@@ -3037,6 +3037,113 @@ SetRelationHasSubclass(Oid relationId, bool relhassubclass)
30373037 table_close(relationRelation, RowExclusiveLock);
30383038}
30393039
3040+ /*
3041+ * CheckRelationTableSpaceMove
3042+ * Check if relation can be moved to new tablespace.
3043+ *
3044+ * NOTE: Caller must be holding an appropriate lock on the relation.
3045+ * ShareUpdateExclusiveLock is sufficient.
3046+ *
3047+ * Returns true if the relation can be moved to the new tablespace;
3048+ * false otherwise.
3049+ */
3050+ bool
3051+ CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
3052+ {
3053+ Oid oldTableSpaceId;
3054+
3055+ /*
3056+ * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3057+ * stored as 0.
3058+ */
3059+ oldTableSpaceId = rel->rd_rel->reltablespace;
3060+ if (newTableSpaceId == oldTableSpaceId ||
3061+ (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
3062+ return false;
3063+
3064+ /*
3065+ * We cannot support moving mapped relations into different tablespaces.
3066+ * (In particular this eliminates all shared catalogs.)
3067+ */
3068+ if (RelationIsMapped(rel))
3069+ ereport(ERROR,
3070+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3071+ errmsg("cannot move system relation \"%s\"",
3072+ RelationGetRelationName(rel))));
3073+
3074+ /* Cannot move a non-shared relation into pg_global */
3075+ if (newTableSpaceId == GLOBALTABLESPACE_OID)
3076+ ereport(ERROR,
3077+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3078+ errmsg("only shared relations can be placed in pg_global tablespace")));
3079+
3080+ /*
3081+ * Do not allow moving temp tables of other backends ... their local
3082+ * buffer manager is not going to cope.
3083+ */
3084+ if (RELATION_IS_OTHER_TEMP(rel))
3085+ ereport(ERROR,
3086+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3087+ errmsg("cannot move temporary tables of other sessions")));
3088+
3089+ return true;
3090+ }
3091+
3092+ /*
3093+ * SetRelationTableSpace
3094+ * Set new reltablespace and relfilenode in pg_class entry.
3095+ *
3096+ * newTableSpaceId is the new tablespace for the relation, and
3097+ * newRelFileNode its new filenode. If newrelfilenode is InvalidOid,
3098+ * this field is not updated.
3099+ *
3100+ * NOTE: Caller must be holding an appropriate lock on the relation.
3101+ * ShareUpdateExclusiveLock is sufficient.
3102+ *
3103+ * The caller of this routine had better check if a relation can be
3104+ * moved to this new tablespace by calling CheckRelationTableSpaceMove()
3105+ * first, and is responsible for making the change visible with
3106+ * CommandCounterIncrement().
3107+ */
3108+ void
3109+ SetRelationTableSpace(Relation rel,
3110+ Oid newTableSpaceId,
3111+ Oid newRelFileNode)
3112+ {
3113+ Relation pg_class;
3114+ HeapTuple tuple;
3115+ Form_pg_class rd_rel;
3116+ Oid reloid = RelationGetRelid(rel);
3117+
3118+ Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
3119+
3120+ /* Get a modifiable copy of the relation's pg_class row. */
3121+ pg_class = table_open(RelationRelationId, RowExclusiveLock);
3122+
3123+ tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
3124+ if (!HeapTupleIsValid(tuple))
3125+ elog(ERROR, "cache lookup failed for relation %u", reloid);
3126+ rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3127+
3128+ /* Update the pg_class row. */
3129+ rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3130+ InvalidOid : newTableSpaceId;
3131+ if (OidIsValid(newRelFileNode))
3132+ rd_rel->relfilenode = newRelFileNode;
3133+ CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
3134+
3135+ /*
3136+ * Record dependency on tablespace. This is only required for relations
3137+ * that have no physical storage.
3138+ */
3139+ if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3140+ changeDependencyOnTablespace(RelationRelationId, reloid,
3141+ rd_rel->reltablespace);
3142+
3143+ heap_freetuple(tuple);
3144+ table_close(pg_class, RowExclusiveLock);
3145+ }
3146+
30403147/*
30413148 * renameatt_check - basic sanity checks before attribute rename
30423149 */
@@ -13160,13 +13267,9 @@ static void
1316013267ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
1316113268{
1316213269 Relation rel;
13163- Oid oldTableSpace;
1316413270 Oid reltoastrelid;
1316513271 Oid newrelfilenode;
1316613272 RelFileNode newrnode;
13167- Relation pg_class;
13168- HeapTuple tuple;
13169- Form_pg_class rd_rel;
1317013273 List *reltoastidxids = NIL;
1317113274 ListCell *lc;
1317213275
@@ -13175,45 +13278,15 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
1317513278 */
1317613279 rel = relation_open(tableOid, lockmode);
1317713280
13178- /*
13179- * No work if no change in tablespace.
13180- */
13181- oldTableSpace = rel->rd_rel->reltablespace;
13182- if (newTableSpace == oldTableSpace ||
13183- (newTableSpace == MyDatabaseTableSpace && oldTableSpace == 0))
13281+ /* Check first if relation can be moved to new tablespace */
13282+ if (!CheckRelationTableSpaceMove(rel, newTableSpace))
1318413283 {
1318513284 InvokeObjectPostAlterHook(RelationRelationId,
1318613285 RelationGetRelid(rel), 0);
13187-
1318813286 relation_close(rel, NoLock);
1318913287 return;
1319013288 }
1319113289
13192- /*
13193- * We cannot support moving mapped relations into different tablespaces.
13194- * (In particular this eliminates all shared catalogs.)
13195- */
13196- if (RelationIsMapped(rel))
13197- ereport(ERROR,
13198- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13199- errmsg("cannot move system relation \"%s\"",
13200- RelationGetRelationName(rel))));
13201-
13202- /* Can't move a non-shared relation into pg_global */
13203- if (newTableSpace == GLOBALTABLESPACE_OID)
13204- ereport(ERROR,
13205- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
13206- errmsg("only shared relations can be placed in pg_global tablespace")));
13207-
13208- /*
13209- * Don't allow moving temp tables of other backends ... their local buffer
13210- * manager is not going to cope.
13211- */
13212- if (RELATION_IS_OTHER_TEMP(rel))
13213- ereport(ERROR,
13214- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13215- errmsg("cannot move temporary tables of other sessions")));
13216-
1321713290 reltoastrelid = rel->rd_rel->reltoastrelid;
1321813291 /* Fetch the list of indexes on toast relation if necessary */
1321913292 if (OidIsValid(reltoastrelid))
@@ -13224,14 +13297,6 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
1322413297 relation_close(toastRel, lockmode);
1322513298 }
1322613299
13227- /* Get a modifiable copy of the relation's pg_class row */
13228- pg_class = table_open(RelationRelationId, RowExclusiveLock);
13229-
13230- tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(tableOid));
13231- if (!HeapTupleIsValid(tuple))
13232- elog(ERROR, "cache lookup failed for relation %u", tableOid);
13233- rd_rel = (Form_pg_class) GETSTRUCT(tuple);
13234-
1323513300 /*
1323613301 * Relfilenodes are not unique in databases across tablespaces, so we need
1323713302 * to allocate a new one in the new tablespace.
@@ -13262,18 +13327,13 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
1326213327 *
1326313328 * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
1326413329 * executed on pg_class or its indexes (the above copy wouldn't contain
13265- * the updated pg_class entry), but that's forbidden above.
13330+ * the updated pg_class entry), but that's forbidden with
13331+ * CheckRelationTableSpaceMove().
1326613332 */
13267- rd_rel->reltablespace = (newTableSpace == MyDatabaseTableSpace) ? InvalidOid : newTableSpace;
13268- rd_rel->relfilenode = newrelfilenode;
13269- CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
13333+ SetRelationTableSpace(rel, newTableSpace, newrelfilenode);
1327013334
1327113335 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
1327213336
13273- heap_freetuple(tuple);
13274-
13275- table_close(pg_class, RowExclusiveLock);
13276-
1327713337 RelationAssumeNewRelfilenode(rel);
1327813338
1327913339 relation_close(rel, NoLock);
@@ -13301,56 +13361,25 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
1330113361static void
1330213362ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
1330313363{
13304- HeapTuple tuple;
13305- Oid oldTableSpace;
13306- Relation pg_class;
13307- Form_pg_class rd_rel;
13308- Oid reloid = RelationGetRelid(rel);
13309-
1331013364 /*
1331113365 * Shouldn't be called on relations having storage; these are processed in
1331213366 * phase 3.
1331313367 */
1331413368 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
1331513369
13316- /* Can't allow a non-shared relation in pg_global */
13317- if (newTableSpace == GLOBALTABLESPACE_OID)
13318- ereport(ERROR,
13319- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
13320- errmsg("only shared relations can be placed in pg_global tablespace")));
13321-
13322- /*
13323- * No work if no change in tablespace.
13324- */
13325- oldTableSpace = rel->rd_rel->reltablespace;
13326- if (newTableSpace == oldTableSpace ||
13327- (newTableSpace == MyDatabaseTableSpace && oldTableSpace == 0))
13370+ /* check if relation can be moved to its new tablespace */
13371+ if (!CheckRelationTableSpaceMove(rel, newTableSpace))
1332813372 {
13329- InvokeObjectPostAlterHook(RelationRelationId, reloid, 0);
13373+ InvokeObjectPostAlterHook(RelationRelationId,
13374+ RelationGetRelid(rel),
13375+ 0);
1333013376 return;
1333113377 }
1333213378
13333- /* Get a modifiable copy of the relation's pg_class row */
13334- pg_class = table_open(RelationRelationId, RowExclusiveLock);
13335-
13336- tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
13337- if (!HeapTupleIsValid(tuple))
13338- elog(ERROR, "cache lookup failed for relation %u", reloid);
13339- rd_rel = (Form_pg_class) GETSTRUCT(tuple);
13340-
13341- /* update the pg_class row */
13342- rd_rel->reltablespace = (newTableSpace == MyDatabaseTableSpace) ? InvalidOid : newTableSpace;
13343- CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
13344-
13345- /* Record dependency on tablespace */
13346- changeDependencyOnTablespace(RelationRelationId,
13347- reloid, rd_rel->reltablespace);
13348-
13349- InvokeObjectPostAlterHook(RelationRelationId, reloid, 0);
13379+ /* Update can be done, so change reltablespace */
13380+ SetRelationTableSpace(rel, newTableSpace, InvalidOid);
1335013381
13351- heap_freetuple(tuple);
13352-
13353- table_close(pg_class, RowExclusiveLock);
13382+ InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
1335413383
1335513384 /* Make sure the reltablespace change is visible */
1335613385 CommandCounterIncrement();
0 commit comments