mirror of
https://github.com/postgres/postgres.git
synced 2025-04-21 12:05:57 +03:00
Implement ALTER TABLE .. SET LOGGED / UNLOGGED
This enables changing permanent (logged) tables to unlogged and vice-versa. (Docs for ALTER TABLE / SET TABLESPACE got shuffled in an order that hopefully makes more sense than the original.) Author: Fabrízio de Royes Mello Reviewed by: Christoph Berg, Andres Freund, Thom Brown Some tweaking by Álvaro Herrera
This commit is contained in:
parent
01d15a2677
commit
f41872d0c1
@ -63,6 +63,8 @@ ALTER TABLE ALL IN TABLESPACE <replaceable class="PARAMETER">name</replaceable>
|
|||||||
SET WITHOUT CLUSTER
|
SET WITHOUT CLUSTER
|
||||||
SET WITH OIDS
|
SET WITH OIDS
|
||||||
SET WITHOUT 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> [, ... ] )
|
SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )
|
||||||
RESET ( <replaceable class="PARAMETER">storage_parameter</replaceable> [, ... ] )
|
RESET ( <replaceable class="PARAMETER">storage_parameter</replaceable> [, ... ] )
|
||||||
INHERIT <replaceable class="PARAMETER">parent_table</replaceable>
|
INHERIT <replaceable class="PARAMETER">parent_table</replaceable>
|
||||||
@ -70,7 +72,6 @@ ALTER TABLE ALL IN TABLESPACE <replaceable class="PARAMETER">name</replaceable>
|
|||||||
OF <replaceable class="PARAMETER">type_name</replaceable>
|
OF <replaceable class="PARAMETER">type_name</replaceable>
|
||||||
NOT OF
|
NOT OF
|
||||||
OWNER TO <replaceable class="PARAMETER">new_owner</replaceable>
|
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}
|
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>
|
<phrase>and <replaceable class="PARAMETER">table_constraint_using_index</replaceable> is:</phrase>
|
||||||
@ -478,6 +479,42 @@ ALTER TABLE ALL IN TABLESPACE <replaceable class="PARAMETER">name</replaceable>
|
|||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</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>
|
<varlistentry>
|
||||||
<term><literal>SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )</literal></term>
|
<term><literal>SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )</literal></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
@ -591,31 +628,6 @@ ALTER TABLE ALL IN TABLESPACE <replaceable class="PARAMETER">name</replaceable>
|
|||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</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">
|
<varlistentry id="SQL-CREATETABLE-REPLICA-IDENTITY">
|
||||||
<term><literal>REPLICA IDENTITY</literal></term>
|
<term><literal>REPLICA IDENTITY</literal></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
|
@ -574,7 +574,8 @@ rebuild_relation(Relation OldHeap, Oid indexOid, bool verbose)
|
|||||||
heap_close(OldHeap, NoLock);
|
heap_close(OldHeap, NoLock);
|
||||||
|
|
||||||
/* Create the transient table that will receive the re-ordered data */
|
/* 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);
|
AccessExclusiveLock);
|
||||||
|
|
||||||
/* Copy the heap data into the new table in the desired order */
|
/* Copy the heap data into the new table in the desired order */
|
||||||
@ -595,13 +596,14 @@ rebuild_relation(Relation OldHeap, Oid indexOid, bool verbose)
|
|||||||
* Create the transient table that will be filled with new data during
|
* Create the transient table that will be filled with new data during
|
||||||
* CLUSTER, ALTER TABLE, and similar operations. The transient table
|
* CLUSTER, ALTER TABLE, and similar operations. The transient table
|
||||||
* duplicates the logical structure of the OldHeap, but is placed in
|
* 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
|
* After this, the caller should load the new heap with transferred/modified
|
||||||
* data, then call finish_heap_swap to complete the operation.
|
* data, then call finish_heap_swap to complete the operation.
|
||||||
*/
|
*/
|
||||||
Oid
|
Oid
|
||||||
make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, bool forcetemp,
|
make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, char relpersistence,
|
||||||
LOCKMODE lockmode)
|
LOCKMODE lockmode)
|
||||||
{
|
{
|
||||||
TupleDesc OldHeapDesc;
|
TupleDesc OldHeapDesc;
|
||||||
@ -613,7 +615,6 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, bool forcetemp,
|
|||||||
Datum reloptions;
|
Datum reloptions;
|
||||||
bool isNull;
|
bool isNull;
|
||||||
Oid namespaceid;
|
Oid namespaceid;
|
||||||
char relpersistence;
|
|
||||||
|
|
||||||
OldHeap = heap_open(OIDOldHeap, lockmode);
|
OldHeap = heap_open(OIDOldHeap, lockmode);
|
||||||
OldHeapDesc = RelationGetDescr(OldHeap);
|
OldHeapDesc = RelationGetDescr(OldHeap);
|
||||||
@ -636,16 +637,10 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, bool forcetemp,
|
|||||||
if (isNull)
|
if (isNull)
|
||||||
reloptions = (Datum) 0;
|
reloptions = (Datum) 0;
|
||||||
|
|
||||||
if (forcetemp)
|
if (relpersistence == RELPERSISTENCE_TEMP)
|
||||||
{
|
|
||||||
namespaceid = LookupCreationNamespace("pg_temp");
|
namespaceid = LookupCreationNamespace("pg_temp");
|
||||||
relpersistence = RELPERSISTENCE_TEMP;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
namespaceid = RelationGetNamespace(OldHeap);
|
namespaceid = RelationGetNamespace(OldHeap);
|
||||||
relpersistence = OldHeap->rd_rel->relpersistence;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create the new heap, using a temporary name in the same namespace as
|
* Create the new heap, using a temporary name in the same namespace as
|
||||||
@ -1109,8 +1104,10 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, bool verbose,
|
|||||||
/*
|
/*
|
||||||
* Swap the physical files of two given relations.
|
* Swap the physical files of two given relations.
|
||||||
*
|
*
|
||||||
* We swap the physical identity (reltablespace and relfilenode) while
|
* We swap the physical identity (reltablespace, relfilenode) while keeping the
|
||||||
* keeping the same logical identities of the two relations.
|
* 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
|
* 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
|
* the physical content of the toast tables (and their indexes), or swap the
|
||||||
@ -1146,6 +1143,7 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
|
|||||||
Oid relfilenode1,
|
Oid relfilenode1,
|
||||||
relfilenode2;
|
relfilenode2;
|
||||||
Oid swaptemp;
|
Oid swaptemp;
|
||||||
|
char swptmpchr;
|
||||||
CatalogIndexState indstate;
|
CatalogIndexState indstate;
|
||||||
|
|
||||||
/* We need writable copies of both pg_class tuples. */
|
/* We need writable copies of both pg_class tuples. */
|
||||||
@ -1166,7 +1164,10 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
|
|||||||
|
|
||||||
if (OidIsValid(relfilenode1) && OidIsValid(relfilenode2))
|
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);
|
Assert(!target_is_pg_class);
|
||||||
|
|
||||||
swaptemp = relform1->relfilenode;
|
swaptemp = relform1->relfilenode;
|
||||||
@ -1177,6 +1178,10 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
|
|||||||
relform1->reltablespace = relform2->reltablespace;
|
relform1->reltablespace = relform2->reltablespace;
|
||||||
relform2->reltablespace = swaptemp;
|
relform2->reltablespace = swaptemp;
|
||||||
|
|
||||||
|
swptmpchr = relform1->relpersistence;
|
||||||
|
relform1->relpersistence = relform2->relpersistence;
|
||||||
|
relform2->relpersistence = swptmpchr;
|
||||||
|
|
||||||
/* Also swap toast links, if we're swapping by links */
|
/* Also swap toast links, if we're swapping by links */
|
||||||
if (!swap_toast_by_content)
|
if (!swap_toast_by_content)
|
||||||
{
|
{
|
||||||
@ -1196,15 +1201,18 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
|
|||||||
NameStr(relform1->relname));
|
NameStr(relform1->relname));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We can't change the tablespace of a mapped rel, and we can't handle
|
* We can't change the tablespace nor persistence of a mapped rel, and
|
||||||
* toast link swapping for one either, because we must not apply any
|
* we can't handle toast link swapping for one either, because we must
|
||||||
* critical changes to its pg_class row. These cases should be
|
* not apply any critical changes to its pg_class row. These cases
|
||||||
* prevented by upstream permissions tests, so this check is a
|
* should be prevented by upstream permissions tests, so these checks
|
||||||
* non-user-facing emergency backstop.
|
* are non-user-facing emergency backstop.
|
||||||
*/
|
*/
|
||||||
if (relform1->reltablespace != relform2->reltablespace)
|
if (relform1->reltablespace != relform2->reltablespace)
|
||||||
elog(ERROR, "cannot change tablespace of mapped relation \"%s\"",
|
elog(ERROR, "cannot change tablespace of mapped relation \"%s\"",
|
||||||
NameStr(relform1->relname));
|
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 &&
|
if (!swap_toast_by_content &&
|
||||||
(relform1->reltoastrelid || relform2->reltoastrelid))
|
(relform1->reltoastrelid || relform2->reltoastrelid))
|
||||||
elog(ERROR, "cannot swap toast by links for mapped relation \"%s\"",
|
elog(ERROR, "cannot swap toast by links for mapped relation \"%s\"",
|
||||||
|
@ -147,6 +147,7 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
|
|||||||
DestReceiver *dest;
|
DestReceiver *dest;
|
||||||
bool concurrent;
|
bool concurrent;
|
||||||
LOCKMODE lockmode;
|
LOCKMODE lockmode;
|
||||||
|
char relpersistence;
|
||||||
|
|
||||||
/* Determine strength of lock needed. */
|
/* Determine strength of lock needed. */
|
||||||
concurrent = stmt->concurrent;
|
concurrent = stmt->concurrent;
|
||||||
@ -233,9 +234,15 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
|
|||||||
|
|
||||||
/* Concurrent refresh builds new data in temp tablespace, and does diff. */
|
/* Concurrent refresh builds new data in temp tablespace, and does diff. */
|
||||||
if (concurrent)
|
if (concurrent)
|
||||||
|
{
|
||||||
tableSpace = GetDefaultTablespace(RELPERSISTENCE_TEMP);
|
tableSpace = GetDefaultTablespace(RELPERSISTENCE_TEMP);
|
||||||
|
relpersistence = RELPERSISTENCE_TEMP;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
tableSpace = matviewRel->rd_rel->reltablespace;
|
tableSpace = matviewRel->rd_rel->reltablespace;
|
||||||
|
relpersistence = matviewRel->rd_rel->relpersistence;
|
||||||
|
}
|
||||||
|
|
||||||
owner = matviewRel->rd_rel->relowner;
|
owner = matviewRel->rd_rel->relowner;
|
||||||
|
|
||||||
@ -244,7 +251,7 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
|
|||||||
* it against access by any other process until commit (by which time it
|
* it against access by any other process until commit (by which time it
|
||||||
* will be gone).
|
* will be gone).
|
||||||
*/
|
*/
|
||||||
OIDNewHeap = make_new_heap(matviewOid, tableSpace, concurrent,
|
OIDNewHeap = make_new_heap(matviewOid, tableSpace, relpersistence,
|
||||||
ExclusiveLock);
|
ExclusiveLock);
|
||||||
LockRelationOid(OIDNewHeap, AccessExclusiveLock);
|
LockRelationOid(OIDNewHeap, AccessExclusiveLock);
|
||||||
dest = CreateTransientRelDestReceiver(OIDNewHeap);
|
dest = CreateTransientRelDestReceiver(OIDNewHeap);
|
||||||
|
@ -152,6 +152,8 @@ typedef struct AlteredTableInfo
|
|||||||
bool new_notnull; /* T if we added new NOT NULL constraints */
|
bool new_notnull; /* T if we added new NOT NULL constraints */
|
||||||
bool rewrite; /* T if a rewrite is forced */
|
bool rewrite; /* T if a rewrite is forced */
|
||||||
Oid newTableSpace; /* new tablespace; 0 means no change */
|
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 */
|
/* Objects to rebuild after completing ALTER TYPE operations */
|
||||||
List *changedConstraintOids; /* OIDs of constraints to rebuild */
|
List *changedConstraintOids; /* OIDs of constraints to rebuild */
|
||||||
List *changedConstraintDefs; /* string definitions of same */
|
List *changedConstraintDefs; /* string definitions of same */
|
||||||
@ -372,7 +374,8 @@ static void ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
|
|||||||
AlterTableCmd *cmd, LOCKMODE lockmode);
|
AlterTableCmd *cmd, LOCKMODE lockmode);
|
||||||
static void ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
|
static void ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
|
||||||
List *options, LOCKMODE lockmode);
|
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,
|
static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
|
||||||
char *cmd, List **wqueue, LOCKMODE lockmode,
|
char *cmd, List **wqueue, LOCKMODE lockmode,
|
||||||
bool rewrite);
|
bool rewrite);
|
||||||
@ -382,8 +385,11 @@ static void change_owner_fix_column_acls(Oid relationOid,
|
|||||||
Oid oldOwnerId, Oid newOwnerId);
|
Oid oldOwnerId, Oid newOwnerId);
|
||||||
static void change_owner_recurse_to_sequences(Oid relationOid,
|
static void change_owner_recurse_to_sequences(Oid relationOid,
|
||||||
Oid newOwnerId, LOCKMODE lockmode);
|
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 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,
|
static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
|
||||||
char *tablespacename, LOCKMODE lockmode);
|
char *tablespacename, LOCKMODE lockmode);
|
||||||
static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
|
static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
|
||||||
@ -2949,6 +2955,11 @@ AlterTableGetLockLevel(List *cmds)
|
|||||||
cmd_lockmode = ShareUpdateExclusiveLock;
|
cmd_lockmode = ShareUpdateExclusiveLock;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case AT_SetLogged:
|
||||||
|
case AT_SetUnLogged:
|
||||||
|
cmd_lockmode = AccessExclusiveLock;
|
||||||
|
break;
|
||||||
|
|
||||||
case AT_ValidateConstraint: /* Uses MVCC in
|
case AT_ValidateConstraint: /* Uses MVCC in
|
||||||
* getConstraints() */
|
* getConstraints() */
|
||||||
cmd_lockmode = ShareUpdateExclusiveLock;
|
cmd_lockmode = ShareUpdateExclusiveLock;
|
||||||
@ -3161,6 +3172,24 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
|
|||||||
/* No command-specific prep needed */
|
/* No command-specific prep needed */
|
||||||
pass = AT_PASS_MISC;
|
pass = AT_PASS_MISC;
|
||||||
break;
|
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 */
|
case AT_AddOids: /* SET WITH OIDS */
|
||||||
ATSimplePermissions(rel, ATT_TABLE);
|
ATSimplePermissions(rel, ATT_TABLE);
|
||||||
if (!rel->rd_rel->relhasoids || recursing)
|
if (!rel->rd_rel->relhasoids || recursing)
|
||||||
@ -3431,6 +3460,9 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
|
|||||||
case AT_DropCluster: /* SET WITHOUT CLUSTER */
|
case AT_DropCluster: /* SET WITHOUT CLUSTER */
|
||||||
ATExecDropCluster(rel, lockmode);
|
ATExecDropCluster(rel, lockmode);
|
||||||
break;
|
break;
|
||||||
|
case AT_SetLogged: /* SET LOGGED */
|
||||||
|
case AT_SetUnLogged: /* SET UNLOGGED */
|
||||||
|
break;
|
||||||
case AT_AddOids: /* SET WITH OIDS */
|
case AT_AddOids: /* SET WITH OIDS */
|
||||||
/* Use the ADD COLUMN code, unless prep decided to do nothing */
|
/* Use the ADD COLUMN code, unless prep decided to do nothing */
|
||||||
if (cmd->def != NULL)
|
if (cmd->def != NULL)
|
||||||
@ -3584,7 +3616,8 @@ ATRewriteTables(List **wqueue, LOCKMODE lockmode)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* We only need to rewrite the table if at least one column needs to
|
* 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)
|
if (tab->rewrite)
|
||||||
{
|
{
|
||||||
@ -3592,6 +3625,7 @@ ATRewriteTables(List **wqueue, LOCKMODE lockmode)
|
|||||||
Relation OldHeap;
|
Relation OldHeap;
|
||||||
Oid OIDNewHeap;
|
Oid OIDNewHeap;
|
||||||
Oid NewTableSpace;
|
Oid NewTableSpace;
|
||||||
|
char persistence;
|
||||||
|
|
||||||
OldHeap = heap_open(tab->relid, NoLock);
|
OldHeap = heap_open(tab->relid, NoLock);
|
||||||
|
|
||||||
@ -3630,10 +3664,31 @@ ATRewriteTables(List **wqueue, LOCKMODE lockmode)
|
|||||||
else
|
else
|
||||||
NewTableSpace = OldHeap->rd_rel->reltablespace;
|
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);
|
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);
|
lockmode);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -3643,6 +3698,16 @@ ATRewriteTables(List **wqueue, LOCKMODE lockmode)
|
|||||||
*/
|
*/
|
||||||
ATRewriteTable(tab, OIDNewHeap, 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
|
* Swap the physical files of the old and new heaps, then rebuild
|
||||||
* indexes and discard the old heap. We can use RecentXmin for
|
* indexes and discard the old heap. We can use RecentXmin for
|
||||||
@ -4053,6 +4118,8 @@ ATGetQueueEntry(List **wqueue, Relation rel)
|
|||||||
tab->relid = relid;
|
tab->relid = relid;
|
||||||
tab->relkind = rel->rd_rel->relkind;
|
tab->relkind = rel->rd_rel->relkind;
|
||||||
tab->oldDesc = CreateTupleDescCopy(RelationGetDescr(rel));
|
tab->oldDesc = CreateTupleDescCopy(RelationGetDescr(rel));
|
||||||
|
tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
|
||||||
|
tab->chgLoggedness = false;
|
||||||
|
|
||||||
*wqueue = lappend(*wqueue, tab);
|
*wqueue = lappend(*wqueue, tab);
|
||||||
|
|
||||||
@ -10600,6 +10667,168 @@ ATExecGenericOptions(Relation rel, List *options)
|
|||||||
heap_freetuple(tuple);
|
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
|
* Execute ALTER TABLE SET SCHEMA
|
||||||
*/
|
*/
|
||||||
|
@ -577,7 +577,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
|
|||||||
|
|
||||||
LABEL LANGUAGE LARGE_P LAST_P LATERAL_P
|
LABEL LANGUAGE LARGE_P LAST_P LATERAL_P
|
||||||
LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
|
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
|
MAPPING MATCH MATERIALIZED MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE
|
||||||
|
|
||||||
@ -2114,6 +2114,20 @@ alter_table_cmd:
|
|||||||
n->name = NULL;
|
n->name = NULL;
|
||||||
$$ = (Node *)n;
|
$$ = (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> */
|
/* ALTER TABLE <name> ENABLE TRIGGER <trig> */
|
||||||
| ENABLE_P TRIGGER name
|
| ENABLE_P TRIGGER name
|
||||||
{
|
{
|
||||||
@ -12963,6 +12977,7 @@ unreserved_keyword:
|
|||||||
| LOCAL
|
| LOCAL
|
||||||
| LOCATION
|
| LOCATION
|
||||||
| LOCK_P
|
| LOCK_P
|
||||||
|
| LOGGED
|
||||||
| MAPPING
|
| MAPPING
|
||||||
| MATCH
|
| MATCH
|
||||||
| MATERIALIZED
|
| MATERIALIZED
|
||||||
|
@ -1641,12 +1641,12 @@ psql_completion(const char *text, int start, int end)
|
|||||||
completion_info_charp = prev3_wd;
|
completion_info_charp = prev3_wd;
|
||||||
COMPLETE_WITH_QUERY(Query_for_index_of_table);
|
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 &&
|
else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 &&
|
||||||
pg_strcasecmp(prev_wd, "SET") == 0)
|
pg_strcasecmp(prev_wd, "SET") == 0)
|
||||||
{
|
{
|
||||||
static const char *const list_TABLESET[] =
|
static const char *const list_TABLESET[] =
|
||||||
{"(", "WITHOUT", "TABLESPACE", "SCHEMA", NULL};
|
{"(", "LOGGED", "SCHEMA", "TABLESPACE", "UNLOGGED", "WITH", "WITHOUT", NULL};
|
||||||
|
|
||||||
COMPLETE_WITH_LIST(list_TABLESET);
|
COMPLETE_WITH_LIST(list_TABLESET);
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ extern void check_index_is_clusterable(Relation OldHeap, Oid indexOid,
|
|||||||
bool recheck, LOCKMODE lockmode);
|
bool recheck, LOCKMODE lockmode);
|
||||||
extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal);
|
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);
|
LOCKMODE lockmode);
|
||||||
extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap,
|
extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap,
|
||||||
bool is_system_catalog,
|
bool is_system_catalog,
|
||||||
|
@ -1307,6 +1307,8 @@ typedef enum AlterTableType
|
|||||||
AT_ChangeOwner, /* change owner */
|
AT_ChangeOwner, /* change owner */
|
||||||
AT_ClusterOn, /* CLUSTER ON */
|
AT_ClusterOn, /* CLUSTER ON */
|
||||||
AT_DropCluster, /* SET WITHOUT CLUSTER */
|
AT_DropCluster, /* SET WITHOUT CLUSTER */
|
||||||
|
AT_SetLogged, /* SET LOGGED */
|
||||||
|
AT_SetUnLogged, /* SET UNLOGGED */
|
||||||
AT_AddOids, /* SET WITH OIDS */
|
AT_AddOids, /* SET WITH OIDS */
|
||||||
AT_AddOidsRecurse, /* internal to commands/tablecmds.c */
|
AT_AddOidsRecurse, /* internal to commands/tablecmds.c */
|
||||||
AT_DropOids, /* SET WITHOUT OIDS */
|
AT_DropOids, /* SET WITHOUT OIDS */
|
||||||
|
@ -230,6 +230,7 @@ PG_KEYWORD("localtime", LOCALTIME, RESERVED_KEYWORD)
|
|||||||
PG_KEYWORD("localtimestamp", LOCALTIMESTAMP, RESERVED_KEYWORD)
|
PG_KEYWORD("localtimestamp", LOCALTIMESTAMP, RESERVED_KEYWORD)
|
||||||
PG_KEYWORD("location", LOCATION, UNRESERVED_KEYWORD)
|
PG_KEYWORD("location", LOCATION, UNRESERVED_KEYWORD)
|
||||||
PG_KEYWORD("lock", LOCK_P, UNRESERVED_KEYWORD)
|
PG_KEYWORD("lock", LOCK_P, UNRESERVED_KEYWORD)
|
||||||
|
PG_KEYWORD("logged", LOGGED, UNRESERVED_KEYWORD)
|
||||||
PG_KEYWORD("mapping", MAPPING, UNRESERVED_KEYWORD)
|
PG_KEYWORD("mapping", MAPPING, UNRESERVED_KEYWORD)
|
||||||
PG_KEYWORD("match", MATCH, UNRESERVED_KEYWORD)
|
PG_KEYWORD("match", MATCH, UNRESERVED_KEYWORD)
|
||||||
PG_KEYWORD("materialized", MATERIALIZED, UNRESERVED_KEYWORD)
|
PG_KEYWORD("materialized", MATERIALIZED, UNRESERVED_KEYWORD)
|
||||||
|
@ -2426,3 +2426,94 @@ TRUNCATE old_system_table;
|
|||||||
ALTER TABLE old_system_table DROP CONSTRAINT new_system_table_pkey;
|
ALTER TABLE old_system_table DROP CONSTRAINT new_system_table_pkey;
|
||||||
ALTER TABLE old_system_table DROP COLUMN othercol;
|
ALTER TABLE old_system_table DROP COLUMN othercol;
|
||||||
DROP TABLE old_system_table;
|
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;
|
||||||
|
@ -1624,3 +1624,55 @@ TRUNCATE old_system_table;
|
|||||||
ALTER TABLE old_system_table DROP CONSTRAINT new_system_table_pkey;
|
ALTER TABLE old_system_table DROP CONSTRAINT new_system_table_pkey;
|
||||||
ALTER TABLE old_system_table DROP COLUMN othercol;
|
ALTER TABLE old_system_table DROP COLUMN othercol;
|
||||||
DROP TABLE old_system_table;
|
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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user