@@ -3037,6 +3037,113 @@ SetRelationHasSubclass(Oid relationId, bool relhassubclass)
3037
3037
table_close(relationRelation, RowExclusiveLock);
3038
3038
}
3039
3039
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
+
3040
3147
/*
3041
3148
* renameatt_check - basic sanity checks before attribute rename
3042
3149
*/
@@ -13160,13 +13267,9 @@ static void
13160
13267
ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
13161
13268
{
13162
13269
Relation rel;
13163
- Oid oldTableSpace;
13164
13270
Oid reltoastrelid;
13165
13271
Oid newrelfilenode;
13166
13272
RelFileNode newrnode;
13167
- Relation pg_class;
13168
- HeapTuple tuple;
13169
- Form_pg_class rd_rel;
13170
13273
List *reltoastidxids = NIL;
13171
13274
ListCell *lc;
13172
13275
@@ -13175,45 +13278,15 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
13175
13278
*/
13176
13279
rel = relation_open(tableOid, lockmode);
13177
13280
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))
13184
13283
{
13185
13284
InvokeObjectPostAlterHook(RelationRelationId,
13186
13285
RelationGetRelid(rel), 0);
13187
-
13188
13286
relation_close(rel, NoLock);
13189
13287
return;
13190
13288
}
13191
13289
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
-
13217
13290
reltoastrelid = rel->rd_rel->reltoastrelid;
13218
13291
/* Fetch the list of indexes on toast relation if necessary */
13219
13292
if (OidIsValid(reltoastrelid))
@@ -13224,14 +13297,6 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
13224
13297
relation_close(toastRel, lockmode);
13225
13298
}
13226
13299
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
-
13235
13300
/*
13236
13301
* Relfilenodes are not unique in databases across tablespaces, so we need
13237
13302
* to allocate a new one in the new tablespace.
@@ -13262,18 +13327,13 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
13262
13327
*
13263
13328
* NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
13264
13329
* 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().
13266
13332
*/
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);
13270
13334
13271
13335
InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
13272
13336
13273
- heap_freetuple(tuple);
13274
-
13275
- table_close(pg_class, RowExclusiveLock);
13276
-
13277
13337
RelationAssumeNewRelfilenode(rel);
13278
13338
13279
13339
relation_close(rel, NoLock);
@@ -13301,56 +13361,25 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
13301
13361
static void
13302
13362
ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
13303
13363
{
13304
- HeapTuple tuple;
13305
- Oid oldTableSpace;
13306
- Relation pg_class;
13307
- Form_pg_class rd_rel;
13308
- Oid reloid = RelationGetRelid(rel);
13309
-
13310
13364
/*
13311
13365
* Shouldn't be called on relations having storage; these are processed in
13312
13366
* phase 3.
13313
13367
*/
13314
13368
Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
13315
13369
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))
13328
13372
{
13329
- InvokeObjectPostAlterHook(RelationRelationId, reloid, 0);
13373
+ InvokeObjectPostAlterHook(RelationRelationId,
13374
+ RelationGetRelid(rel),
13375
+ 0);
13330
13376
return;
13331
13377
}
13332
13378
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);
13350
13381
13351
- heap_freetuple(tuple);
13352
-
13353
- table_close(pg_class, RowExclusiveLock);
13382
+ InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
13354
13383
13355
13384
/* Make sure the reltablespace change is visible */
13356
13385
CommandCounterIncrement();
0 commit comments