@@ -176,6 +176,7 @@ typedef struct AlteredTableInfo
176176 List *afterStmts; /* List of utility command parsetrees */
177177 bool verify_new_notnull; /* T if we should recheck NOT NULL */
178178 int rewrite; /* Reason for forced rewrite, if any */
179+ Oid newAccessMethod; /* new access method; 0 means no change */
179180 Oid newTableSpace; /* new tablespace; 0 means no change */
180181 bool chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
181182 char newrelpersistence; /* if above is true */
@@ -538,6 +539,7 @@ static void change_owner_recurse_to_sequences(Oid relationOid,
538539static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
539540 LOCKMODE lockmode);
540541static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
542+ static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname);
541543static bool ATPrepChangePersistence(Relation rel, bool toLogged);
542544static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
543545 const char *tablespacename, LOCKMODE lockmode);
@@ -4096,6 +4098,7 @@ AlterTableGetLockLevel(List *cmds)
40964098 */
40974099 case AT_AddColumn: /* may rewrite heap, in some cases and visible
40984100 * to SELECT */
4101+ case AT_SetAccessMethod: /* must rewrite heap */
40994102 case AT_SetTableSpace: /* must rewrite heap */
41004103 case AT_AlterColumnType: /* must rewrite heap */
41014104 cmd_lockmode = AccessExclusiveLock;
@@ -4622,6 +4625,24 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
46224625 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
46234626 pass = AT_PASS_DROP;
46244627 break;
4628+ case AT_SetAccessMethod: /* SET ACCESS METHOD */
4629+ ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
4630+
4631+ /* partitioned tables don't have an access method */
4632+ if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
4633+ ereport(ERROR,
4634+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
4635+ errmsg("cannot change access method of a partitioned table")));
4636+
4637+ /* check if another access method change was already requested */
4638+ if (OidIsValid(tab->newAccessMethod))
4639+ ereport(ERROR,
4640+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4641+ errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
4642+
4643+ ATPrepSetAccessMethod(tab, rel, cmd->name);
4644+ pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
4645+ break;
46254646 case AT_SetTableSpace: /* SET TABLESPACE */
46264647 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX |
46274648 ATT_PARTITIONED_INDEX);
@@ -4997,6 +5018,9 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
49975018 case AT_DropOids: /* SET WITHOUT OIDS */
49985019 /* nothing to do here, oid columns don't exist anymore */
49995020 break;
5021+ case AT_SetAccessMethod: /* SET ACCESS METHOD */
5022+ /* handled specially in Phase 3 */
5023+ break;
50005024 case AT_SetTableSpace: /* SET TABLESPACE */
50015025
50025026 /*
@@ -5324,7 +5348,7 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
53245348
53255349 /*
53265350 * We only need to rewrite the table if at least one column needs to
5327- * be recomputed, or we are changing its persistence.
5351+ * be recomputed, or we are changing its persistence or access method .
53285352 *
53295353 * There are two reasons for requiring a rewrite when changing
53305354 * persistence: on one hand, we need to ensure that the buffers
@@ -5338,6 +5362,7 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
53385362 /* Build a temporary relation and copy data */
53395363 Relation OldHeap;
53405364 Oid OIDNewHeap;
5365+ Oid NewAccessMethod;
53415366 Oid NewTableSpace;
53425367 char persistence;
53435368
@@ -5378,6 +5403,15 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
53785403 else
53795404 NewTableSpace = OldHeap->rd_rel->reltablespace;
53805405
5406+ /*
5407+ * Select destination access method (same as original unless user
5408+ * requested a change)
5409+ */
5410+ if (OidIsValid(tab->newAccessMethod))
5411+ NewAccessMethod = tab->newAccessMethod;
5412+ else
5413+ NewAccessMethod = OldHeap->rd_rel->relam;
5414+
53815415 /*
53825416 * Select persistence of transient table (same as original unless
53835417 * user requested a change)
@@ -5417,8 +5451,8 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
54175451 * persistence. That wouldn't work for pg_class, but that can't be
54185452 * unlogged anyway.
54195453 */
5420- OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, persistence ,
5421- lockmode);
5454+ OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod ,
5455+ persistence, lockmode);
54225456
54235457 /*
54245458 * Copy the heap data into the new table with the desired
@@ -5933,6 +5967,8 @@ ATGetQueueEntry(List **wqueue, Relation rel)
59335967 tab->rel = NULL; /* set later */
59345968 tab->relkind = rel->rd_rel->relkind;
59355969 tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
5970+ tab->newAccessMethod = InvalidOid;
5971+ tab->newTableSpace = InvalidOid;
59365972 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
59375973 tab->chgPersistence = false;
59385974
@@ -6003,6 +6039,8 @@ alter_table_type_to_string(AlterTableType cmdtype)
60036039 return "CLUSTER ON";
60046040 case AT_DropCluster:
60056041 return "SET WITHOUT CLUSTER";
6042+ case AT_SetAccessMethod:
6043+ return "SET ACCESS METHOD";
60066044 case AT_SetLogged:
60076045 return "SET LOGGED";
60086046 case AT_SetUnLogged:
@@ -13609,6 +13647,28 @@ ATExecDropCluster(Relation rel, LOCKMODE lockmode)
1360913647 mark_index_clustered(rel, InvalidOid, false);
1361013648}
1361113649
13650+ /*
13651+ * Preparation phase for SET ACCESS METHOD
13652+ *
13653+ * Check that access method exists. If it is the same as the table's current
13654+ * access method, it is a no-op. Otherwise, a table rewrite is necessary.
13655+ */
13656+ static void
13657+ ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
13658+ {
13659+ Oid amoid;
13660+
13661+ /* Check that the table access method exists */
13662+ amoid = get_table_am_oid(amname, false);
13663+
13664+ if (rel->rd_rel->relam == amoid)
13665+ return;
13666+
13667+ /* Save info for Phase 3 to do the real work */
13668+ tab->rewrite |= AT_REWRITE_ACCESS_METHOD;
13669+ tab->newAccessMethod = amoid;
13670+ }
13671+
1361213672/*
1361313673 * ALTER TABLE SET TABLESPACE
1361413674 */
0 commit comments