aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/commands/tablecmds.c211
-rw-r--r--src/include/commands/tablecmds.h4
2 files changed, 124 insertions, 91 deletions
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 8687e9a97c5..36747e7277d 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -3038,6 +3038,113 @@ SetRelationHasSubclass(Oid relationId, bool relhassubclass)
}
/*
+ * CheckRelationTableSpaceMove
+ * Check if relation can be moved to new tablespace.
+ *
+ * NOTE: Caller must be holding an appropriate lock on the relation.
+ * ShareUpdateExclusiveLock is sufficient.
+ *
+ * Returns true if the relation can be moved to the new tablespace;
+ * false otherwise.
+ */
+bool
+CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
+{
+ Oid oldTableSpaceId;
+
+ /*
+ * No work if no change in tablespace. Note that MyDatabaseTableSpace is
+ * stored as 0.
+ */
+ oldTableSpaceId = rel->rd_rel->reltablespace;
+ if (newTableSpaceId == oldTableSpaceId ||
+ (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
+ return false;
+
+ /*
+ * We cannot support moving mapped relations into different tablespaces.
+ * (In particular this eliminates all shared catalogs.)
+ */
+ if (RelationIsMapped(rel))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot move system relation \"%s\"",
+ RelationGetRelationName(rel))));
+
+ /* Cannot move a non-shared relation into pg_global */
+ if (newTableSpaceId == GLOBALTABLESPACE_OID)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("only shared relations can be placed in pg_global tablespace")));
+
+ /*
+ * Do not allow moving temp tables of other backends ... their local
+ * buffer manager is not going to cope.
+ */
+ if (RELATION_IS_OTHER_TEMP(rel))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot move temporary tables of other sessions")));
+
+ return true;
+}
+
+/*
+ * SetRelationTableSpace
+ * Set new reltablespace and relfilenode in pg_class entry.
+ *
+ * newTableSpaceId is the new tablespace for the relation, and
+ * newRelFileNode its new filenode. If newrelfilenode is InvalidOid,
+ * this field is not updated.
+ *
+ * NOTE: Caller must be holding an appropriate lock on the relation.
+ * ShareUpdateExclusiveLock is sufficient.
+ *
+ * The caller of this routine had better check if a relation can be
+ * moved to this new tablespace by calling CheckRelationTableSpaceMove()
+ * first, and is responsible for making the change visible with
+ * CommandCounterIncrement().
+ */
+void
+SetRelationTableSpace(Relation rel,
+ Oid newTableSpaceId,
+ Oid newRelFileNode)
+{
+ Relation pg_class;
+ HeapTuple tuple;
+ Form_pg_class rd_rel;
+ Oid reloid = RelationGetRelid(rel);
+
+ Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
+
+ /* Get a modifiable copy of the relation's pg_class row. */
+ pg_class = table_open(RelationRelationId, RowExclusiveLock);
+
+ tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for relation %u", reloid);
+ rd_rel = (Form_pg_class) GETSTRUCT(tuple);
+
+ /* Update the pg_class row. */
+ rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
+ InvalidOid : newTableSpaceId;
+ if (OidIsValid(newRelFileNode))
+ rd_rel->relfilenode = newRelFileNode;
+ CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
+
+ /*
+ * Record dependency on tablespace. This is only required for relations
+ * that have no physical storage.
+ */
+ if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
+ changeDependencyOnTablespace(RelationRelationId, reloid,
+ rd_rel->reltablespace);
+
+ heap_freetuple(tuple);
+ table_close(pg_class, RowExclusiveLock);
+}
+
+/*
* renameatt_check - basic sanity checks before attribute rename
*/
static void
@@ -13160,13 +13267,9 @@ static void
ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
{
Relation rel;
- Oid oldTableSpace;
Oid reltoastrelid;
Oid newrelfilenode;
RelFileNode newrnode;
- Relation pg_class;
- HeapTuple tuple;
- Form_pg_class rd_rel;
List *reltoastidxids = NIL;
ListCell *lc;
@@ -13175,45 +13278,15 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
*/
rel = relation_open(tableOid, lockmode);
- /*
- * No work if no change in tablespace.
- */
- oldTableSpace = rel->rd_rel->reltablespace;
- if (newTableSpace == oldTableSpace ||
- (newTableSpace == MyDatabaseTableSpace && oldTableSpace == 0))
+ /* Check first if relation can be moved to new tablespace */
+ if (!CheckRelationTableSpaceMove(rel, newTableSpace))
{
InvokeObjectPostAlterHook(RelationRelationId,
RelationGetRelid(rel), 0);
-
relation_close(rel, NoLock);
return;
}
- /*
- * We cannot support moving mapped relations into different tablespaces.
- * (In particular this eliminates all shared catalogs.)
- */
- if (RelationIsMapped(rel))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot move system relation \"%s\"",
- RelationGetRelationName(rel))));
-
- /* Can't move a non-shared relation into pg_global */
- if (newTableSpace == GLOBALTABLESPACE_OID)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("only shared relations can be placed in pg_global tablespace")));
-
- /*
- * Don't allow moving temp tables of other backends ... their local buffer
- * manager is not going to cope.
- */
- if (RELATION_IS_OTHER_TEMP(rel))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot move temporary tables of other sessions")));
-
reltoastrelid = rel->rd_rel->reltoastrelid;
/* Fetch the list of indexes on toast relation if necessary */
if (OidIsValid(reltoastrelid))
@@ -13224,14 +13297,6 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
relation_close(toastRel, lockmode);
}
- /* Get a modifiable copy of the relation's pg_class row */
- pg_class = table_open(RelationRelationId, RowExclusiveLock);
-
- tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(tableOid));
- if (!HeapTupleIsValid(tuple))
- elog(ERROR, "cache lookup failed for relation %u", tableOid);
- rd_rel = (Form_pg_class) GETSTRUCT(tuple);
-
/*
* Relfilenodes are not unique in databases across tablespaces, so we need
* to allocate a new one in the new tablespace.
@@ -13262,18 +13327,13 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
*
* NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
* executed on pg_class or its indexes (the above copy wouldn't contain
- * the updated pg_class entry), but that's forbidden above.
+ * the updated pg_class entry), but that's forbidden with
+ * CheckRelationTableSpaceMove().
*/
- rd_rel->reltablespace = (newTableSpace == MyDatabaseTableSpace) ? InvalidOid : newTableSpace;
- rd_rel->relfilenode = newrelfilenode;
- CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
+ SetRelationTableSpace(rel, newTableSpace, newrelfilenode);
InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
- heap_freetuple(tuple);
-
- table_close(pg_class, RowExclusiveLock);
-
RelationAssumeNewRelfilenode(rel);
relation_close(rel, NoLock);
@@ -13301,56 +13361,25 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
static void
ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
{
- HeapTuple tuple;
- Oid oldTableSpace;
- Relation pg_class;
- Form_pg_class rd_rel;
- Oid reloid = RelationGetRelid(rel);
-
/*
* Shouldn't be called on relations having storage; these are processed in
* phase 3.
*/
Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
- /* Can't allow a non-shared relation in pg_global */
- if (newTableSpace == GLOBALTABLESPACE_OID)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("only shared relations can be placed in pg_global tablespace")));
-
- /*
- * No work if no change in tablespace.
- */
- oldTableSpace = rel->rd_rel->reltablespace;
- if (newTableSpace == oldTableSpace ||
- (newTableSpace == MyDatabaseTableSpace && oldTableSpace == 0))
+ /* check if relation can be moved to its new tablespace */
+ if (!CheckRelationTableSpaceMove(rel, newTableSpace))
{
- InvokeObjectPostAlterHook(RelationRelationId, reloid, 0);
+ InvokeObjectPostAlterHook(RelationRelationId,
+ RelationGetRelid(rel),
+ 0);
return;
}
- /* Get a modifiable copy of the relation's pg_class row */
- pg_class = table_open(RelationRelationId, RowExclusiveLock);
-
- tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
- if (!HeapTupleIsValid(tuple))
- elog(ERROR, "cache lookup failed for relation %u", reloid);
- rd_rel = (Form_pg_class) GETSTRUCT(tuple);
-
- /* update the pg_class row */
- rd_rel->reltablespace = (newTableSpace == MyDatabaseTableSpace) ? InvalidOid : newTableSpace;
- CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
-
- /* Record dependency on tablespace */
- changeDependencyOnTablespace(RelationRelationId,
- reloid, rd_rel->reltablespace);
-
- InvokeObjectPostAlterHook(RelationRelationId, reloid, 0);
+ /* Update can be done, so change reltablespace */
+ SetRelationTableSpace(rel, newTableSpace, InvalidOid);
- heap_freetuple(tuple);
-
- table_close(pg_class, RowExclusiveLock);
+ InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
/* Make sure the reltablespace change is visible */
CommandCounterIncrement();
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 08c463d3c43..b3d30acc359 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -61,6 +61,10 @@ extern void ExecuteTruncateGuts(List *explicit_rels, List *relids, List *relids_
extern void SetRelationHasSubclass(Oid relationId, bool relhassubclass);
+extern bool CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId);
+extern void SetRelationTableSpace(Relation rel, Oid newTableSpaceId,
+ Oid newRelFileNode);
+
extern ObjectAddress renameatt(RenameStmt *stmt);
extern ObjectAddress RenameConstraint(RenameStmt *stmt);