SET WITHOUT CLUSTER
SET WITH OIDS
SET WITHOUT OIDS
+ SET TABLESPACE <replaceable class="PARAMETER">new_tablespace</replaceable>
+ SET {LOGGED | UNLOGGED}
SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )
RESET ( <replaceable class="PARAMETER">storage_parameter</replaceable> [, ... ] )
INHERIT <replaceable class="PARAMETER">parent_table</replaceable>
OF <replaceable class="PARAMETER">type_name</replaceable>
NOT OF
OWNER TO <replaceable class="PARAMETER">new_owner</replaceable>
- SET TABLESPACE <replaceable class="PARAMETER">new_tablespace</replaceable>
REPLICA IDENTITY {DEFAULT | USING INDEX <replaceable class="PARAMETER">index_name</replaceable> | FULL | NOTHING}
<phrase>and <replaceable class="PARAMETER">table_constraint_using_index</replaceable> is:</phrase>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>SET TABLESPACE</literal></term>
+ <listitem>
+ <para>
+ This form changes the table's tablespace to the specified tablespace and
+ moves the data file(s) associated with the table to the new tablespace.
+ Indexes on the table, if any, are not moved; but they can be moved
+ separately with additional <literal>SET TABLESPACE</literal> commands.
+ All tables in the current database in a tablespace can be moved by using
+ the <literal>ALL IN TABLESPACE</literal> form, which will lock all tables
+ to be moved first and then move each one. This form also supports
+ <literal>OWNED BY</literal>, which will only move tables owned by the
+ roles specified. If the <literal>NOWAIT</literal> option is specified
+ then the command will fail if it is unable to acquire all of the locks
+ required immediately. Note that system catalogs are not moved by this
+ command, use <command>ALTER DATABASE</command> or explicit
+ <command>ALTER TABLE</command> invocations instead if desired. The
+ <literal>information_schema</literal> relations are not considered part
+ of the system catalogs and will be moved.
+ See also
+ <xref linkend="SQL-CREATETABLESPACE">.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>SET {LOGGED | UNLOGGED}</literal></term>
+ <listitem>
+ <para>
+ This form changes the table from unlogged to logged or vice-versa
+ (see <xref linkend="SQL-CREATETABLE-UNLOGGED">). It cannot be applied
+ to a temporary table.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )</literal></term>
<listitem>
</listitem>
</varlistentry>
- <varlistentry>
- <term><literal>SET TABLESPACE</literal></term>
- <listitem>
- <para>
- This form changes the table's tablespace to the specified tablespace and
- moves the data file(s) associated with the table to the new tablespace.
- Indexes on the table, if any, are not moved; but they can be moved
- separately with additional <literal>SET TABLESPACE</literal> commands.
- All tables in the current database in a tablespace can be moved by using
- the <literal>ALL IN TABLESPACE</literal> form, which will lock all tables
- to be moved first and then move each one. This form also supports
- <literal>OWNED BY</literal>, which will only move tables owned by the
- roles specified. If the <literal>NOWAIT</literal> option is specified
- then the command will fail if it is unable to acquire all of the locks
- required immediately. Note that system catalogs are not moved by this
- command, use <command>ALTER DATABASE</command> or explicit
- <command>ALTER TABLE</command> invocations instead if desired. The
- <literal>information_schema</literal> relations are not considered part
- of the system catalogs and will be moved.
- See also
- <xref linkend="SQL-CREATETABLESPACE">.
- </para>
- </listitem>
- </varlistentry>
-
<varlistentry id="SQL-CREATETABLE-REPLICA-IDENTITY">
<term><literal>REPLICA IDENTITY</literal></term>
<listitem>
heap_close(OldHeap, NoLock);
/* Create the transient table that will receive the re-ordered data */
- OIDNewHeap = make_new_heap(tableOid, tableSpace, false,
+ OIDNewHeap = make_new_heap(tableOid, tableSpace,
+ OldHeap->rd_rel->relpersistence,
AccessExclusiveLock);
/* Copy the heap data into the new table in the desired order */
* Create the transient table that will be filled with new data during
* CLUSTER, ALTER TABLE, and similar operations. The transient table
* duplicates the logical structure of the OldHeap, but is placed in
- * NewTableSpace which might be different from OldHeap's.
+ * NewTableSpace which might be different from OldHeap's. Also, it's built
+ * with the specified persistence, which might differ from the original's.
*
* After this, the caller should load the new heap with transferred/modified
* data, then call finish_heap_swap to complete the operation.
*/
Oid
-make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, bool forcetemp,
+make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, char relpersistence,
LOCKMODE lockmode)
{
TupleDesc OldHeapDesc;
Datum reloptions;
bool isNull;
Oid namespaceid;
- char relpersistence;
OldHeap = heap_open(OIDOldHeap, lockmode);
OldHeapDesc = RelationGetDescr(OldHeap);
if (isNull)
reloptions = (Datum) 0;
- if (forcetemp)
- {
+ if (relpersistence == RELPERSISTENCE_TEMP)
namespaceid = LookupCreationNamespace("pg_temp");
- relpersistence = RELPERSISTENCE_TEMP;
- }
else
- {
namespaceid = RelationGetNamespace(OldHeap);
- relpersistence = OldHeap->rd_rel->relpersistence;
- }
/*
* Create the new heap, using a temporary name in the same namespace as
/*
* Swap the physical files of two given relations.
*
- * We swap the physical identity (reltablespace and relfilenode) while
- * keeping the same logical identities of the two relations.
+ * We swap the physical identity (reltablespace, relfilenode) while keeping the
+ * same logical identities of the two relations. relpersistence is also
+ * swapped, which is critical since it determines where buffers live for each
+ * relation.
*
* We can swap associated TOAST data in either of two ways: recursively swap
* the physical content of the toast tables (and their indexes), or swap the
Oid relfilenode1,
relfilenode2;
Oid swaptemp;
+ char swptmpchr;
CatalogIndexState indstate;
/* We need writable copies of both pg_class tuples. */
if (OidIsValid(relfilenode1) && OidIsValid(relfilenode2))
{
- /* Normal non-mapped relations: swap relfilenodes and reltablespaces */
+ /*
+ * Normal non-mapped relations: swap relfilenodes, reltablespaces,
+ * relpersistence
+ */
Assert(!target_is_pg_class);
swaptemp = relform1->relfilenode;
relform1->reltablespace = relform2->reltablespace;
relform2->reltablespace = swaptemp;
+ swptmpchr = relform1->relpersistence;
+ relform1->relpersistence = relform2->relpersistence;
+ relform2->relpersistence = swptmpchr;
+
/* Also swap toast links, if we're swapping by links */
if (!swap_toast_by_content)
{
NameStr(relform1->relname));
/*
- * We can't change the tablespace of a mapped rel, and we can't handle
- * toast link swapping for one either, because we must not apply any
- * critical changes to its pg_class row. These cases should be
- * prevented by upstream permissions tests, so this check is a
- * non-user-facing emergency backstop.
+ * We can't change the tablespace nor persistence of a mapped rel, and
+ * we can't handle toast link swapping for one either, because we must
+ * not apply any critical changes to its pg_class row. These cases
+ * should be prevented by upstream permissions tests, so these checks
+ * are non-user-facing emergency backstop.
*/
if (relform1->reltablespace != relform2->reltablespace)
elog(ERROR, "cannot change tablespace of mapped relation \"%s\"",
NameStr(relform1->relname));
+ if (relform1->relpersistence != relform2->relpersistence)
+ elog(ERROR, "cannot change persistence of mapped relation \"%s\"",
+ NameStr(relform1->relname));
if (!swap_toast_by_content &&
(relform1->reltoastrelid || relform2->reltoastrelid))
elog(ERROR, "cannot swap toast by links for mapped relation \"%s\"",
DestReceiver *dest;
bool concurrent;
LOCKMODE lockmode;
+ char relpersistence;
/* Determine strength of lock needed. */
concurrent = stmt->concurrent;
/* Concurrent refresh builds new data in temp tablespace, and does diff. */
if (concurrent)
+ {
tableSpace = GetDefaultTablespace(RELPERSISTENCE_TEMP);
+ relpersistence = RELPERSISTENCE_TEMP;
+ }
else
+ {
tableSpace = matviewRel->rd_rel->reltablespace;
+ relpersistence = matviewRel->rd_rel->relpersistence;
+ }
owner = matviewRel->rd_rel->relowner;
* it against access by any other process until commit (by which time it
* will be gone).
*/
- OIDNewHeap = make_new_heap(matviewOid, tableSpace, concurrent,
+ OIDNewHeap = make_new_heap(matviewOid, tableSpace, relpersistence,
ExclusiveLock);
LockRelationOid(OIDNewHeap, AccessExclusiveLock);
dest = CreateTransientRelDestReceiver(OIDNewHeap);
bool new_notnull; /* T if we added new NOT NULL constraints */
bool rewrite; /* T if a rewrite is forced */
Oid newTableSpace; /* new tablespace; 0 means no change */
+ bool chgLoggedness; /* T if SET LOGGED/UNLOGGED is used */
+ char newrelpersistence; /* if above is true */
/* Objects to rebuild after completing ALTER TYPE operations */
List *changedConstraintOids; /* OIDs of constraints to rebuild */
List *changedConstraintDefs; /* string definitions of same */
AlterTableCmd *cmd, LOCKMODE lockmode);
static void ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
List *options, LOCKMODE lockmode);
-static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode);
+static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
+ LOCKMODE lockmode);
static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
char *cmd, List **wqueue, LOCKMODE lockmode,
bool rewrite);
Oid oldOwnerId, Oid newOwnerId);
static void change_owner_recurse_to_sequences(Oid relationOid,
Oid newOwnerId, LOCKMODE lockmode);
-static void ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode);
+static void ATExecClusterOn(Relation rel, const char *indexName,
+ LOCKMODE lockmode);
static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
+static bool ATPrepChangeLoggedness(Relation rel, bool toLogged);
+static void ATChangeIndexesLoggedness(Oid relid, char relpersistence);
static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
char *tablespacename, LOCKMODE lockmode);
static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
cmd_lockmode = ShareUpdateExclusiveLock;
break;
+ case AT_SetLogged:
+ case AT_SetUnLogged:
+ cmd_lockmode = AccessExclusiveLock;
+ break;
+
case AT_ValidateConstraint: /* Uses MVCC in
* getConstraints() */
cmd_lockmode = ShareUpdateExclusiveLock;
/* No command-specific prep needed */
pass = AT_PASS_MISC;
break;
+ case AT_SetLogged: /* SET LOGGED */
+ ATSimplePermissions(rel, ATT_TABLE);
+ tab->chgLoggedness = ATPrepChangeLoggedness(rel, true);
+ tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
+ /* force rewrite if necessary */
+ if (tab->chgLoggedness)
+ tab->rewrite = true;
+ pass = AT_PASS_MISC;
+ break;
+ case AT_SetUnLogged: /* SET UNLOGGED */
+ ATSimplePermissions(rel, ATT_TABLE);
+ tab->chgLoggedness = ATPrepChangeLoggedness(rel, false);
+ tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
+ /* force rewrite if necessary */
+ if (tab->chgLoggedness)
+ tab->rewrite = true;
+ pass = AT_PASS_MISC;
+ break;
case AT_AddOids: /* SET WITH OIDS */
ATSimplePermissions(rel, ATT_TABLE);
if (!rel->rd_rel->relhasoids || recursing)
case AT_DropCluster: /* SET WITHOUT CLUSTER */
ATExecDropCluster(rel, lockmode);
break;
+ case AT_SetLogged: /* SET LOGGED */
+ case AT_SetUnLogged: /* SET UNLOGGED */
+ break;
case AT_AddOids: /* SET WITH OIDS */
/* Use the ADD COLUMN code, unless prep decided to do nothing */
if (cmd->def != NULL)
/*
* We only need to rewrite the table if at least one column needs to
- * be recomputed, or we are adding/removing the OID column.
+ * be recomputed, we are adding/removing the OID column, or we are
+ * changing its persistence.
*/
if (tab->rewrite)
{
Relation OldHeap;
Oid OIDNewHeap;
Oid NewTableSpace;
+ char persistence;
OldHeap = heap_open(tab->relid, NoLock);
else
NewTableSpace = OldHeap->rd_rel->reltablespace;
+ /*
+ * Select persistence of transient table (same as original unless
+ * user requested a change)
+ */
+ persistence = tab->chgLoggedness ?
+ tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
+
heap_close(OldHeap, NoLock);
- /* Create transient table that will receive the modified data */
- OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, false,
+ /*
+ * Create transient table that will receive the modified data.
+ *
+ * Ensure it is marked correctly as logged or unlogged. We have
+ * to do this here so that buffers for the new relfilenode will
+ * have the right persistence set, and at the same time ensure
+ * that the original filenode's buffers will get read in with the
+ * correct setting (i.e. the original one). Otherwise a rollback
+ * after the rewrite would possibly result with buffers for the
+ * original filenode having the wrong persistence setting.
+ *
+ * NB: This relies on swap_relation_files() also swapping the
+ * persistence. That wouldn't work for pg_class, but that can't be
+ * unlogged anyway.
+ */
+ OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, persistence,
lockmode);
/*
*/
ATRewriteTable(tab, OIDNewHeap, lockmode);
+ /*
+ * Change the persistence marking of indexes, if necessary. This
+ * is so that the new copies are built with the right persistence
+ * in the reindex step below. Note we cannot do this earlier,
+ * because the rewrite step might read the indexes, and that would
+ * cause buffers for them to have the wrong setting.
+ */
+ if (tab->chgLoggedness)
+ ATChangeIndexesLoggedness(tab->relid, tab->newrelpersistence);
+
/*
* Swap the physical files of the old and new heaps, then rebuild
* indexes and discard the old heap. We can use RecentXmin for
tab->relid = relid;
tab->relkind = rel->rd_rel->relkind;
tab->oldDesc = CreateTupleDescCopy(RelationGetDescr(rel));
+ tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
+ tab->chgLoggedness = false;
*wqueue = lappend(*wqueue, tab);
heap_freetuple(tuple);
}
+/*
+ * Preparation phase for SET LOGGED/UNLOGGED
+ *
+ * This verifies that we're not trying to change a temp table. Also,
+ * existing foreign key constraints are checked to avoid ending up with
+ * permanent tables referencing unlogged tables.
+ *
+ * Return value is false if the operation is a no-op (in which case the
+ * checks are skipped), otherwise true.
+ */
+static bool
+ATPrepChangeLoggedness(Relation rel, bool toLogged)
+{
+ Relation pg_constraint;
+ HeapTuple tuple;
+ SysScanDesc scan;
+ ScanKeyData skey[1];
+
+ /*
+ * Disallow changing status for a temp table. Also verify whether we can
+ * get away with doing nothing; in such cases we don't need to run the
+ * checks below, either.
+ */
+ switch (rel->rd_rel->relpersistence)
+ {
+ case RELPERSISTENCE_TEMP:
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ errmsg("cannot change logged status of table %s",
+ RelationGetRelationName(rel)),
+ errdetail("Table %s is temporary.",
+ RelationGetRelationName(rel)),
+ errtable(rel)));
+ break;
+ case RELPERSISTENCE_PERMANENT:
+ if (toLogged)
+ /* nothing to do */
+ return false;
+ break;
+ case RELPERSISTENCE_UNLOGGED:
+ if (!toLogged)
+ /* nothing to do */
+ return false;
+ break;
+ }
+
+ /*
+ * Check existing foreign key constraints to preserve the invariant that
+ * no permanent tables cannot reference unlogged ones. Self-referencing
+ * foreign keys can safely be ignored.
+ */
+ pg_constraint = heap_open(ConstraintRelationId, AccessShareLock);
+
+ /*
+ * Scan conrelid if changing to permanent, else confrelid. This also
+ * determines whether an useful index exists.
+ */
+ ScanKeyInit(&skey[0],
+ toLogged ? Anum_pg_constraint_conrelid :
+ Anum_pg_constraint_confrelid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(RelationGetRelid(rel)));
+ scan = systable_beginscan(pg_constraint,
+ toLogged ? ConstraintRelidIndexId : InvalidOid,
+ true, NULL, 1, skey);
+
+ while (HeapTupleIsValid(tuple = systable_getnext(scan)))
+ {
+ Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
+
+ if (con->contype == CONSTRAINT_FOREIGN)
+ {
+ Oid foreignrelid;
+ Relation foreignrel;
+
+ /* the opposite end of what we used as scankey */
+ foreignrelid = toLogged ? con->confrelid : con->conrelid;
+
+ /* ignore if self-referencing */
+ if (RelationGetRelid(rel) == foreignrelid)
+ continue;
+
+ foreignrel = relation_open(foreignrelid, AccessShareLock);
+
+ if (toLogged)
+ {
+ if (foreignrel->rd_rel->relpersistence != RELPERSISTENCE_PERMANENT)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ errmsg("cannot change status of table %s to logged",
+ RelationGetRelationName(rel)),
+ errdetail("Table %s references unlogged table %s.",
+ RelationGetRelationName(rel),
+ RelationGetRelationName(foreignrel)),
+ errtableconstraint(rel, NameStr(con->conname))));
+ }
+ else
+ {
+ if (foreignrel->rd_rel->relpersistence == RELPERSISTENCE_PERMANENT)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ errmsg("cannot change status of table %s to unlogged",
+ RelationGetRelationName(rel)),
+ errdetail("Logged table %s is referenced by table %s.",
+ RelationGetRelationName(foreignrel),
+ RelationGetRelationName(rel)),
+ errtableconstraint(rel, NameStr(con->conname))));
+ }
+
+ relation_close(foreignrel, AccessShareLock);
+ }
+ }
+
+ systable_endscan(scan);
+
+ heap_close(pg_constraint, AccessShareLock);
+
+ return true;
+}
+
+/*
+ * Update the pg_class entry of each index for the given relation to the
+ * given persistence.
+ */
+static void
+ATChangeIndexesLoggedness(Oid relid, char relpersistence)
+{
+ Relation rel;
+ Relation pg_class;
+ List *indexes;
+ ListCell *cell;
+
+ pg_class = heap_open(RelationRelationId, RowExclusiveLock);
+
+ /* We already have a lock on the table */
+ rel = relation_open(relid, NoLock);
+ indexes = RelationGetIndexList(rel);
+ foreach(cell, indexes)
+ {
+ Oid indexid = lfirst_oid(cell);
+ HeapTuple tuple;
+ Form_pg_class pg_class_form;
+
+ tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(indexid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for relation %u",
+ indexid);
+
+ pg_class_form = (Form_pg_class) GETSTRUCT(tuple);
+ pg_class_form->relpersistence = relpersistence;
+ simple_heap_update(pg_class, &tuple->t_self, tuple);
+
+ /* keep catalog indexes current */
+ CatalogUpdateIndexes(pg_class, tuple);
+
+ heap_freetuple(tuple);
+ }
+
+ heap_close(pg_class, RowExclusiveLock);
+ heap_close(rel, NoLock);
+}
+
/*
* Execute ALTER TABLE SET SCHEMA
*/
LABEL LANGUAGE LARGE_P LAST_P LATERAL_P
LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
- LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P
+ LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOGGED
MAPPING MATCH MATERIALIZED MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE
n->name = NULL;
$$ = (Node *)n;
}
+ /* ALTER TABLE <name> SET LOGGED */
+ | SET LOGGED
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_SetLogged;
+ $$ = (Node *)n;
+ }
+ /* ALTER TABLE <name> SET UNLOGGED */
+ | SET UNLOGGED
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_SetUnLogged;
+ $$ = (Node *)n;
+ }
/* ALTER TABLE <name> ENABLE TRIGGER <trig> */
| ENABLE_P TRIGGER name
{
| LOCAL
| LOCATION
| LOCK_P
+ | LOGGED
| MAPPING
| MATCH
| MATERIALIZED
completion_info_charp = prev3_wd;
COMPLETE_WITH_QUERY(Query_for_index_of_table);
}
- /* If we have TABLE <sth> SET, provide WITHOUT,TABLESPACE and SCHEMA */
+ /* If we have TABLE <sth> SET, provide list of attributes and '(' */
else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
pg_strcasecmp(prev_wd, "SET") == 0)
{
static const char *const list_TABLESET[] =
- {"(", "WITHOUT", "TABLESPACE", "SCHEMA", NULL};
+ {"(", "LOGGED", "SCHEMA", "TABLESPACE", "UNLOGGED", "WITH", "WITHOUT", NULL};
COMPLETE_WITH_LIST(list_TABLESET);
}
bool recheck, LOCKMODE lockmode);
extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal);
-extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, bool forcetemp,
+extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, char relpersistence,
LOCKMODE lockmode);
extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap,
bool is_system_catalog,
AT_ChangeOwner, /* change owner */
AT_ClusterOn, /* CLUSTER ON */
AT_DropCluster, /* SET WITHOUT CLUSTER */
+ AT_SetLogged, /* SET LOGGED */
+ AT_SetUnLogged, /* SET UNLOGGED */
AT_AddOids, /* SET WITH OIDS */
AT_AddOidsRecurse, /* internal to commands/tablecmds.c */
AT_DropOids, /* SET WITHOUT OIDS */
PG_KEYWORD("localtimestamp", LOCALTIMESTAMP, RESERVED_KEYWORD)
PG_KEYWORD("location", LOCATION, UNRESERVED_KEYWORD)
PG_KEYWORD("lock", LOCK_P, UNRESERVED_KEYWORD)
+PG_KEYWORD("logged", LOGGED, UNRESERVED_KEYWORD)
PG_KEYWORD("mapping", MAPPING, UNRESERVED_KEYWORD)
PG_KEYWORD("match", MATCH, UNRESERVED_KEYWORD)
PG_KEYWORD("materialized", MATERIALIZED, UNRESERVED_KEYWORD)
ALTER TABLE old_system_table DROP CONSTRAINT new_system_table_pkey;
ALTER TABLE old_system_table DROP COLUMN othercol;
DROP TABLE old_system_table;
+-- set logged
+CREATE UNLOGGED TABLE unlogged1(f1 SERIAL PRIMARY KEY, f2 TEXT);
+-- check relpersistence of an unlogged table
+SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^unlogged1'
+UNION ALL
+SELECT 'toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^unlogged1'
+UNION ALL
+SELECT 'toast index', ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^unlogged1'
+ORDER BY relname;
+ relname | relkind | relpersistence
+------------------+---------+----------------
+ toast index | i | u
+ toast table | t | u
+ unlogged1 | r | u
+ unlogged1_f1_seq | S | p
+ unlogged1_pkey | i | u
+(5 rows)
+
+CREATE UNLOGGED TABLE unlogged2(f1 SERIAL PRIMARY KEY, f2 INTEGER REFERENCES unlogged1); -- foreign key
+CREATE UNLOGGED TABLE unlogged3(f1 SERIAL PRIMARY KEY, f2 INTEGER REFERENCES unlogged3); -- self-referencing foreign key
+ALTER TABLE unlogged3 SET LOGGED; -- skip self-referencing foreign key
+ALTER TABLE unlogged2 SET LOGGED; -- fails because a foreign key to an unlogged table exists
+ERROR: cannot change status of table unlogged2 to logged
+DETAIL: Table unlogged2 references unlogged table unlogged1.
+ALTER TABLE unlogged1 SET LOGGED;
+-- check relpersistence of an unlogged table after changing to permament
+SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^unlogged1'
+UNION ALL
+SELECT 'toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^unlogged1'
+UNION ALL
+SELECT 'toast index', ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^unlogged1'
+ORDER BY relname;
+ relname | relkind | relpersistence
+------------------+---------+----------------
+ toast index | i | p
+ toast table | t | p
+ unlogged1 | r | p
+ unlogged1_f1_seq | S | p
+ unlogged1_pkey | i | p
+(5 rows)
+
+ALTER TABLE unlogged1 SET LOGGED; -- silently do nothing
+DROP TABLE unlogged3;
+DROP TABLE unlogged2;
+DROP TABLE unlogged1;
+-- set unlogged
+CREATE TABLE logged1(f1 SERIAL PRIMARY KEY, f2 TEXT);
+-- check relpersistence of a permanent table
+SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^logged1'
+UNION ALL
+SELECT 'toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^logged1'
+UNION ALL
+SELECT 'toast index', ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^logged1'
+ORDER BY relname;
+ relname | relkind | relpersistence
+----------------+---------+----------------
+ logged1 | r | p
+ logged1_f1_seq | S | p
+ logged1_pkey | i | p
+ toast index | i | p
+ toast table | t | p
+(5 rows)
+
+CREATE TABLE logged2(f1 SERIAL PRIMARY KEY, f2 INTEGER REFERENCES logged1); -- foreign key
+CREATE TABLE logged3(f1 SERIAL PRIMARY KEY, f2 INTEGER REFERENCES logged3); -- self-referencing foreign key
+ALTER TABLE logged1 SET UNLOGGED; -- fails because a foreign key from a permanent table exists
+ERROR: cannot change status of table logged1 to unlogged
+DETAIL: Logged table logged2 is referenced by table logged1.
+ALTER TABLE logged3 SET UNLOGGED; -- skip self-referencing foreign key
+ALTER TABLE logged2 SET UNLOGGED;
+ALTER TABLE logged1 SET UNLOGGED;
+-- check relpersistence of a permanent table after changing to unlogged
+SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^logged1'
+UNION ALL
+SELECT 'toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^logged1'
+UNION ALL
+SELECT 'toast index', ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^logged1'
+ORDER BY relname;
+ relname | relkind | relpersistence
+----------------+---------+----------------
+ logged1 | r | u
+ logged1_f1_seq | S | p
+ logged1_pkey | i | u
+ toast index | i | u
+ toast table | t | u
+(5 rows)
+
+ALTER TABLE logged1 SET UNLOGGED; -- silently do nothing
+DROP TABLE logged3;
+DROP TABLE logged2;
+DROP TABLE logged1;
ALTER TABLE old_system_table DROP CONSTRAINT new_system_table_pkey;
ALTER TABLE old_system_table DROP COLUMN othercol;
DROP TABLE old_system_table;
+
+-- set logged
+CREATE UNLOGGED TABLE unlogged1(f1 SERIAL PRIMARY KEY, f2 TEXT);
+-- check relpersistence of an unlogged table
+SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^unlogged1'
+UNION ALL
+SELECT 'toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^unlogged1'
+UNION ALL
+SELECT 'toast index', ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^unlogged1'
+ORDER BY relname;
+CREATE UNLOGGED TABLE unlogged2(f1 SERIAL PRIMARY KEY, f2 INTEGER REFERENCES unlogged1); -- foreign key
+CREATE UNLOGGED TABLE unlogged3(f1 SERIAL PRIMARY KEY, f2 INTEGER REFERENCES unlogged3); -- self-referencing foreign key
+ALTER TABLE unlogged3 SET LOGGED; -- skip self-referencing foreign key
+ALTER TABLE unlogged2 SET LOGGED; -- fails because a foreign key to an unlogged table exists
+ALTER TABLE unlogged1 SET LOGGED;
+-- check relpersistence of an unlogged table after changing to permament
+SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^unlogged1'
+UNION ALL
+SELECT 'toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^unlogged1'
+UNION ALL
+SELECT 'toast index', ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^unlogged1'
+ORDER BY relname;
+ALTER TABLE unlogged1 SET LOGGED; -- silently do nothing
+DROP TABLE unlogged3;
+DROP TABLE unlogged2;
+DROP TABLE unlogged1;
+-- set unlogged
+CREATE TABLE logged1(f1 SERIAL PRIMARY KEY, f2 TEXT);
+-- check relpersistence of a permanent table
+SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^logged1'
+UNION ALL
+SELECT 'toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^logged1'
+UNION ALL
+SELECT 'toast index', ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^logged1'
+ORDER BY relname;
+CREATE TABLE logged2(f1 SERIAL PRIMARY KEY, f2 INTEGER REFERENCES logged1); -- foreign key
+CREATE TABLE logged3(f1 SERIAL PRIMARY KEY, f2 INTEGER REFERENCES logged3); -- self-referencing foreign key
+ALTER TABLE logged1 SET UNLOGGED; -- fails because a foreign key from a permanent table exists
+ALTER TABLE logged3 SET UNLOGGED; -- skip self-referencing foreign key
+ALTER TABLE logged2 SET UNLOGGED;
+ALTER TABLE logged1 SET UNLOGGED;
+-- check relpersistence of a permanent table after changing to unlogged
+SELECT relname, relkind, relpersistence FROM pg_class WHERE relname ~ '^logged1'
+UNION ALL
+SELECT 'toast table', t.relkind, t.relpersistence FROM pg_class r JOIN pg_class t ON t.oid = r.reltoastrelid WHERE r.relname ~ '^logged1'
+UNION ALL
+SELECT 'toast index', ri.relkind, ri.relpersistence FROM pg_class r join pg_class t ON t.oid = r.reltoastrelid JOIN pg_index i ON i.indrelid = t.oid JOIN pg_class ri ON ri.oid = i.indexrelid WHERE r.relname ~ '^logged1'
+ORDER BY relname;
+ALTER TABLE logged1 SET UNLOGGED; -- silently do nothing
+DROP TABLE logged3;
+DROP TABLE logged2;
+DROP TABLE logged1;