diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml index 6f1917f..aca3749 100644 --- a/doc/src/sgml/ref/alter_table.sgml +++ b/doc/src/sgml/ref/alter_table.sgml @@ -66,6 +66,8 @@ ALTER TABLE name NOT OF OWNER TO new_owner SET TABLESPACE new_tablespace + SET TABLE TABLESPACE new_tablespace + SET TOAST TABLESPACE new_tablespace and table_constraint_using_index is: @@ -555,6 +557,30 @@ ALTER TABLE name + SET TABLE TABLESPACE + + + This form changes only table's tablespace (not associated TOAST table's tablespace) + to the specified tablespace and moves the data file(s) associated to the new tablespace. + See also + + + + + + + SET TOAST TABLESPACE + + + This form changes the TOAST table's tablespace to the specified tablespace and + moves the data file(s) associated with the TOAST table to the new tablespace. + See also + + + + + + RENAME diff --git a/doc/src/sgml/storage.sgml b/doc/src/sgml/storage.sgml index cb2f60e..e307422 100644 --- a/doc/src/sgml/storage.sgml +++ b/doc/src/sgml/storage.sgml @@ -427,6 +427,11 @@ pages). There was no run time difference compared to an un-TOASTed comparison table, in which all the HTML pages were cut down to 7 kB to fit. + +TOAST table can be moved to a different tablespace with +ALTER TABLE SET TOAST TABLESPACE + + diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 707ba7e..f01d689 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -36,7 +36,7 @@ extern Oid binary_upgrade_next_toast_pg_class_oid; Oid binary_upgrade_next_toast_pg_type_oid = InvalidOid; static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, - Datum reloptions); + Datum reloptions, Oid toastTableSpace, bool new_toast); static bool needs_toast_table(Relation rel); @@ -53,10 +53,10 @@ static bool needs_toast_table(Relation rel); * to end with CommandCounterIncrement if it makes any changes. */ void -AlterTableCreateToastTable(Oid relOid, Datum reloptions) +AlterTableCreateToastTable(Oid relOid, Datum reloptions, Oid toastTableSpace, bool new_toast) { Relation rel; - + /* * Grab an exclusive lock on the target table, since we'll update its * pg_class tuple. This is redundant for all present uses, since caller @@ -65,9 +65,9 @@ AlterTableCreateToastTable(Oid relOid, Datum reloptions) * so let's be safe. */ rel = heap_open(relOid, AccessExclusiveLock); - + /* create_toast_table does all the work */ - (void) create_toast_table(rel, InvalidOid, InvalidOid, reloptions); + (void) create_toast_table(rel, InvalidOid, InvalidOid, reloptions, toastTableSpace, new_toast); heap_close(rel, NoLock); } @@ -93,7 +93,7 @@ BootstrapToastTable(char *relName, Oid toastOid, Oid toastIndexOid) relName))); /* create_toast_table does all the work */ - if (!create_toast_table(rel, toastOid, toastIndexOid, (Datum) 0)) + if (!create_toast_table(rel, toastOid, toastIndexOid, (Datum) 0, InvalidOid, true)) elog(ERROR, "\"%s\" does not require a toast table", relName); @@ -109,7 +109,7 @@ BootstrapToastTable(char *relName, Oid toastOid, Oid toastIndexOid) * bootstrap they can be nonzero to specify hand-assigned OIDs */ static bool -create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptions) +create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptions, Oid toastTableSpace, bool new_toast) { Oid relOid = RelationGetRelid(rel); HeapTuple reltup; @@ -209,10 +209,17 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio toast_typid = binary_upgrade_next_toast_pg_type_oid; binary_upgrade_next_toast_pg_type_oid = InvalidOid; } + + /* + * Use table's tablespace if toastTableSpace is invalid + * and if this is not a TOAST re-creation case (through CLUSTER) + */ + if (new_toast && !OidIsValid(toastTableSpace)) + toastTableSpace = rel->rd_rel->reltablespace; toast_relid = heap_create_with_catalog(toast_relname, namespaceid, - rel->rd_rel->reltablespace, + toastTableSpace, toastOid, toast_typid, InvalidOid, @@ -278,7 +285,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio indexInfo, list_make2("chunk_id", "chunk_seq"), BTREE_AM_OID, - rel->rd_rel->reltablespace, + toastTableSpace, collationObjectId, classObjectId, coloptions, (Datum) 0, true, false, false, false, true, false, false); diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index 9408f25..5553b8c 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -541,6 +541,9 @@ rebuild_relation(Relation OldHeap, Oid indexOid, bool is_system_catalog; bool swap_toast_by_content; TransactionId frozenXid; + Oid ToastTableSpace; + Relation ToastRel; + /* Mark the correct index as clustered */ if (OidIsValid(indexOid)) @@ -549,11 +552,25 @@ rebuild_relation(Relation OldHeap, Oid indexOid, /* Remember if it's a system catalog */ is_system_catalog = IsSystemRelation(OldHeap); + /* + * Verifiy if a TOASTed relation exists and is a valid relation + * If true, keep its previous tablespace in memory to rebuild it in + * the same tablespace + */ + if (OidIsValid(OldHeap->rd_rel->reltoastrelid)) + { + ToastRel = relation_open(OldHeap->rd_rel->reltoastrelid, NoLock); + ToastTableSpace = ToastRel->rd_rel->reltablespace; + relation_close(ToastRel, NoLock); + } + else + ToastTableSpace = (is_system_catalog) ? tableSpace : InvalidOid; + /* Close relcache entry, but keep lock until transaction commit */ heap_close(OldHeap, NoLock); /* Create the transient table that will receive the re-ordered data */ - OIDNewHeap = make_new_heap(tableOid, tableSpace); + OIDNewHeap = make_new_heap(tableOid, tableSpace, ToastTableSpace); /* Copy the heap data into the new table in the desired order */ copy_heap_data(OIDNewHeap, tableOid, indexOid, @@ -579,7 +596,7 @@ rebuild_relation(Relation OldHeap, Oid indexOid, * data, then call finish_heap_swap to complete the operation. */ Oid -make_new_heap(Oid OIDOldHeap, Oid NewTableSpace) +make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewToastTableSpace) { TupleDesc OldHeapDesc; char NewHeapName[NAMEDATALEN]; @@ -676,8 +693,8 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace) &isNull); if (isNull) reloptions = (Datum) 0; - - AlterTableCreateToastTable(OIDNewHeap, reloptions); + + AlterTableCreateToastTable(OIDNewHeap, reloptions, NewToastTableSpace, false); ReleaseSysCache(tuple); } diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cc210f0..1c9e525 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -147,7 +147,8 @@ typedef struct AlteredTableInfo List *newvals; /* List of NewColumnValue */ bool new_notnull; /* T if we added new NOT NULL constraints */ bool rewrite; /* T if a rewrite is forced */ - Oid newTableSpace; /* new tablespace; 0 means no change */ + Oid newTableSpace; /* new tablespace; 0 means no change */ + Oid newToastTableSpace; /* new TOAST tablespace; 0 means no change */ /* Objects to rebuild after completing ALTER TYPE operations */ List *changedConstraintOids; /* OIDs of constraints to rebuild */ List *changedConstraintDefs; /* string definitions of same */ @@ -364,8 +365,11 @@ static void change_owner_recurse_to_sequences(Oid relationOid, static void ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode); static void ATExecDropCluster(Relation rel, LOCKMODE lockmode); static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, + char *tablespacename, LOCKMODE lockmode, bool table_only); +static void ATPrepSetToastTableSpace(AlteredTableInfo *tab, Relation rel, char *tablespacename, LOCKMODE lockmode); -static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode); +static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode, Oid newToastTableSpace); +static void ATExecSetToastTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode); static void ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation, LOCKMODE lockmode); @@ -2971,7 +2975,19 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, case AT_SetTableSpace: /* SET TABLESPACE */ ATSimplePermissions(rel, ATT_TABLE | ATT_INDEX); /* This command never recurses */ - ATPrepSetTableSpace(tab, rel, cmd->name, lockmode); + ATPrepSetTableSpace(tab, rel, cmd->name, lockmode, false); + pass = AT_PASS_MISC; /* doesn't actually matter */ + break; + case AT_SetTableTableSpace: /* SET TABLE TABLESPACE */ + ATSimplePermissions(rel, ATT_TABLE | ATT_INDEX); + /* This command never recurses */ + ATPrepSetTableSpace(tab, rel, cmd->name, lockmode, true); + pass = AT_PASS_MISC; /* doesn't actually matter */ + break; + case AT_SetToastTableSpace: /* SET TOAST TABLESPACE */ + ATSimplePermissions(rel, ATT_TABLE); + /* This command never recurses */ + ATPrepSetToastTableSpace(tab, rel, cmd->name, lockmode); pass = AT_PASS_MISC; /* doesn't actually matter */ break; case AT_SetRelOptions: /* SET (...) */ @@ -3089,9 +3105,32 @@ ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode) foreach(ltab, *wqueue) { AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab); + + Relation toast_rel; + Oid toast_tablespace_oid; + Relation rel; + bool new_toast; if (tab->relkind == RELKIND_RELATION) - AlterTableCreateToastTable(tab->relid, (Datum) 0); + { + rel = relation_open(tab->relid, NoLock); + if (OidIsValid(rel->rd_rel->reltoastrelid)) + { + + toast_rel = relation_open(rel->rd_rel->reltoastrelid, NoLock); + toast_tablespace_oid = toast_rel->rd_rel->reltablespace; + relation_close(toast_rel, NoLock); + new_toast = false; + } + else + { + toast_tablespace_oid = InvalidOid; + new_toast = true; + } + relation_close(rel, NoLock); + AlterTableCreateToastTable(tab->relid, (Datum) 0, toast_tablespace_oid, new_toast); + + } } } @@ -3219,6 +3258,18 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, * Nothing to do here; Phase 3 does the work */ break; + case AT_SetTableTableSpace: /* SET TABLE TABLESPACE */ + + /* + * Nothing to do here; Phase 3 does the work + */ + break; + case AT_SetToastTableSpace: /* SET TOAST TABLESPACE */ + + /* + * Nothing to do here; Phase 3 does the work + */ + break; case AT_SetRelOptions: /* SET (...) */ case AT_ResetRelOptions: /* RESET (...) */ case AT_ReplaceRelOptions: /* replace entire option list */ @@ -3350,8 +3401,10 @@ ATRewriteTables(List **wqueue, LOCKMODE lockmode) { /* Build a temporary relation and copy data */ Relation OldHeap; + Relation OldToastRel; Oid OIDNewHeap; Oid NewTableSpace; + Oid NewToastTableSpace; OldHeap = heap_open(tab->relid, NoLock); @@ -3383,11 +3436,24 @@ ATRewriteTables(List **wqueue, LOCKMODE lockmode) NewTableSpace = tab->newTableSpace; else NewTableSpace = OldHeap->rd_rel->reltablespace; - + + if (tab->newToastTableSpace) + NewToastTableSpace = tab->newToastTableSpace; + else + { + if (OidIsValid(OldHeap->rd_rel->reltoastrelid)) + { + OldToastRel = relation_open(OldHeap->rd_rel->reltoastrelid, NoLock); + NewToastTableSpace = OldToastRel->rd_rel->reltablespace; + relation_close(OldToastRel, NoLock); + } + else + NewToastTableSpace = InvalidOid; + } heap_close(OldHeap, NoLock); /* Create transient table that will receive the modified data */ - OIDNewHeap = make_new_heap(tab->relid, NewTableSpace); + OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewToastTableSpace); /* * Copy the heap data into the new table with the desired @@ -3421,7 +3487,9 @@ ATRewriteTables(List **wqueue, LOCKMODE lockmode) * just do a block-by-block copy. */ if (tab->newTableSpace) - ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode); + ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode, tab->newToastTableSpace); + if (tab->newToastTableSpace) + ATExecSetToastTableSpace(tab->relid, tab->newToastTableSpace, lockmode); } } @@ -8126,30 +8194,65 @@ ATExecDropCluster(Relation rel, LOCKMODE lockmode) } /* - * ALTER TABLE SET TABLESPACE + * Check tablespace's permissions & no multiple SET TABLESPACE subcommands */ -static void -ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, char *tablespacename, LOCKMODE lockmode) +extern void CheckTableSpaceAlterTable(char *TableSpaceName, Oid TableSpaceOid, Oid NewTableSpaceOid) { - Oid tablespaceId; AclResult aclresult; - - /* Check that the tablespace exists */ - tablespaceId = get_tablespace_oid(tablespacename, false); - /* Check its permissions */ - aclresult = pg_tablespace_aclcheck(tablespaceId, GetUserId(), ACL_CREATE); + aclresult = pg_tablespace_aclcheck(TableSpaceOid, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_TABLESPACE, tablespacename); + aclcheck_error(aclresult, ACL_KIND_TABLESPACE, TableSpaceName); - /* Save info for Phase 3 to do the real work */ - if (OidIsValid(tab->newTableSpace)) + if (OidIsValid(NewTableSpaceOid)) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("cannot have multiple SET TABLESPACE subcommands"))); +} + +/* + * ALTER TABLE SET [TABLE] TABLESPACE + */ +static void +ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, char *tablespacename, LOCKMODE lockmode, bool table_only) +{ + Oid tablespaceId; + + /* Check that the tablespace exists */ + tablespaceId = get_tablespace_oid(tablespacename, false); + + /* Check it */ + CheckTableSpaceAlterTable(tablespacename, tablespaceId, tab->newTableSpace); + + /* Save tablespace Oid */ tab->newTableSpace = tablespaceId; + + /* The case when we want to move only table location not its TOAST table */ + if (table_only) + tab->newToastTableSpace = InvalidOid; + else + tab->newToastTableSpace = tablespaceId; + +} + +/* + * ALTER TABLE SET TOAST TABLESPACE + */ +static void +ATPrepSetToastTableSpace(AlteredTableInfo *tab, Relation rel, char *tablespacename, LOCKMODE lockmode) +{ + Oid tablespaceId; + + /* Check that the tablespace exists */ + tablespaceId = get_tablespace_oid(tablespacename, false); + /* Check it */ + CheckTableSpaceAlterTable(tablespacename, tablespaceId, tab->newToastTableSpace); + + /* Save TOAST tablespace Oid */ + tab->newToastTableSpace = tablespaceId; } + /* * Set, reset, or replace reloptions. */ @@ -8309,12 +8412,42 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation, heap_close(pgclass, RowExclusiveLock); } + +extern void +RelationIsMoveableToNewTablespace(Relation rel, Oid NewTableSpaceOid) +{ + /* + * 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 (NewTableSpaceOid == 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"))); +} + /* - * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple + * Execute ALTER TABLE SET [TABLE] TABLESPACE for cases where there is no tuple * rewriting to be done, so we just want to copy the data as fast as possible. */ static void -ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode) +ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode, Oid newToastTableSpace) { Relation rel; Oid oldTableSpace; @@ -8343,31 +8476,8 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode) 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"))); + + RelationIsMoveableToNewTablespace(rel, newTableSpace); reltoastrelid = rel->rd_rel->reltoastrelid; reltoastidxid = rel->rd_rel->reltoastidxid; @@ -8433,6 +8543,7 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode) /* update the pg_class row */ rd_rel->reltablespace = (newTableSpace == MyDatabaseTableSpace) ? InvalidOid : newTableSpace; + rd_rel->relfilenode = newrelfilenode; simple_heap_update(pg_class, &tuple->t_self, tuple); CatalogUpdateIndexes(pg_class, tuple); @@ -8447,12 +8558,69 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode) CommandCounterIncrement(); /* Move associated toast relation and/or index, too */ - if (OidIsValid(reltoastrelid)) - ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode); + if (OidIsValid(newToastTableSpace)) + { + if (OidIsValid(reltoastrelid)) + ATExecSetTableSpace(reltoastrelid, newToastTableSpace, lockmode, newToastTableSpace); + if (OidIsValid(reltoastidxid)) + ATExecSetTableSpace(reltoastidxid, newToastTableSpace, lockmode, newToastTableSpace); + } +} + + +/* + * Execute ALTER TABLE SET TOAST TABLESPACE + */ +static void +ATExecSetToastTableSpace(Oid tableOid, Oid newToastTableSpace, LOCKMODE lockmode) +{ + Relation rel; + Oid oldToastTableSpace; + Oid reltoastrelid; + Oid reltoastidxid; + Relation relToast; + /* + * Need lock here in case we are recursing to toast table or index + */ + rel = relation_open(tableOid, lockmode); + + /* + * Need to know old TOAST tablespace + */ + if (OidIsValid(rel->rd_rel->reltoastrelid)) + { + relToast = relation_open(rel->rd_rel->reltoastrelid, NoLock); + + oldToastTableSpace = relToast->rd_rel->reltablespace; + if (newToastTableSpace == oldToastTableSpace || + (newToastTableSpace == MyDatabaseTableSpace && oldToastTableSpace == 0)) + { + relation_close(rel, NoLock); + relation_close(relToast, NoLock); + return; + } + } + else + { + relation_close(rel, NoLock); + return; + } + + reltoastrelid = rel->rd_rel->reltoastrelid; + reltoastidxid = rel->rd_rel->reltoastidxid; + + RelationIsMoveableToNewTablespace(rel, newToastTableSpace); + + relation_close(rel, NoLock); + relation_close(relToast, NoLock); + + ATExecSetTableSpace(reltoastrelid, newToastTableSpace, lockmode, newToastTableSpace); if (OidIsValid(reltoastidxid)) - ATExecSetTableSpace(reltoastidxid, newTableSpace, lockmode); + ATExecSetTableSpace(reltoastidxid, newToastTableSpace, lockmode, newToastTableSpace); + } + /* * Copy data, block by block */ diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 422f737..59c89cb 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -2629,7 +2629,7 @@ OpenIntoRel(QueryDesc *queryDesc) (void) heap_reloptions(RELKIND_TOASTVALUE, reloptions, true); - AlterTableCreateToastTable(intoRelationId, reloptions); + AlterTableCreateToastTable(intoRelationId, reloptions, InvalidOid, true); /* * And open the constructed table for writing. diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 0ec039b..d8649bb 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -557,7 +557,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType, SYMMETRIC SYSID SYSTEM_P TABLE TABLES TABLESPACE TEMP TEMPLATE TEMPORARY TEXT_P THEN TIME TIMESTAMP - TO TRAILING TRANSACTION TREAT TRIGGER TRIM TRUE_P + TO TOAST TRAILING TRANSACTION TREAT TRIGGER TRIM TRUE_P TRUNCATE TRUSTED TYPE_P TYPES_P UNBOUNDED UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNLOGGED @@ -1993,6 +1993,22 @@ alter_table_cmd: n->name = $3; $$ = (Node *)n; } + /* ALTER TABLE SET TOAST TABLESPACE */ + | SET TOAST TABLESPACE name + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_SetToastTableSpace; + n->name = $4; + $$ = (Node *)n; + } + /* ALTER TABLE SET TABLE TABLESPACE */ + | SET TABLE TABLESPACE name + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_SetTableTableSpace; + n->name = $4; + $$ = (Node *)n; + } /* ALTER TABLE SET (...) */ | SET reloptions { @@ -12104,6 +12120,7 @@ unreserved_keyword: | TEMPLATE | TEMPORARY | TEXT_P + | TOAST | TRANSACTION | TRIGGER | TRUNCATE diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index de16a61..793c36b 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -545,7 +545,7 @@ standard_ProcessUtility(Node *parsetree, (void) heap_reloptions(RELKIND_TOASTVALUE, toast_options, true); - AlterTableCreateToastTable(relOid, toast_options); + AlterTableCreateToastTable(relOid, toast_options, InvalidOid, true); } else if (IsA(stmt, CreateForeignTableStmt)) { diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 13fc667..6e9c096 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -3926,6 +3926,7 @@ getTables(int *numTables) int i_owning_tab; int i_owning_col; int i_reltablespace; + int i_reltoasttablespace; int i_reloptions; int i_toastreloptions; int i_reloftype; @@ -3953,7 +3954,44 @@ getTables(int *numTables) * we cannot correctly identify inherited columns, owned sequences, etc. */ - if (g_fout->remoteVersion >= 90100) + if (g_fout->remoteVersion >= 90200) + { + /* + * Left join to pick up dependency info linking sequences to their + * owning column, if any (note this dependency is AUTO as of 8.2) + */ + appendPQExpBuffer(query, + "SELECT c.tableoid, c.oid, c.relname, " + "c.relacl, c.relkind, c.relnamespace, " + "(%s c.relowner) AS rolname, " + "c.relchecks, c.relhastriggers, " + "c.relhasindex, c.relhasrules, c.relhasoids, " + "c.relfrozenxid, tc.oid AS toid, " + "tc.relfrozenxid AS tfrozenxid, " + "c.relpersistence, " + "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, " + "d.refobjid AS owning_tab, " + "d.refobjsubid AS owning_col, " + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " + "array_to_string(c.reloptions, ', ') AS reloptions, " + "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions, " + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = tc.reltablespace) AS reltoasttablespace " + "FROM pg_class c " + "LEFT JOIN pg_depend d ON " + "(c.relkind = '%c' AND " + "d.classid = c.tableoid AND d.objid = c.oid AND " + "d.objsubid = 0 AND " + "d.refclassid = c.tableoid AND d.deptype = 'a') " + "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid) " + "WHERE c.relkind in ('%c', '%c', '%c', '%c', '%c') " + "ORDER BY c.oid", + username_subquery, + RELKIND_SEQUENCE, + RELKIND_RELATION, RELKIND_SEQUENCE, + RELKIND_VIEW, RELKIND_COMPOSITE_TYPE, + RELKIND_FOREIGN_TABLE); + } + else if (g_fout->remoteVersion >= 90100) { /* * Left join to pick up dependency info linking sequences to their @@ -3973,7 +4011,8 @@ getTables(int *numTables) "d.refobjsubid AS owning_col, " "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " "array_to_string(c.reloptions, ', ') AS reloptions, " - "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions " + "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions, " + "NULL AS reltoasttablespace " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -4009,7 +4048,8 @@ getTables(int *numTables) "d.refobjsubid AS owning_col, " "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " "array_to_string(c.reloptions, ', ') AS reloptions, " - "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions " + "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions, " + "NULL AS reltoasttablespace " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -4044,7 +4084,8 @@ getTables(int *numTables) "d.refobjsubid AS owning_col, " "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " "array_to_string(c.reloptions, ', ') AS reloptions, " - "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions " + "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions, " + "NULL AS reltoasttablespace " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -4079,7 +4120,8 @@ getTables(int *numTables) "d.refobjsubid AS owning_col, " "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " "array_to_string(c.reloptions, ', ') AS reloptions, " - "NULL AS toast_reloptions " + "NULL AS toast_reloptions, " + "NULL AS reltoasttablespace " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -4115,7 +4157,8 @@ getTables(int *numTables) "d.refobjsubid AS owning_col, " "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " "NULL AS reloptions, " - "NULL AS toast_reloptions " + "NULL AS toast_reloptions, " + "NULL AS reltoasttablespace " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -4150,7 +4193,8 @@ getTables(int *numTables) "d.refobjsubid AS owning_col, " "NULL AS reltablespace, " "NULL AS reloptions, " - "NULL AS toast_reloptions " + "NULL AS toast_reloptions, " + "NULL AS reltoasttablespace " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -4181,7 +4225,8 @@ getTables(int *numTables) "NULL::int4 AS owning_col, " "NULL AS reltablespace, " "NULL AS reloptions, " - "NULL AS toast_reloptions " + "NULL AS toast_reloptions, " + "NULL AS reltoasttablespace " "FROM pg_class " "WHERE relkind IN ('%c', '%c', '%c') " "ORDER BY oid", @@ -4207,7 +4252,8 @@ getTables(int *numTables) "NULL::int4 AS owning_col, " "NULL AS reltablespace, " "NULL AS reloptions, " - "NULL AS toast_reloptions " + "NULL AS toast_reloptions, " + "NULL AS reltoasttablespace " "FROM pg_class " "WHERE relkind IN ('%c', '%c', '%c') " "ORDER BY oid", @@ -4243,7 +4289,8 @@ getTables(int *numTables) "NULL::int4 AS owning_col, " "NULL AS reltablespace, " "NULL AS reloptions, " - "NULL AS toast_reloptions " + "NULL AS toast_reloptions, " + "NULL AS reltoasttablespace " "FROM pg_class c " "WHERE relkind IN ('%c', '%c') " "ORDER BY oid", @@ -4292,6 +4339,7 @@ getTables(int *numTables) i_reloptions = PQfnumber(res, "reloptions"); i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); + i_reltoasttablespace = PQfnumber(res, "reltoasttablespace"); if (lockWaitTimeout && g_fout->remoteVersion >= 70300) { @@ -4346,6 +4394,7 @@ getTables(int *numTables) tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace)); tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions)); tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].reltoasttablespace = pg_strdup(PQgetvalue(res, i, i_reltoasttablespace)); /* other fields were zeroed above */ @@ -12879,7 +12928,14 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) } } } - + if (strlen(tbinfo->reltoasttablespace) > 0) + { + appendPQExpBuffer(q, "ALTER TABLE %s ", + fmtId(tbinfo->dobj.name)); + appendPQExpBuffer(q, "SET TOAST TABLESPACE %s;\n", + tbinfo->reltoasttablespace); + } + if (binary_upgrade) binary_upgrade_extension_member(q, &tbinfo->dobj, labelq->data); diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index 11c4d37..7e08343 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -243,6 +243,7 @@ typedef struct _tableInfo char relkind; char relpersistence; /* relation persistence */ char *reltablespace; /* relation tablespace */ + char *reltoasttablespace; /* TOAST relation tablespace */ char *reloptions; /* options specified by WITH (...) */ char *toast_reloptions; /* ditto, for the TOAST table */ bool hasindex; /* does it have any indexes? */ diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 4eee4be..73fb09a 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -29,6 +29,7 @@ static bool describeOneTableDetails(const char *schemaname, bool verbose); static void add_tablespace_footer(printTableContent *const cont, char relkind, Oid tablespace, const bool newline); +static void add_toasttablespace_footer(printTableContent *const cont, Oid toasttablespace); static void add_role_attribute(PQExpBuffer buf, const char *const str); static bool listTSParsersVerbose(const char *pattern); static bool describeOneTSParser(const char *oid, const char *nspname, @@ -1121,6 +1122,7 @@ describeOneTableDetails(const char *schemaname, bool hastriggers; bool hasoids; Oid tablespace; + Oid toasttablespace; char *reloptions; char *reloftype; char relpersistence; @@ -1143,7 +1145,7 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&buf, "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, " "c.relhastriggers, c.relhasoids, " - "%s, c.reltablespace, " + "%s, c.reltablespace, tc.reltablespace, " "CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END, " "c.relpersistence\n" "FROM pg_catalog.pg_class c\n " @@ -1160,7 +1162,7 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&buf, "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, " "c.relhastriggers, c.relhasoids, " - "%s, c.reltablespace, " + "%s, c.reltablespace, tc.reltablespace, " "CASE WHEN c.reloftype = 0 THEN '' ELSE c.reloftype::pg_catalog.regtype::pg_catalog.text END\n" "FROM pg_catalog.pg_class c\n " "LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n" @@ -1176,7 +1178,7 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&buf, "SELECT c.relchecks, c.relkind, c.relhasindex, c.relhasrules, " "c.relhastriggers, c.relhasoids, " - "%s, c.reltablespace\n" + "%s, c.reltablespace, tc.reltablespace\n" "FROM pg_catalog.pg_class c\n " "LEFT JOIN pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)\n" "WHERE c.oid = '%s';", @@ -1191,7 +1193,7 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&buf, "SELECT relchecks, relkind, relhasindex, relhasrules, " "reltriggers <> 0, relhasoids, " - "%s, reltablespace\n" + "%s, reltablespace, ''\n" "FROM pg_catalog.pg_class WHERE oid = '%s';", (verbose ? "pg_catalog.array_to_string(reloptions, E', ')" : "''"), @@ -1202,7 +1204,7 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&buf, "SELECT relchecks, relkind, relhasindex, relhasrules, " "reltriggers <> 0, relhasoids, " - "'', reltablespace\n" + "'', reltablespace, ''\n" "FROM pg_catalog.pg_class WHERE oid = '%s';", oid); } @@ -1211,7 +1213,7 @@ describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&buf, "SELECT relchecks, relkind, relhasindex, relhasrules, " "reltriggers <> 0, relhasoids, " - "'', ''\n" + "'', '', ''\n" "FROM pg_catalog.pg_class WHERE oid = '%s';", oid); } @@ -1239,10 +1241,12 @@ describeOneTableDetails(const char *schemaname, strdup(PQgetvalue(res, 0, 6)) : 0; tableinfo.tablespace = (pset.sversion >= 80000) ? atooid(PQgetvalue(res, 0, 7)) : 0; - tableinfo.reloftype = (pset.sversion >= 90000 && strcmp(PQgetvalue(res, 0, 8), "") != 0) ? - strdup(PQgetvalue(res, 0, 8)) : 0; - tableinfo.relpersistence = (pset.sversion >= 90100 && strcmp(PQgetvalue(res, 0, 9), "") != 0) ? - PQgetvalue(res, 0, 9)[0] : 0; + tableinfo.toasttablespace = (pset.sversion >= 80400) ? + atooid(PQgetvalue(res, 0, 8)) : 0; + tableinfo.reloftype = (pset.sversion >= 90000 && strcmp(PQgetvalue(res, 0, 9), "") != 0) ? + strdup(PQgetvalue(res, 0, 9)) : 0; + tableinfo.relpersistence = (pset.sversion >= 90100 && strcmp(PQgetvalue(res, 0, 10), "") != 0) ? + PQgetvalue(res, 0, 10)[0] : 0; PQclear(res); res = NULL; @@ -1598,6 +1602,7 @@ describeOneTableDetails(const char *schemaname, printTableAddFooter(&cont, tmpbuf.data); add_tablespace_footer(&cont, tableinfo.relkind, tableinfo.tablespace, true); + add_toasttablespace_footer(&cont, tableinfo.toasttablespace); } PQclear(result); @@ -2248,6 +2253,7 @@ describeOneTableDetails(const char *schemaname, add_tablespace_footer(&cont, tableinfo.relkind, tableinfo.tablespace, true); + add_toasttablespace_footer(&cont, tableinfo.toasttablespace); } printTable(&cont, pset.queryFout, pset.logfile); @@ -2347,6 +2353,37 @@ add_tablespace_footer(printTableContent *const cont, char relkind, } /* + * Add a TOAST tablespace description to a footer. + */ +static void +add_toasttablespace_footer(printTableContent *const cont, Oid toasttablespace) +{ + if (toasttablespace != 0) + { + PGresult *result = NULL; + PQExpBufferData buf; + + initPQExpBuffer(&buf); + printfPQExpBuffer(&buf, + "SELECT spcname FROM pg_catalog.pg_tablespace\n" + "WHERE oid = '%u';", toasttablespace); + result = PSQLexec(buf.data, false); + if (!result) + return; + /* Should always be the case, but.... */ + if (PQntuples(result) > 0) + { + /* Add the TOAST tablespace as a new footer */ + printfPQExpBuffer(&buf, _("TOAST Tablespace: \"%s\""), + PQgetvalue(result, 0, 0)); + printTableAddFooter(cont, buf.data); + } + PQclear(result); + termPQExpBuffer(&buf); + } +} + +/* * \du or \dg * * Describes roles. Any schema portion of the pattern is ignored. diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 6efc0ce..8240f69 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -814,7 +814,7 @@ psql_completion(char *text, int start, int end) "GROUP", "INDEX", "LANGUAGE", "LARGE OBJECT", "OPERATOR", "ROLE", "SCHEMA", "SERVER", "SEQUENCE", "TABLE", "TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE", - "USER", "USER MAPPING FOR", "VIEW", NULL}; + "USER", "USER MAPPING FOR", "VIEW", "TOAST TABLESPACE", "TABLE TABLESPACE", NULL}; COMPLETE_WITH_LIST(list_ALTER); } @@ -1288,12 +1288,15 @@ psql_completion(char *text, int start, int end) completion_info_charp = prev3_wd; COMPLETE_WITH_QUERY(Query_for_index_of_table); } - /* If we have TABLE SET, provide WITHOUT,TABLESPACE and SCHEMA */ + /* + * If we have TABLE SET, provide WITHOUT,TABLESPACE, TOAST TABLESPACE, + * TABLE TABLESPACE and SCHEMA + */ else if (pg_strcasecmp(prev3_wd, "TABLE") == 0 && pg_strcasecmp(prev_wd, "SET") == 0) { static const char *const list_TABLESET[] = - {"(", "WITHOUT", "TABLESPACE", "SCHEMA", NULL}; + {"(", "WITHOUT", "TABLESPACE", "SCHEMA", "TOAST TABLESPACE", "TABLE TABLESPACE", NULL}; COMPLETE_WITH_LIST(list_TABLESET); } @@ -1302,6 +1305,16 @@ psql_completion(char *text, int start, int end) pg_strcasecmp(prev2_wd, "SET") == 0 && pg_strcasecmp(prev_wd, "TABLESPACE") == 0) COMPLETE_WITH_QUERY(Query_for_list_of_tablespaces); + /* If we have ALTER TABLE SET TABLE provide TABLESPACE */ + else if (pg_strcasecmp(prev5_wd, "ALTER") == 0 && + pg_strcasecmp(prev4_wd, "TABLE") == 0 && + pg_strcasecmp(prev2_wd, "SET") == 0 && + pg_strcasecmp(prev_wd, "TABLE") == 0) + { + static const char *const list_TABLETABLESPACE[] = + {"TABLESPACE", NULL}; + COMPLETE_WITH_LIST(list_TABLETABLESPACE); + } /* If we have TABLE SET WITHOUT provide CLUSTER or OIDS */ else if (pg_strcasecmp(prev4_wd, "TABLE") == 0 && pg_strcasecmp(prev2_wd, "SET") == 0 && diff --git a/src/include/catalog/toasting.h b/src/include/catalog/toasting.h index a4f1718..d253f01 100644 --- a/src/include/catalog/toasting.h +++ b/src/include/catalog/toasting.h @@ -17,7 +17,7 @@ /* * toasting.c prototypes */ -extern void AlterTableCreateToastTable(Oid relOid, Datum reloptions); +extern void AlterTableCreateToastTable(Oid relOid, Datum reloptions, Oid toastTableSpace, bool new_toast); extern void BootstrapToastTable(char *relName, Oid toastOid, Oid toastIndexOid); diff --git a/src/include/commands/cluster.h b/src/include/commands/cluster.h index 21b2a58..1c8e863 100644 --- a/src/include/commands/cluster.h +++ b/src/include/commands/cluster.h @@ -25,7 +25,7 @@ extern void check_index_is_clusterable(Relation OldHeap, Oid indexOid, bool recheck, LOCKMODE lockmode); extern void mark_index_clustered(Relation rel, Oid indexOid); -extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace); +extern Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewToastTableSpace); extern void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h index 03f397d..e5cdb93 100644 --- a/src/include/commands/tablecmds.h +++ b/src/include/commands/tablecmds.h @@ -24,6 +24,10 @@ extern Oid DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId); extern void RemoveRelations(DropStmt *drop); +extern void RelationIsMoveableToNewTablespace(Relation rel, Oid NewTableSpaceOid); + +extern void CheckTableSpaceAlterTable(char *TableSpaceName, Oid TableSpaceOid, Oid NewTableSpaceOid); + extern Oid AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode); extern void AlterTable(Oid relid, LOCKMODE lockmode, AlterTableStmt *stmt); diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index dce0e72..7701e4f 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -1226,7 +1226,9 @@ typedef enum AlterTableType AT_DropInherit, /* NO INHERIT parent */ AT_AddOf, /* OF */ AT_DropOf, /* NOT OF */ - AT_GenericOptions /* OPTIONS (...) */ + AT_GenericOptions, /* OPTIONS (...) */ + AT_SetToastTableSpace, /* SET TOAST TABLESPACE */ + AT_SetTableTableSpace /* SET TABLE TABLESPACE */ } AlterTableType; typedef struct AlterTableCmd /* one subcommand of an ALTER TABLE */ diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h index 3d7de06..64f89b8 100644 --- a/src/include/parser/kwlist.h +++ b/src/include/parser/kwlist.h @@ -364,6 +364,7 @@ PG_KEYWORD("then", THEN, RESERVED_KEYWORD) PG_KEYWORD("time", TIME, COL_NAME_KEYWORD) PG_KEYWORD("timestamp", TIMESTAMP, COL_NAME_KEYWORD) PG_KEYWORD("to", TO, RESERVED_KEYWORD) +PG_KEYWORD("toast",TOAST, UNRESERVED_KEYWORD) PG_KEYWORD("trailing", TRAILING, RESERVED_KEYWORD) PG_KEYWORD("transaction", TRANSACTION, UNRESERVED_KEYWORD) PG_KEYWORD("treat", TREAT, COL_NAME_KEYWORD) diff --git a/src/test/regress/GNUmakefile b/src/test/regress/GNUmakefile index 27b86da..15c29f9 100644 --- a/src/test/regress/GNUmakefile +++ b/src/test/regress/GNUmakefile @@ -125,7 +125,9 @@ $(top_builddir)/contrib/dummy_seclabel/dummy_seclabel$(DLSUFFIX): $(top_builddir .PHONY: tablespace-setup tablespace-setup: rm -rf ./testtablespace + rm -rf ./testtablespace2 mkdir ./testtablespace + mkdir ./testtablespace2 ## @@ -170,4 +172,5 @@ clean distclean maintainer-clean: clean-lib # things created by various check targets rm -f $(output_files) $(input_files) rm -rf testtablespace + rm -rf testtablespace2 rm -rf $(pg_regress_clean_files) diff --git a/src/test/regress/input/tablespace.source b/src/test/regress/input/tablespace.source index dba96f4..4cac8fb 100644 --- a/src/test/regress/input/tablespace.source +++ b/src/test/regress/input/tablespace.source @@ -11,7 +11,7 @@ ALTER TABLESPACE testspace RESET (random_page_cost, seq_page_cost); -- ok CREATE SCHEMA testschema; -- try a table -CREATE TABLE testschema.foo (i int) TABLESPACE testspace; +CREATE TABLE testschema.foo (i int, label varchar) TABLESPACE testspace; SELECT relname, spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c where c.reltablespace = t.oid AND c.relname = 'foo'; @@ -54,7 +54,30 @@ CREATE TABLE bar (i int) TABLESPACE nosuchspace; -- Fail, not empty DROP TABLESPACE testspace; +-- ALTER TABLE SET TOAST TABLESPACE +CREATE TABLESPACE testspace2 LOCATION '@testtablespace2@'; +ALTER TABLE testschema.foo SET TOAST TABLESPACE testspace; +SELECT spcname FROM pg_class c JOIN pg_class d ON (c.reltoastrelid=d.oid) JOIN pg_tablespace ON (d.reltablespace = pg_tablespace.oid) WHERE c.relname = 'foo'; + +ALTER TABLE testschema.foo SET TOAST TABLESPACE testspace2; +SELECT spcname FROM pg_class c JOIN pg_class d ON (c.reltoastrelid=d.oid) JOIN pg_tablespace ON (d.reltablespace = pg_tablespace.oid) WHERE c.relname = 'foo'; + +ALTER TABLE testschema.foo SET TABLESPACE testspace2; +ALTER TABLE testschema.foo SET TABLE TABLESPACE testspace; +SELECT spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c WHERE c.reltablespace = t.oid AND c.relname = 'foo'; +SELECT spcname FROM pg_class c JOIN pg_class d ON (c.reltoastrelid=d.oid) JOIN pg_tablespace ON (d.reltablespace = pg_tablespace.oid) WHERE c.relname = 'foo'; + +CREATE TABLE testschema.foo2 (id SERIAL PRIMARY KEY, l text) TABLESPACE testspace2; +ALTER TABLE testschema.foo2 SET TOAST TABLESPACE pg_default; +INSERT INTO testschema.foo2 (l) VALUES ('foo'); +UPDATE testschema.foo2 SET l = l||l; +CLUSTER testschema.foo2 USING foo2_pkey; +SELECT spcname FROM pg_class c JOIN pg_class d ON (c.reltoastrelid=d.oid) JOIN pg_tablespace ON (d.reltablespace = pg_tablespace.oid) WHERE c.relname = 'foo2'; + +SELECT spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c WHERE c.reltablespace = t.oid AND c.relname = 'foo2'; + DROP SCHEMA testschema CASCADE; -- Should succeed DROP TABLESPACE testspace; +DROP TABLESPACE testspace2; diff --git a/src/test/regress/output/tablespace.source b/src/test/regress/output/tablespace.source index 1260c96..dea569b 100644 --- a/src/test/regress/output/tablespace.source +++ b/src/test/regress/output/tablespace.source @@ -10,7 +10,7 @@ ALTER TABLESPACE testspace RESET (random_page_cost, seq_page_cost); -- ok -- create a schema we can use CREATE SCHEMA testschema; -- try a table -CREATE TABLE testschema.foo (i int) TABLESPACE testspace; +CREATE TABLE testschema.foo (i int, label varchar) TABLESPACE testspace; SELECT relname, spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c where c.reltablespace = t.oid AND c.relname = 'foo'; relname | spcname @@ -72,11 +72,61 @@ ERROR: tablespace "nosuchspace" does not exist -- Fail, not empty DROP TABLESPACE testspace; ERROR: tablespace "testspace" is not empty +-- ALTER TABLE SET TOAST TABLESPACE +CREATE TABLESPACE testspace2 LOCATION '@testtablespace2@'; +ALTER TABLE testschema.foo SET TOAST TABLESPACE testspace; +SELECT spcname FROM pg_class c JOIN pg_class d ON (c.reltoastrelid=d.oid) JOIN pg_tablespace ON (d.reltablespace = pg_tablespace.oid) WHERE c.relname = 'foo'; + spcname +----------- + testspace +(1 row) + +ALTER TABLE testschema.foo SET TOAST TABLESPACE testspace2; +SELECT spcname FROM pg_class c JOIN pg_class d ON (c.reltoastrelid=d.oid) JOIN pg_tablespace ON (d.reltablespace = pg_tablespace.oid) WHERE c.relname = 'foo'; + spcname +------------ + testspace2 +(1 row) + +ALTER TABLE testschema.foo SET TABLESPACE testspace2; +ALTER TABLE testschema.foo SET TABLE TABLESPACE testspace; +SELECT spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c WHERE c.reltablespace = t.oid AND c.relname = 'foo'; + spcname +----------- + testspace +(1 row) + +SELECT spcname FROM pg_class c JOIN pg_class d ON (c.reltoastrelid=d.oid) JOIN pg_tablespace ON (d.reltablespace = pg_tablespace.oid) WHERE c.relname = 'foo'; + spcname +------------ + testspace2 +(1 row) + +CREATE TABLE testschema.foo2 (id SERIAL PRIMARY KEY, l text) TABLESPACE testspace2; +NOTICE: CREATE TABLE will create implicit sequence "foo2_id_seq" for serial column "foo2.id" +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "foo2_pkey" for table "foo2" +ALTER TABLE testschema.foo2 SET TOAST TABLESPACE pg_default; +INSERT INTO testschema.foo2 (l) VALUES ('foo'); +UPDATE testschema.foo2 SET l = l||l; +CLUSTER testschema.foo2 USING foo2_pkey; +SELECT spcname FROM pg_class c JOIN pg_class d ON (c.reltoastrelid=d.oid) JOIN pg_tablespace ON (d.reltablespace = pg_tablespace.oid) WHERE c.relname = 'foo2'; + spcname +--------- +(0 rows) + +SELECT spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c WHERE c.reltablespace = t.oid AND c.relname = 'foo2'; + spcname +------------ + testspace2 +(1 row) + DROP SCHEMA testschema CASCADE; -NOTICE: drop cascades to 4 other objects +NOTICE: drop cascades to 5 other objects DETAIL: drop cascades to table testschema.foo drop cascades to table testschema.asselect drop cascades to table testschema.asexecute drop cascades to table testschema.atable +drop cascades to table testschema.foo2 -- Should succeed DROP TABLESPACE testspace; +DROP TABLESPACE testspace2; diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c index 2f6b37b..a0860ca 100644 --- a/src/test/regress/pg_regress.c +++ b/src/test/regress/pg_regress.c @@ -410,6 +410,7 @@ static void convert_sourcefiles_in(char *source_subdir, char *dest_subdir, char *suffix) { char testtablespace[MAXPGPATH]; + char testtablespace2[MAXPGPATH]; char indir[MAXPGPATH]; struct stat st; int ret; @@ -436,6 +437,7 @@ convert_sourcefiles_in(char *source_subdir, char *dest_subdir, char *suffix) exit(2); snprintf(testtablespace, MAXPGPATH, "%s/testtablespace", outputdir); + snprintf(testtablespace2, MAXPGPATH, "%s/testtablespace2", outputdir); #ifdef WIN32 @@ -452,6 +454,9 @@ convert_sourcefiles_in(char *source_subdir, char *dest_subdir, char *suffix) if (directory_exists(testtablespace)) rmtree(testtablespace, true); make_directory(testtablespace); + if (directory_exists(testtablespace2)) + rmtree(testtablespace2, true); + make_directory(testtablespace2); #endif /* finally loop on each file and do the replacement */ @@ -496,6 +501,7 @@ convert_sourcefiles_in(char *source_subdir, char *dest_subdir, char *suffix) replace_string(line, "@abs_srcdir@", inputdir); replace_string(line, "@abs_builddir@", outputdir); replace_string(line, "@testtablespace@", testtablespace); + replace_string(line, "@testtablespace2@", testtablespace2); replace_string(line, "@libdir@", dlpath); replace_string(line, "@DLSUFFIX@", DLSUFFIX); fputs(line, outfile);