@@ -176,6 +176,7 @@ typedef struct AlteredTableInfo
176
176
List *afterStmts; /* List of utility command parsetrees */
177
177
bool verify_new_notnull; /* T if we should recheck NOT NULL */
178
178
int rewrite; /* Reason for forced rewrite, if any */
179
+ Oid newAccessMethod; /* new access method; 0 means no change */
179
180
Oid newTableSpace; /* new tablespace; 0 means no change */
180
181
bool chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
181
182
char newrelpersistence; /* if above is true */
@@ -538,6 +539,7 @@ static void change_owner_recurse_to_sequences(Oid relationOid,
538
539
static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
539
540
LOCKMODE lockmode);
540
541
static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
542
+ static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname);
541
543
static bool ATPrepChangePersistence(Relation rel, bool toLogged);
542
544
static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
543
545
const char *tablespacename, LOCKMODE lockmode);
@@ -4096,6 +4098,7 @@ AlterTableGetLockLevel(List *cmds)
4096
4098
*/
4097
4099
case AT_AddColumn: /* may rewrite heap, in some cases and visible
4098
4100
* to SELECT */
4101
+ case AT_SetAccessMethod: /* must rewrite heap */
4099
4102
case AT_SetTableSpace: /* must rewrite heap */
4100
4103
case AT_AlterColumnType: /* must rewrite heap */
4101
4104
cmd_lockmode = AccessExclusiveLock;
@@ -4622,6 +4625,24 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
4622
4625
ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4623
4626
pass = AT_PASS_DROP;
4624
4627
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;
4625
4646
case AT_SetTableSpace: /* SET TABLESPACE */
4626
4647
ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX |
4627
4648
ATT_PARTITIONED_INDEX);
@@ -4997,6 +5018,9 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
4997
5018
case AT_DropOids: /* SET WITHOUT OIDS */
4998
5019
/* nothing to do here, oid columns don't exist anymore */
4999
5020
break;
5021
+ case AT_SetAccessMethod: /* SET ACCESS METHOD */
5022
+ /* handled specially in Phase 3 */
5023
+ break;
5000
5024
case AT_SetTableSpace: /* SET TABLESPACE */
5001
5025
5002
5026
/*
@@ -5324,7 +5348,7 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
5324
5348
5325
5349
/*
5326
5350
* 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 .
5328
5352
*
5329
5353
* There are two reasons for requiring a rewrite when changing
5330
5354
* persistence: on one hand, we need to ensure that the buffers
@@ -5338,6 +5362,7 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
5338
5362
/* Build a temporary relation and copy data */
5339
5363
Relation OldHeap;
5340
5364
Oid OIDNewHeap;
5365
+ Oid NewAccessMethod;
5341
5366
Oid NewTableSpace;
5342
5367
char persistence;
5343
5368
@@ -5378,6 +5403,15 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
5378
5403
else
5379
5404
NewTableSpace = OldHeap->rd_rel->reltablespace;
5380
5405
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
+
5381
5415
/*
5382
5416
* Select persistence of transient table (same as original unless
5383
5417
* user requested a change)
@@ -5417,8 +5451,8 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
5417
5451
* persistence. That wouldn't work for pg_class, but that can't be
5418
5452
* unlogged anyway.
5419
5453
*/
5420
- OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, persistence ,
5421
- lockmode);
5454
+ OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod ,
5455
+ persistence, lockmode);
5422
5456
5423
5457
/*
5424
5458
* Copy the heap data into the new table with the desired
@@ -5933,6 +5967,8 @@ ATGetQueueEntry(List **wqueue, Relation rel)
5933
5967
tab->rel = NULL; /* set later */
5934
5968
tab->relkind = rel->rd_rel->relkind;
5935
5969
tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
5970
+ tab->newAccessMethod = InvalidOid;
5971
+ tab->newTableSpace = InvalidOid;
5936
5972
tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
5937
5973
tab->chgPersistence = false;
5938
5974
@@ -6003,6 +6039,8 @@ alter_table_type_to_string(AlterTableType cmdtype)
6003
6039
return "CLUSTER ON";
6004
6040
case AT_DropCluster:
6005
6041
return "SET WITHOUT CLUSTER";
6042
+ case AT_SetAccessMethod:
6043
+ return "SET ACCESS METHOD";
6006
6044
case AT_SetLogged:
6007
6045
return "SET LOGGED";
6008
6046
case AT_SetUnLogged:
@@ -13609,6 +13647,28 @@ ATExecDropCluster(Relation rel, LOCKMODE lockmode)
13609
13647
mark_index_clustered(rel, InvalidOid, false);
13610
13648
}
13611
13649
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
+
13612
13672
/*
13613
13673
* ALTER TABLE SET TABLESPACE
13614
13674
*/
0 commit comments