mirror of
https://github.com/postgres/postgres.git
synced 2025-07-03 20:02:46 +03:00
Add support for SET ACCESS METHOD in ALTER TABLE
The logic used to support a change of access method for a table is similar to changes for tablespace or relation persistence, requiring a table rewrite with an exclusive lock of the relation changed. Table rewrites done in ALTER TABLE already go through the table AM layer when scanning tuples from the old relation and inserting them into the new one, making this implementation straight-forward. Note that partitioned tables are not supported as these have no access methods defined. Author: Justin Pryzby, Jeff Davis Reviewed-by: Michael Paquier, Vignesh C Discussion: https://postgr.es/m/20210228222530.GD20769@telsasoft.com
This commit is contained in:
@ -176,6 +176,7 @@ typedef struct AlteredTableInfo
|
||||
List *afterStmts; /* List of utility command parsetrees */
|
||||
bool verify_new_notnull; /* T if we should recheck NOT NULL */
|
||||
int rewrite; /* Reason for forced rewrite, if any */
|
||||
Oid newAccessMethod; /* new access method; 0 means no change */
|
||||
Oid newTableSpace; /* new tablespace; 0 means no change */
|
||||
bool chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
|
||||
char newrelpersistence; /* if above is true */
|
||||
@ -538,6 +539,7 @@ static void change_owner_recurse_to_sequences(Oid relationOid,
|
||||
static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
|
||||
LOCKMODE lockmode);
|
||||
static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
|
||||
static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname);
|
||||
static bool ATPrepChangePersistence(Relation rel, bool toLogged);
|
||||
static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
|
||||
const char *tablespacename, LOCKMODE lockmode);
|
||||
@ -4096,6 +4098,7 @@ AlterTableGetLockLevel(List *cmds)
|
||||
*/
|
||||
case AT_AddColumn: /* may rewrite heap, in some cases and visible
|
||||
* to SELECT */
|
||||
case AT_SetAccessMethod: /* must rewrite heap */
|
||||
case AT_SetTableSpace: /* must rewrite heap */
|
||||
case AT_AlterColumnType: /* must rewrite heap */
|
||||
cmd_lockmode = AccessExclusiveLock;
|
||||
@ -4622,6 +4625,24 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
|
||||
ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
|
||||
pass = AT_PASS_DROP;
|
||||
break;
|
||||
case AT_SetAccessMethod: /* SET ACCESS METHOD */
|
||||
ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
|
||||
|
||||
/* partitioned tables don't have an access method */
|
||||
if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
||||
errmsg("cannot change access method of a partitioned table")));
|
||||
|
||||
/* check if another access method change was already requested */
|
||||
if (OidIsValid(tab->newAccessMethod))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
|
||||
|
||||
ATPrepSetAccessMethod(tab, rel, cmd->name);
|
||||
pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
|
||||
break;
|
||||
case AT_SetTableSpace: /* SET TABLESPACE */
|
||||
ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX |
|
||||
ATT_PARTITIONED_INDEX);
|
||||
@ -4997,6 +5018,9 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
|
||||
case AT_DropOids: /* SET WITHOUT OIDS */
|
||||
/* nothing to do here, oid columns don't exist anymore */
|
||||
break;
|
||||
case AT_SetAccessMethod: /* SET ACCESS METHOD */
|
||||
/* handled specially in Phase 3 */
|
||||
break;
|
||||
case AT_SetTableSpace: /* SET TABLESPACE */
|
||||
|
||||
/*
|
||||
@ -5324,7 +5348,7 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
|
||||
|
||||
/*
|
||||
* We only need to rewrite the table if at least one column needs to
|
||||
* be recomputed, or we are changing its persistence.
|
||||
* be recomputed, or we are changing its persistence or access method.
|
||||
*
|
||||
* There are two reasons for requiring a rewrite when changing
|
||||
* persistence: on one hand, we need to ensure that the buffers
|
||||
@ -5338,6 +5362,7 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
|
||||
/* Build a temporary relation and copy data */
|
||||
Relation OldHeap;
|
||||
Oid OIDNewHeap;
|
||||
Oid NewAccessMethod;
|
||||
Oid NewTableSpace;
|
||||
char persistence;
|
||||
|
||||
@ -5378,6 +5403,15 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
|
||||
else
|
||||
NewTableSpace = OldHeap->rd_rel->reltablespace;
|
||||
|
||||
/*
|
||||
* Select destination access method (same as original unless user
|
||||
* requested a change)
|
||||
*/
|
||||
if (OidIsValid(tab->newAccessMethod))
|
||||
NewAccessMethod = tab->newAccessMethod;
|
||||
else
|
||||
NewAccessMethod = OldHeap->rd_rel->relam;
|
||||
|
||||
/*
|
||||
* Select persistence of transient table (same as original unless
|
||||
* user requested a change)
|
||||
@ -5417,8 +5451,8 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
|
||||
* persistence. That wouldn't work for pg_class, but that can't be
|
||||
* unlogged anyway.
|
||||
*/
|
||||
OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, persistence,
|
||||
lockmode);
|
||||
OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
|
||||
persistence, lockmode);
|
||||
|
||||
/*
|
||||
* Copy the heap data into the new table with the desired
|
||||
@ -5933,6 +5967,8 @@ ATGetQueueEntry(List **wqueue, Relation rel)
|
||||
tab->rel = NULL; /* set later */
|
||||
tab->relkind = rel->rd_rel->relkind;
|
||||
tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
|
||||
tab->newAccessMethod = InvalidOid;
|
||||
tab->newTableSpace = InvalidOid;
|
||||
tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
|
||||
tab->chgPersistence = false;
|
||||
|
||||
@ -6003,6 +6039,8 @@ alter_table_type_to_string(AlterTableType cmdtype)
|
||||
return "CLUSTER ON";
|
||||
case AT_DropCluster:
|
||||
return "SET WITHOUT CLUSTER";
|
||||
case AT_SetAccessMethod:
|
||||
return "SET ACCESS METHOD";
|
||||
case AT_SetLogged:
|
||||
return "SET LOGGED";
|
||||
case AT_SetUnLogged:
|
||||
@ -13609,6 +13647,28 @@ ATExecDropCluster(Relation rel, LOCKMODE lockmode)
|
||||
mark_index_clustered(rel, InvalidOid, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Preparation phase for SET ACCESS METHOD
|
||||
*
|
||||
* Check that access method exists. If it is the same as the table's current
|
||||
* access method, it is a no-op. Otherwise, a table rewrite is necessary.
|
||||
*/
|
||||
static void
|
||||
ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
|
||||
{
|
||||
Oid amoid;
|
||||
|
||||
/* Check that the table access method exists */
|
||||
amoid = get_table_am_oid(amname, false);
|
||||
|
||||
if (rel->rd_rel->relam == amoid)
|
||||
return;
|
||||
|
||||
/* Save info for Phase 3 to do the real work */
|
||||
tab->rewrite |= AT_REWRITE_ACCESS_METHOD;
|
||||
tab->newAccessMethod = amoid;
|
||||
}
|
||||
|
||||
/*
|
||||
* ALTER TABLE SET TABLESPACE
|
||||
*/
|
||||
|
Reference in New Issue
Block a user