@@ -184,7 +184,9 @@ typedef struct AlteredTableInfo
184
184
List *afterStmts; /* List of utility command parsetrees */
185
185
bool verify_new_notnull; /* T if we should recheck NOT NULL */
186
186
int rewrite; /* Reason for forced rewrite, if any */
187
- Oid newAccessMethod; /* new access method; 0 means no change */
187
+ bool chgAccessMethod; /* T if SET ACCESS METHOD is used */
188
+ Oid newAccessMethod; /* new access method; 0 means no change,
189
+ * if above is true */
188
190
Oid newTableSpace; /* new tablespace; 0 means no change */
189
191
bool chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
190
192
char newrelpersistence; /* if above is true */
@@ -595,6 +597,7 @@ static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
595
597
LOCKMODE lockmode);
596
598
static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
597
599
static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname);
600
+ static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethod);
598
601
static bool ATPrepChangePersistence(Relation rel, bool toLogged);
599
602
static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
600
603
const char *tablespacename, LOCKMODE lockmode);
@@ -709,7 +712,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
709
712
Oid ofTypeId;
710
713
ObjectAddress address;
711
714
LOCKMODE parentLockmode;
712
- const char *accessMethod = NULL;
713
715
Oid accessMethodId = InvalidOid;
714
716
715
717
/*
@@ -954,24 +956,22 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
954
956
}
955
957
956
958
/*
957
- * If the statement hasn't specified an access method, but we're defining
958
- * a type of relation that needs one, use the default .
959
+ * Select access method to use: an explicitly indicated one, or (in the
960
+ * case of a partitioned table) the parent's, if it has one .
959
961
*/
960
962
if (stmt->accessMethod != NULL)
963
+ accessMethodId = get_table_am_oid(stmt->accessMethod, false);
964
+ else if (stmt->partbound)
961
965
{
962
- accessMethod = stmt->accessMethod;
963
-
964
- if (partitioned)
965
- ereport(ERROR,
966
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
967
- errmsg("specifying a table access method is not supported on a partitioned table")));
966
+ Assert(list_length(inheritOids) == 1);
967
+ accessMethodId = get_rel_relam(linitial_oid(inheritOids));
968
968
}
969
- else if (RELKIND_HAS_TABLE_AM(relkind))
970
- accessMethod = default_table_access_method ;
969
+ else
970
+ accessMethodId = InvalidOid ;
971
971
972
- /* look up the access method, verify it is for a table */
973
- if (accessMethod != NULL )
974
- accessMethodId = get_table_am_oid(accessMethod , false);
972
+ /* still nothing? use the default */
973
+ if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId) )
974
+ accessMethodId = get_table_am_oid(default_table_access_method , false);
975
975
976
976
/*
977
977
* Create the relation. Inherited defaults and constraints are passed in
@@ -5047,14 +5047,8 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
5047
5047
case AT_SetAccessMethod: /* SET ACCESS METHOD */
5048
5048
ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
5049
5049
5050
- /* partitioned tables don't have an access method */
5051
- if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5052
- ereport(ERROR,
5053
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
5054
- errmsg("cannot change access method of a partitioned table")));
5055
-
5056
5050
/* check if another access method change was already requested */
5057
- if (OidIsValid( tab->newAccessMethod) )
5051
+ if (tab->chgAccessMethod )
5058
5052
ereport(ERROR,
5059
5053
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5060
5054
errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
@@ -5408,7 +5402,14 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab,
5408
5402
/* nothing to do here, oid columns don't exist anymore */
5409
5403
break;
5410
5404
case AT_SetAccessMethod: /* SET ACCESS METHOD */
5411
- /* handled specially in Phase 3 */
5405
+
5406
+ /*
5407
+ * Only do this for partitioned tables, for which this is just a
5408
+ * catalog change. Tables with storage are handled by Phase 3.
5409
+ */
5410
+ if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5411
+ tab->chgAccessMethod)
5412
+ ATExecSetAccessMethodNoStorage(rel, tab->newAccessMethod);
5412
5413
break;
5413
5414
case AT_SetTableSpace: /* SET TABLESPACE */
5414
5415
@@ -5814,7 +5815,7 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
5814
5815
* Select destination access method (same as original unless user
5815
5816
* requested a change)
5816
5817
*/
5817
- if (OidIsValid( tab->newAccessMethod) )
5818
+ if (tab->chgAccessMethod )
5818
5819
NewAccessMethod = tab->newAccessMethod;
5819
5820
else
5820
5821
NewAccessMethod = OldHeap->rd_rel->relam;
@@ -6402,6 +6403,7 @@ ATGetQueueEntry(List **wqueue, Relation rel)
6402
6403
tab->relkind = rel->rd_rel->relkind;
6403
6404
tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
6404
6405
tab->newAccessMethod = InvalidOid;
6406
+ tab->chgAccessMethod = false;
6405
6407
tab->newTableSpace = InvalidOid;
6406
6408
tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
6407
6409
tab->chgPersistence = false;
@@ -15343,25 +15345,128 @@ ATExecDropCluster(Relation rel, LOCKMODE lockmode)
15343
15345
/*
15344
15346
* Preparation phase for SET ACCESS METHOD
15345
15347
*
15346
- * Check that access method exists. If it is the same as the table's current
15347
- * access method, it is a no-op. Otherwise, a table rewrite is necessary.
15348
- * If amname is NULL, select default_table_access_method as access method.
15348
+ * Check that the access method exists and determine whether a change is
15349
+ * actually needed.
15349
15350
*/
15350
15351
static void
15351
15352
ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
15352
15353
{
15353
15354
Oid amoid;
15354
15355
15355
- /* Check that the table access method exists */
15356
- amoid = get_table_am_oid(amname ? amname : default_table_access_method,
15357
- false);
15356
+ /*
15357
+ * Look up the access method name and check that it differs from the
15358
+ * table's current AM. If DEFAULT was specified for a partitioned table
15359
+ * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
15360
+ */
15361
+ if (amname != NULL)
15362
+ amoid = get_table_am_oid(amname, false);
15363
+ else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15364
+ amoid = InvalidOid;
15365
+ else
15366
+ amoid = get_table_am_oid(default_table_access_method, false);
15358
15367
15368
+ /* if it's a match, phase 3 doesn't need to do anything */
15359
15369
if (rel->rd_rel->relam == amoid)
15360
15370
return;
15361
15371
15362
15372
/* Save info for Phase 3 to do the real work */
15363
15373
tab->rewrite |= AT_REWRITE_ACCESS_METHOD;
15364
15374
tab->newAccessMethod = amoid;
15375
+ tab->chgAccessMethod = true;
15376
+ }
15377
+
15378
+ /*
15379
+ * Special handling of ALTER TABLE SET ACCESS METHOD for relations with no
15380
+ * storage that have an interest in preserving AM.
15381
+ *
15382
+ * Since these have no storage, setting the access method is a catalog only
15383
+ * operation.
15384
+ */
15385
+ static void
15386
+ ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
15387
+ {
15388
+ Relation pg_class;
15389
+ Oid oldAccessMethodId;
15390
+ HeapTuple tuple;
15391
+ Form_pg_class rd_rel;
15392
+ Oid reloid = RelationGetRelid(rel);
15393
+
15394
+ /*
15395
+ * Shouldn't be called on relations having storage; these are processed in
15396
+ * phase 3.
15397
+ */
15398
+ Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
15399
+
15400
+ /* Get a modifiable copy of the relation's pg_class row. */
15401
+ pg_class = table_open(RelationRelationId, RowExclusiveLock);
15402
+
15403
+ tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
15404
+ if (!HeapTupleIsValid(tuple))
15405
+ elog(ERROR, "cache lookup failed for relation %u", reloid);
15406
+ rd_rel = (Form_pg_class) GETSTRUCT(tuple);
15407
+
15408
+ /* Update the pg_class row. */
15409
+ oldAccessMethodId = rd_rel->relam;
15410
+ rd_rel->relam = newAccessMethodId;
15411
+
15412
+ /* Leave if no update required */
15413
+ if (rd_rel->relam == oldAccessMethodId)
15414
+ {
15415
+ heap_freetuple(tuple);
15416
+ table_close(pg_class, RowExclusiveLock);
15417
+ return;
15418
+ }
15419
+
15420
+ CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
15421
+
15422
+ /*
15423
+ * Update the dependency on the new access method. No dependency is added
15424
+ * if the new access method is InvalidOid (default case). Be very careful
15425
+ * that this has to compare the previous value stored in pg_class with the
15426
+ * new one.
15427
+ */
15428
+ if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
15429
+ {
15430
+ ObjectAddress relobj,
15431
+ referenced;
15432
+
15433
+ /*
15434
+ * New access method is defined and there was no dependency
15435
+ * previously, so record a new one.
15436
+ */
15437
+ ObjectAddressSet(relobj, RelationRelationId, reloid);
15438
+ ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
15439
+ recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
15440
+ }
15441
+ else if (OidIsValid(oldAccessMethodId) &&
15442
+ !OidIsValid(rd_rel->relam))
15443
+ {
15444
+ /*
15445
+ * There was an access method defined, and no new one, so just remove
15446
+ * the existing dependency.
15447
+ */
15448
+ deleteDependencyRecordsForClass(RelationRelationId, reloid,
15449
+ AccessMethodRelationId,
15450
+ DEPENDENCY_NORMAL);
15451
+ }
15452
+ else
15453
+ {
15454
+ Assert(OidIsValid(oldAccessMethodId) &&
15455
+ OidIsValid(rd_rel->relam));
15456
+
15457
+ /* Both are valid, so update the dependency */
15458
+ changeDependencyFor(RelationRelationId, reloid,
15459
+ AccessMethodRelationId,
15460
+ oldAccessMethodId, rd_rel->relam);
15461
+ }
15462
+
15463
+ /* make the relam and dependency changes visible */
15464
+ CommandCounterIncrement();
15465
+
15466
+ InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15467
+
15468
+ heap_freetuple(tuple);
15469
+ table_close(pg_class, RowExclusiveLock);
15365
15470
}
15366
15471
15367
15472
/*
0 commit comments