@@ -3069,6 +3069,112 @@ SetRelationHasSubclass(Oid relationId, bool relhassubclass)
3069
3069
table_close(relationRelation, RowExclusiveLock);
3070
3070
}
3071
3071
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
+
3072
3178
/*
3073
3179
*renameatt_check- basic sanity checks before attribute rename
3074
3180
*/
@@ -13565,13 +13671,9 @@ static void
13565
13671
ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
13566
13672
{
13567
13673
Relationrel;
13568
- OidoldTableSpace;
13569
13674
Oidreltoastrelid;
13570
13675
Oidnewrelfilenode;
13571
13676
RelFileNode newrnode;
13572
- Relationpg_class;
13573
- HeapTupletuple;
13574
- Form_pg_class rd_rel;
13575
13677
List *reltoastidxids = NIL;
13576
13678
ListCell *lc;
13577
13679
@@ -13580,45 +13682,15 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
13580
13682
*/
13581
13683
rel = relation_open(tableOid, lockmode);
13582
13684
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))
13589
13687
{
13590
13688
InvokeObjectPostAlterHook(RelationRelationId,
13591
13689
RelationGetRelid(rel), 0);
13592
-
13593
13690
relation_close(rel, NoLock);
13594
13691
return;
13595
13692
}
13596
13693
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
-
13622
13694
reltoastrelid = rel->rd_rel->reltoastrelid;
13623
13695
/* Fetch the list of indexes on toast relation if necessary */
13624
13696
if (OidIsValid(reltoastrelid))
@@ -13629,14 +13701,6 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
13629
13701
relation_close(toastRel, lockmode);
13630
13702
}
13631
13703
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
-
13640
13704
/*
13641
13705
* Relfilenodes are not unique in databases across tablespaces, so we need
13642
13706
* to allocate a new one in the new tablespace.
@@ -13667,18 +13731,13 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
13667
13731
*
13668
13732
* NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
13669
13733
* 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().
13671
13736
*/
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);
13675
13738
13676
13739
InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
13677
13740
13678
- heap_freetuple(tuple);
13679
-
13680
- table_close(pg_class, RowExclusiveLock);
13681
-
13682
13741
RelationAssumeNewRelfilenode(rel);
13683
13742
13684
13743
relation_close(rel, NoLock);
@@ -13706,56 +13765,25 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
13706
13765
static void
13707
13766
ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
13708
13767
{
13709
- HeapTupletuple;
13710
- OidoldTableSpace;
13711
- Relationpg_class;
13712
- Form_pg_class rd_rel;
13713
- Oidreloid = RelationGetRelid(rel);
13714
-
13715
13768
/*
13716
13769
* Shouldn't be called on relations having storage; these are processed in
13717
13770
* phase 3.
13718
13771
*/
13719
13772
Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
13720
13773
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))
13733
13776
{
13734
- InvokeObjectPostAlterHook(RelationRelationId, reloid, 0);
13777
+ InvokeObjectPostAlterHook(RelationRelationId,
13778
+ RelationGetRelid(rel),
13779
+ 0);
13735
13780
return;
13736
13781
}
13737
13782
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);
13755
13785
13756
- heap_freetuple(tuple);
13757
-
13758
- table_close(pg_class, RowExclusiveLock);
13786
+ InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
13759
13787
13760
13788
/* Make sure the reltablespace change is visible */
13761
13789
CommandCounterIncrement();