@@ -3069,6 +3069,112 @@ SetRelationHasSubclass(Oid relationId, bool relhassubclass)
30693069table_close(relationRelation, RowExclusiveLock);
30703070}
30713071
3072+ /*
3073+ * CheckRelationTableSpaceMove
3074+ *Check if relation can be moved to new tablespace.
3075+ *
3076+ * NOTE: The caller must hold AccessExclusiveLock on the relation.
3077+ *
3078+ * Returns true if the relation can be moved to the new tablespace; raises
3079+ * an error if it is not possible to do the move; returns false if the move
3080+ * would have no effect.
3081+ */
3082+ bool
3083+ CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
3084+ {
3085+ OidoldTableSpaceId;
3086+
3087+ /*
3088+ * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3089+ * stored as 0.
3090+ */
3091+ oldTableSpaceId = rel->rd_rel->reltablespace;
3092+ if (newTableSpaceId == oldTableSpaceId ||
3093+ (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
3094+ return false;
3095+
3096+ /*
3097+ * We cannot support moving mapped relations into different tablespaces.
3098+ * (In particular this eliminates all shared catalogs.)
3099+ */
3100+ if (RelationIsMapped(rel))
3101+ ereport(ERROR,
3102+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3103+ errmsg("cannot move system relation \"%s\"",
3104+ RelationGetRelationName(rel))));
3105+
3106+ /* Cannot move a non-shared relation into pg_global */
3107+ if (newTableSpaceId == GLOBALTABLESPACE_OID)
3108+ ereport(ERROR,
3109+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3110+ errmsg("only shared relations can be placed in pg_global tablespace")));
3111+
3112+ /*
3113+ * Do not allow moving temp tables of other backends ... their local
3114+ * buffer manager is not going to cope.
3115+ */
3116+ if (RELATION_IS_OTHER_TEMP(rel))
3117+ ereport(ERROR,
3118+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3119+ errmsg("cannot move temporary tables of other sessions")));
3120+
3121+ return true;
3122+ }
3123+
3124+ /*
3125+ * SetRelationTableSpace
3126+ *Set new reltablespace and relfilenode in pg_class entry.
3127+ *
3128+ * newTableSpaceId is the new tablespace for the relation, and
3129+ * newRelFileNode its new filenode. If newRelFileNode is InvalidOid,
3130+ * this field is not updated.
3131+ *
3132+ * NOTE: The caller must hold AccessExclusiveLock on the relation.
3133+ *
3134+ * The caller of this routine had better check if a relation can be
3135+ * moved to this new tablespace by calling CheckRelationTableSpaceMove()
3136+ * first, and is responsible for making the change visible with
3137+ * CommandCounterIncrement().
3138+ */
3139+ void
3140+ SetRelationTableSpace(Relation rel,
3141+ Oid newTableSpaceId,
3142+ Oid newRelFileNode)
3143+ {
3144+ Relationpg_class;
3145+ HeapTupletuple;
3146+ Form_pg_class rd_rel;
3147+ Oidreloid = RelationGetRelid(rel);
3148+
3149+ Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
3150+
3151+ /* Get a modifiable copy of the relation's pg_class row. */
3152+ pg_class = table_open(RelationRelationId, RowExclusiveLock);
3153+
3154+ tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
3155+ if (!HeapTupleIsValid(tuple))
3156+ elog(ERROR, "cache lookup failed for relation %u", reloid);
3157+ rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3158+
3159+ /* Update the pg_class row. */
3160+ rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3161+ InvalidOid : newTableSpaceId;
3162+ if (OidIsValid(newRelFileNode))
3163+ rd_rel->relfilenode = newRelFileNode;
3164+ CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
3165+
3166+ /*
3167+ * Record dependency on tablespace. This is only required for relations
3168+ * that have no physical storage.
3169+ */
3170+ if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3171+ changeDependencyOnTablespace(RelationRelationId, reloid,
3172+ rd_rel->reltablespace);
3173+
3174+ heap_freetuple(tuple);
3175+ table_close(pg_class, RowExclusiveLock);
3176+ }
3177+
30723178/*
30733179 *renameatt_check- basic sanity checks before attribute rename
30743180 */
@@ -13565,13 +13671,9 @@ static void
1356513671ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
1356613672{
1356713673Relationrel;
13568- OidoldTableSpace;
1356913674Oidreltoastrelid;
1357013675Oidnewrelfilenode;
1357113676RelFileNode newrnode;
13572- Relationpg_class;
13573- HeapTupletuple;
13574- Form_pg_class rd_rel;
1357513677List *reltoastidxids = NIL;
1357613678ListCell *lc;
1357713679
@@ -13580,45 +13682,15 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
1358013682 */
1358113683rel = relation_open(tableOid, lockmode);
1358213684
13583- /*
13584- * No work if no change in tablespace.
13585- */
13586- oldTableSpace = rel->rd_rel->reltablespace;
13587- if (newTableSpace == oldTableSpace ||
13588- (newTableSpace == MyDatabaseTableSpace && oldTableSpace == 0))
13685+ /* Check first if relation can be moved to new tablespace */
13686+ if (!CheckRelationTableSpaceMove(rel, newTableSpace))
1358913687{
1359013688InvokeObjectPostAlterHook(RelationRelationId,
1359113689 RelationGetRelid(rel), 0);
13592-
1359313690relation_close(rel, NoLock);
1359413691return;
1359513692}
1359613693
13597- /*
13598- * We cannot support moving mapped relations into different tablespaces.
13599- * (In particular this eliminates all shared catalogs.)
13600- */
13601- if (RelationIsMapped(rel))
13602- ereport(ERROR,
13603- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13604- errmsg("cannot move system relation \"%s\"",
13605- RelationGetRelationName(rel))));
13606-
13607- /* Can't move a non-shared relation into pg_global */
13608- if (newTableSpace == GLOBALTABLESPACE_OID)
13609- ereport(ERROR,
13610- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
13611- errmsg("only shared relations can be placed in pg_global tablespace")));
13612-
13613- /*
13614- * Don't allow moving temp tables of other backends ... their local buffer
13615- * manager is not going to cope.
13616- */
13617- if (RELATION_IS_OTHER_TEMP(rel))
13618- ereport(ERROR,
13619- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13620- errmsg("cannot move temporary tables of other sessions")));
13621-
1362213694reltoastrelid = rel->rd_rel->reltoastrelid;
1362313695/* Fetch the list of indexes on toast relation if necessary */
1362413696if (OidIsValid(reltoastrelid))
@@ -13629,14 +13701,6 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
1362913701relation_close(toastRel, lockmode);
1363013702}
1363113703
13632- /* Get a modifiable copy of the relation's pg_class row */
13633- pg_class = table_open(RelationRelationId, RowExclusiveLock);
13634-
13635- tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(tableOid));
13636- if (!HeapTupleIsValid(tuple))
13637- elog(ERROR, "cache lookup failed for relation %u", tableOid);
13638- rd_rel = (Form_pg_class) GETSTRUCT(tuple);
13639-
1364013704/*
1364113705 * Relfilenodes are not unique in databases across tablespaces, so we need
1364213706 * to allocate a new one in the new tablespace.
@@ -13667,18 +13731,13 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
1366713731 *
1366813732 * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
1366913733 * executed on pg_class or its indexes (the above copy wouldn't contain
13670- * the updated pg_class entry), but that's forbidden above.
13734+ * the updated pg_class entry), but that's forbidden with
13735+ * CheckRelationTableSpaceMove().
1367113736 */
13672- rd_rel->reltablespace = (newTableSpace == MyDatabaseTableSpace) ? InvalidOid : newTableSpace;
13673- rd_rel->relfilenode = newrelfilenode;
13674- CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
13737+ SetRelationTableSpace(rel, newTableSpace, newrelfilenode);
1367513738
1367613739InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
1367713740
13678- heap_freetuple(tuple);
13679-
13680- table_close(pg_class, RowExclusiveLock);
13681-
1368213741RelationAssumeNewRelfilenode(rel);
1368313742
1368413743relation_close(rel, NoLock);
@@ -13706,56 +13765,25 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
1370613765static void
1370713766ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
1370813767{
13709- HeapTupletuple;
13710- OidoldTableSpace;
13711- Relationpg_class;
13712- Form_pg_class rd_rel;
13713- Oidreloid = RelationGetRelid(rel);
13714-
1371513768/*
1371613769 * Shouldn't be called on relations having storage; these are processed in
1371713770 * phase 3.
1371813771 */
1371913772Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
1372013773
13721- /* Can't allow a non-shared relation in pg_global */
13722- if (newTableSpace == GLOBALTABLESPACE_OID)
13723- ereport(ERROR,
13724- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
13725- errmsg("only shared relations can be placed in pg_global tablespace")));
13726-
13727- /*
13728- * No work if no change in tablespace.
13729- */
13730- oldTableSpace = rel->rd_rel->reltablespace;
13731- if (newTableSpace == oldTableSpace ||
13732- (newTableSpace == MyDatabaseTableSpace && oldTableSpace == 0))
13774+ /* check if relation can be moved to its new tablespace */
13775+ if (!CheckRelationTableSpaceMove(rel, newTableSpace))
1373313776{
13734- InvokeObjectPostAlterHook(RelationRelationId, reloid, 0);
13777+ InvokeObjectPostAlterHook(RelationRelationId,
13778+ RelationGetRelid(rel),
13779+ 0);
1373513780return;
1373613781}
1373713782
13738- /* Get a modifiable copy of the relation's pg_class row */
13739- pg_class = table_open(RelationRelationId, RowExclusiveLock);
13740-
13741- tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
13742- if (!HeapTupleIsValid(tuple))
13743- elog(ERROR, "cache lookup failed for relation %u", reloid);
13744- rd_rel = (Form_pg_class) GETSTRUCT(tuple);
13745-
13746- /* update the pg_class row */
13747- rd_rel->reltablespace = (newTableSpace == MyDatabaseTableSpace) ? InvalidOid : newTableSpace;
13748- CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
13749-
13750- /* Record dependency on tablespace */
13751- changeDependencyOnTablespace(RelationRelationId,
13752- reloid, rd_rel->reltablespace);
13753-
13754- InvokeObjectPostAlterHook(RelationRelationId, reloid, 0);
13783+ /* Update can be done, so change reltablespace */
13784+ SetRelationTableSpace(rel, newTableSpace, InvalidOid);
1375513785
13756- heap_freetuple(tuple);
13757-
13758- table_close(pg_class, RowExclusiveLock);
13786+ InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
1375913787
1376013788/* Make sure the reltablespace change is visible */
1376113789CommandCounterIncrement();