Fix tablespace inheritance for partitioned rels

Commit ca4103025d left a few loose ends.  The most important one
(broken pg_dump output) is already fixed by virtue of commit
3b23552ad8, but some things remained:

* When ALTER TABLE rewrites tables, the indexes must remain in the
  tablespace they were originally in.  This didn't work because
  index recreation during ALTER TABLE runs manufactured SQL (yuck),
  which runs afoul of default_tablespace in competition with the parent
  relation tablespace.  To fix, reset default_tablespace to the empty
  string temporarily, and add the TABLESPACE clause as appropriate.

* Setting a partitioned rel's tablespace to the database default is
  confusing; if it worked, it would direct the partitions to that
  tablespace regardless of default_tablespace.  But in reality it does
  not work, and making it work is a larger project.  Therefore, throw
  an error when this condition is detected, to alert the unwary.

Add some docs and tests, too.

Author: Álvaro Herrera
Discussion: https://postgr.es/m/CAKJS1f_1c260nOt_vBJ067AZ3JXptXVRohDVMLEBmudX1YEx-A@mail.gmail.com
This commit is contained in:
Alvaro Herrera 2019-04-25 10:20:23 -04:00
parent 3b23552ad8
commit 87259588d0
18 changed files with 519 additions and 34 deletions

View File

@ -7356,7 +7356,8 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv;
<para>
This variable specifies the default tablespace in which to create
objects (tables and indexes) when a <command>CREATE</command> command does
not explicitly specify a tablespace.
not explicitly specify a tablespace. It also determines the tablespace
that a partitioned relation will direct future partitions to.
</para>
<para>

View File

@ -1265,9 +1265,9 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
<xref linkend="guc-default-tablespace"/> is consulted, or
<xref linkend="guc-temp-tablespaces"/> if the table is temporary. For
partitioned tables, since no storage is required for the table itself,
the tablespace specified here only serves to mark the default tablespace
for any newly created partitions when no other tablespace is explicitly
specified.
the tablespace specified overrides <literal>default_tablespace</literal>
as the default tablespace to use for any newly created partitions when no
other tablespace is explicitly specified.
</para>
</listitem>
</varlistentry>

View File

@ -314,6 +314,7 @@ Boot_DeclareIndexStmt:
stmt->transformed = false;
stmt->concurrent = false;
stmt->if_not_exists = false;
stmt->reset_default_tblspc = false;
/* locks and races need not concern us in bootstrap mode */
relationId = RangeVarGetRelid(stmt->relation, NoLock,
@ -363,6 +364,7 @@ Boot_DeclareUniqueIndexStmt:
stmt->transformed = false;
stmt->concurrent = false;
stmt->if_not_exists = false;
stmt->reset_default_tblspc = false;
/* locks and races need not concern us in bootstrap mode */
relationId = RangeVarGetRelid(stmt->relation, NoLock,

View File

@ -467,8 +467,21 @@ DefineIndex(Oid relationId,
LOCKTAG heaplocktag;
LOCKMODE lockmode;
Snapshot snapshot;
int save_nestlevel = -1;
int i;
/*
* Some callers need us to run with an empty default_tablespace; this is a
* necessary hack to be able to reproduce catalog state accurately when
* recreating indexes after table-rewriting ALTER TABLE.
*/
if (stmt->reset_default_tblspc)
{
save_nestlevel = NewGUCNestLevel();
(void) set_config_option("default_tablespace", "",
PGC_USERSET, PGC_S_SESSION,
GUC_ACTION_SAVE, true, 0, false);
}
/*
* Start progress report. If we're building a partition, this was already
@ -622,10 +635,15 @@ DefineIndex(Oid relationId,
if (stmt->tableSpace)
{
tablespaceId = get_tablespace_oid(stmt->tableSpace, false);
if (partitioned && tablespaceId == MyDatabaseTableSpace)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot specify default tablespace for partitioned relation")));
}
else
{
tablespaceId = GetDefaultTablespace(rel->rd_rel->relpersistence);
tablespaceId = GetDefaultTablespace(rel->rd_rel->relpersistence,
partitioned);
/* note InvalidOid is OK in this case */
}
@ -980,6 +998,13 @@ DefineIndex(Oid relationId,
ObjectAddressSet(address, RelationRelationId, indexRelationId);
/*
* Revert to original default_tablespace. Must do this before any return
* from this function, but after index_create, so this is a good time.
*/
if (save_nestlevel >= 0)
AtEOXact_GUC(true, save_nestlevel);
if (!OidIsValid(indexRelationId))
{
table_close(rel, NoLock);

View File

@ -284,7 +284,7 @@ ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString,
/* Concurrent refresh builds new data in temp tablespace, and does diff. */
if (concurrent)
{
tableSpace = GetDefaultTablespace(RELPERSISTENCE_TEMP);
tableSpace = GetDefaultTablespace(RELPERSISTENCE_TEMP, false);
relpersistence = RELPERSISTENCE_TEMP;
}
else

View File

@ -567,6 +567,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
Datum reloptions;
ListCell *listptr;
AttrNumber attnum;
bool partitioned;
static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
Oid ofTypeId;
ObjectAddress address;
@ -595,7 +596,10 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
elog(ERROR, "unexpected relkind: %d", (int) relkind);
relkind = RELKIND_PARTITIONED_TABLE;
partitioned = true;
}
else
partitioned = false;
/*
* Look up the namespace in which we are supposed to create the relation,
@ -664,31 +668,24 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
if (stmt->tablespacename)
{
tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
if (partitioned && tablespaceId == MyDatabaseTableSpace)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot specify default tablespace for partitioned relations")));
}
else if (stmt->partbound)
{
HeapTuple tup;
/*
* For partitions, when no other tablespace is specified, we default
* the tablespace to the parent partitioned table's.
*/
Assert(list_length(inheritOids) == 1);
tup = SearchSysCache1(RELOID,
DatumGetObjectId(linitial_oid(inheritOids)));
tablespaceId = ((Form_pg_class) GETSTRUCT(tup))->reltablespace;
if (!OidIsValid(tablespaceId))
tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence);
ReleaseSysCache(tup);
tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
}
else
{
tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence);
/* note InvalidOid is OK in this case */
}
tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
partitioned);
/* Check permissions except when using database's default */
if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
@ -825,7 +822,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
{
accessMethod = stmt->accessMethod;
if (relkind == RELKIND_PARTITIONED_TABLE)
if (partitioned)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("specifying a table access method is not supported on a partitioned table")));
@ -998,7 +995,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
* Process the partitioning specification (if any) and store the partition
* key information into the catalog.
*/
if (stmt->partspec)
if (partitioned)
{
ParseState *pstate;
char strategy;
@ -11276,6 +11273,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
if (!rewrite)
TryReuseIndex(oldId, stmt);
stmt->reset_default_tblspc = true;
/* keep the index's comment */
stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
@ -11307,6 +11305,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
/* keep any comment on the index */
indstmt->idxcomment = GetComment(indoid,
RelationRelationId, 0);
indstmt->reset_default_tblspc = true;
cmd->subtype = AT_ReAddIndex;
tab->subcmds[AT_PASS_OLD_INDEX] =
@ -11329,6 +11328,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
if (con->contype == CONSTR_FOREIGN &&
!rewrite && tab->rewrite == 0)
TryReuseForeignKey(oldId, con);
con->reset_default_tblspc = true;
cmd->subtype = AT_ReAddConstraint;
tab->subcmds[AT_PASS_OLD_CONSTR] =
lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);

View File

@ -1104,7 +1104,9 @@ check_default_tablespace(char **newval, void **extra, GucSource source)
* GetDefaultTablespace -- get the OID of the current default tablespace
*
* Temporary objects have different default tablespaces, hence the
* relpersistence parameter must be specified.
* relpersistence parameter must be specified. Also, for partitioned tables,
* we disallow specifying the database default, so that needs to be specified
* too.
*
* May return InvalidOid to indicate "use the database's default tablespace".
*
@ -1115,7 +1117,7 @@ check_default_tablespace(char **newval, void **extra, GucSource source)
* default_tablespace GUC variable.
*/
Oid
GetDefaultTablespace(char relpersistence)
GetDefaultTablespace(char relpersistence, bool partitioned)
{
Oid result;
@ -1141,10 +1143,18 @@ GetDefaultTablespace(char relpersistence)
/*
* Allow explicit specification of database's default tablespace in
* default_tablespace without triggering permissions checks.
* default_tablespace without triggering permissions checks. Don't
* allow specifying that when creating a partitioned table, however,
* since the result is confusing.
*/
if (result == MyDatabaseTableSpace)
{
if (partitioned)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot specify default tablespace for partitioned relations")));
result = InvalidOid;
}
return result;
}

View File

@ -2919,6 +2919,7 @@ _copyConstraint(const Constraint *from)
COPY_NODE_FIELD(options);
COPY_STRING_FIELD(indexname);
COPY_STRING_FIELD(indexspace);
COPY_SCALAR_FIELD(reset_default_tblspc);
COPY_STRING_FIELD(access_method);
COPY_NODE_FIELD(where_clause);
COPY_NODE_FIELD(pktable);
@ -3475,6 +3476,7 @@ _copyIndexStmt(const IndexStmt *from)
COPY_SCALAR_FIELD(transformed);
COPY_SCALAR_FIELD(concurrent);
COPY_SCALAR_FIELD(if_not_exists);
COPY_SCALAR_FIELD(reset_default_tblspc);
return newnode;
}

View File

@ -1347,6 +1347,7 @@ _equalIndexStmt(const IndexStmt *a, const IndexStmt *b)
COMPARE_SCALAR_FIELD(transformed);
COMPARE_SCALAR_FIELD(concurrent);
COMPARE_SCALAR_FIELD(if_not_exists);
COMPARE_SCALAR_FIELD(reset_default_tblspc);
return true;
}
@ -2593,6 +2594,7 @@ _equalConstraint(const Constraint *a, const Constraint *b)
COMPARE_NODE_FIELD(options);
COMPARE_STRING_FIELD(indexname);
COMPARE_STRING_FIELD(indexspace);
COMPARE_SCALAR_FIELD(reset_default_tblspc);
COMPARE_STRING_FIELD(access_method);
COMPARE_NODE_FIELD(where_clause);
COMPARE_NODE_FIELD(pktable);

View File

@ -2649,6 +2649,7 @@ _outIndexStmt(StringInfo str, const IndexStmt *node)
WRITE_BOOL_FIELD(transformed);
WRITE_BOOL_FIELD(concurrent);
WRITE_BOOL_FIELD(if_not_exists);
WRITE_BOOL_FIELD(reset_default_tblspc);
}
static void
@ -3491,6 +3492,7 @@ _outConstraint(StringInfo str, const Constraint *node)
WRITE_NODE_FIELD(options);
WRITE_STRING_FIELD(indexname);
WRITE_STRING_FIELD(indexspace);
WRITE_BOOL_FIELD(reset_default_tblspc);
/* access_method and where_clause not currently used */
break;
@ -3501,6 +3503,7 @@ _outConstraint(StringInfo str, const Constraint *node)
WRITE_NODE_FIELD(options);
WRITE_STRING_FIELD(indexname);
WRITE_STRING_FIELD(indexspace);
WRITE_BOOL_FIELD(reset_default_tblspc);
/* access_method and where_clause not currently used */
break;
@ -3511,6 +3514,7 @@ _outConstraint(StringInfo str, const Constraint *node)
WRITE_NODE_FIELD(options);
WRITE_STRING_FIELD(indexname);
WRITE_STRING_FIELD(indexspace);
WRITE_BOOL_FIELD(reset_default_tblspc);
WRITE_STRING_FIELD(access_method);
WRITE_NODE_FIELD(where_clause);
break;

View File

@ -7363,6 +7363,7 @@ IndexStmt: CREATE opt_unique INDEX opt_concurrently opt_index_name
n->initdeferred = false;
n->transformed = false;
n->if_not_exists = false;
n->reset_default_tblspc = false;
$$ = (Node *)n;
}
| CREATE opt_unique INDEX opt_concurrently IF_P NOT EXISTS index_name
@ -7390,6 +7391,7 @@ IndexStmt: CREATE opt_unique INDEX opt_concurrently opt_index_name
n->initdeferred = false;
n->transformed = false;
n->if_not_exists = true;
n->reset_default_tblspc = false;
$$ = (Node *)n;
}
;

View File

@ -1406,6 +1406,7 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
index->transformed = true; /* don't need transformIndexStmt */
index->concurrent = false;
index->if_not_exists = false;
index->reset_default_tblspc = false;
/*
* We don't try to preserve the name of the source index; instead, just
@ -2001,6 +2002,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
index->transformed = false;
index->concurrent = false;
index->if_not_exists = false;
index->reset_default_tblspc = constraint->reset_default_tblspc;
/*
* If it's ALTER TABLE ADD CONSTRAINT USING INDEX, look up the index and

View File

@ -1429,12 +1429,13 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
Oid tblspc;
tblspc = get_rel_tablespace(indexrelid);
if (!OidIsValid(tblspc))
tblspc = MyDatabaseTableSpace;
if (isConstraint)
appendStringInfoString(&buf, " USING INDEX");
appendStringInfo(&buf, " TABLESPACE %s",
quote_identifier(get_tablespace_name(tblspc)));
if (OidIsValid(tblspc))
{
if (isConstraint)
appendStringInfoString(&buf, " USING INDEX");
appendStringInfo(&buf, " TABLESPACE %s",
quote_identifier(get_tablespace_name(tblspc)));
}
}
/*
@ -2170,6 +2171,12 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
pfree(options);
}
/*
* Print the tablespace, unless it's the database default.
* This is to help ALTER TABLE usage of this facility,
* which needs this behavior to recreate exact catalog
* state.
*/
tblspc = get_rel_tablespace(indexId);
if (OidIsValid(tblspc))
appendStringInfo(&buf, " USING INDEX TABLESPACE %s",

View File

@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 201904072
#define CATALOG_VERSION_NO 201904251
#endif

View File

@ -49,7 +49,7 @@ extern Oid AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt);
extern void TablespaceCreateDbspace(Oid spcNode, Oid dbNode, bool isRedo);
extern Oid GetDefaultTablespace(char relpersistence);
extern Oid GetDefaultTablespace(char relpersistence, bool partitioned);
extern void PrepareTempTablespaces(void);

View File

@ -2154,6 +2154,8 @@ typedef struct Constraint
List *options; /* options from WITH clause */
char *indexname; /* existing index to use; otherwise NULL */
char *indexspace; /* index tablespace; NULL for default */
bool reset_default_tblspc; /* reset default_tablespace prior to
* creating the index */
/* These could be, but currently are not, used for UNIQUE/PKEY: */
char *access_method; /* index access method; NULL for default */
Node *where_clause; /* partial index predicate */
@ -2769,6 +2771,8 @@ typedef struct IndexStmt
bool transformed; /* true when transformIndexStmt is finished */
bool concurrent; /* should this be a concurrent index build? */
bool if_not_exists; /* just do nothing if index already exists? */
bool reset_default_tblspc; /* reset default_tablespace prior to
* executing */
} IndexStmt;
/* ----------------------

View File

@ -65,24 +65,45 @@ SELECT relname, spcname FROM pg_catalog.pg_tablespace t, pg_catalog.pg_class c
where c.reltablespace = t.oid AND c.relname LIKE 'part%_idx';
\d testschema.part_a_idx
-- partitioned rels cannot specify the default tablespace. These fail:
CREATE TABLE testschema.dflt (a int PRIMARY KEY) PARTITION BY LIST (a) TABLESPACE pg_default;
CREATE TABLE testschema.dflt (a int PRIMARY KEY USING INDEX TABLESPACE pg_default) PARTITION BY LIST (a);
SET default_tablespace TO 'pg_default';
CREATE TABLE testschema.dflt (a int PRIMARY KEY) PARTITION BY LIST (a) TABLESPACE regress_tblspace;
CREATE TABLE testschema.dflt (a int PRIMARY KEY USING INDEX TABLESPACE regress_tblspace) PARTITION BY LIST (a);
-- but these work:
CREATE TABLE testschema.dflt (a int PRIMARY KEY USING INDEX TABLESPACE regress_tblspace) PARTITION BY LIST (a) TABLESPACE regress_tblspace;
SET default_tablespace TO '';
CREATE TABLE testschema.dflt2 (a int PRIMARY KEY) PARTITION BY LIST (a);
DROP TABLE testschema.dflt, testschema.dflt2;
-- check that default_tablespace doesn't affect ALTER TABLE index rebuilds
CREATE TABLE testschema.test_default_tab(id bigint) TABLESPACE regress_tblspace;
INSERT INTO testschema.test_default_tab VALUES (1);
CREATE INDEX test_index1 on testschema.test_default_tab (id);
CREATE INDEX test_index2 on testschema.test_default_tab (id) TABLESPACE regress_tblspace;
ALTER TABLE testschema.test_default_tab ADD CONSTRAINT test_index3 PRIMARY KEY (id);
ALTER TABLE testschema.test_default_tab ADD CONSTRAINT test_index4 UNIQUE (id) USING INDEX TABLESPACE regress_tblspace;
\d testschema.test_index1
\d testschema.test_index2
\d testschema.test_index3
\d testschema.test_index4
-- use a custom tablespace for default_tablespace
SET default_tablespace TO regress_tblspace;
-- tablespace should not change if no rewrite
ALTER TABLE testschema.test_default_tab ALTER id TYPE bigint;
\d testschema.test_index1
\d testschema.test_index2
\d testschema.test_index3
\d testschema.test_index4
SELECT * FROM testschema.test_default_tab;
-- tablespace should not change even if there is an index rewrite
ALTER TABLE testschema.test_default_tab ALTER id TYPE int;
\d testschema.test_index1
\d testschema.test_index2
\d testschema.test_index3
\d testschema.test_index4
SELECT * FROM testschema.test_default_tab;
-- now use the default tablespace for default_tablespace
SET default_tablespace TO '';
@ -90,12 +111,64 @@ SET default_tablespace TO '';
ALTER TABLE testschema.test_default_tab ALTER id TYPE int;
\d testschema.test_index1
\d testschema.test_index2
\d testschema.test_index3
\d testschema.test_index4
-- tablespace should not change even if there is an index rewrite
ALTER TABLE testschema.test_default_tab ALTER id TYPE bigint;
\d testschema.test_index1
\d testschema.test_index2
\d testschema.test_index3
\d testschema.test_index4
DROP TABLE testschema.test_default_tab;
-- check that default_tablespace doesn't affect ALTER TABLE index rebuilds
-- (this time with a partitioned table)
CREATE TABLE testschema.test_default_tab_p(id bigint, val bigint)
PARTITION BY LIST (id) TABLESPACE regress_tblspace;
CREATE TABLE testschema.test_default_tab_p1 PARTITION OF testschema.test_default_tab_p
FOR VALUES IN (1);
INSERT INTO testschema.test_default_tab_p VALUES (1);
CREATE INDEX test_index1 on testschema.test_default_tab_p (val);
CREATE INDEX test_index2 on testschema.test_default_tab_p (val) TABLESPACE regress_tblspace;
ALTER TABLE testschema.test_default_tab_p ADD CONSTRAINT test_index3 PRIMARY KEY (id);
ALTER TABLE testschema.test_default_tab_p ADD CONSTRAINT test_index4 UNIQUE (id) USING INDEX TABLESPACE regress_tblspace;
\d testschema.test_index1
\d testschema.test_index2
\d testschema.test_index3
\d testschema.test_index4
-- use a custom tablespace for default_tablespace
SET default_tablespace TO regress_tblspace;
-- tablespace should not change if no rewrite
ALTER TABLE testschema.test_default_tab_p ALTER val TYPE bigint;
\d testschema.test_index1
\d testschema.test_index2
\d testschema.test_index3
\d testschema.test_index4
SELECT * FROM testschema.test_default_tab_p;
-- tablespace should not change even if there is an index rewrite
ALTER TABLE testschema.test_default_tab_p ALTER val TYPE int;
\d testschema.test_index1
\d testschema.test_index2
\d testschema.test_index3
\d testschema.test_index4
SELECT * FROM testschema.test_default_tab_p;
-- now use the default tablespace for default_tablespace
SET default_tablespace TO '';
-- tablespace should not change if no rewrite
ALTER TABLE testschema.test_default_tab_p ALTER val TYPE int;
\d testschema.test_index1
\d testschema.test_index2
\d testschema.test_index3
\d testschema.test_index4
-- tablespace should not change even if there is an index rewrite
ALTER TABLE testschema.test_default_tab_p ALTER val TYPE bigint;
\d testschema.test_index1
\d testschema.test_index2
\d testschema.test_index3
\d testschema.test_index4
DROP TABLE testschema.test_default_tab_p;
-- check that default_tablespace affects index additions in ALTER TABLE
CREATE TABLE testschema.test_tab(id int) TABLESPACE regress_tblspace;
INSERT INTO testschema.test_tab VALUES (1);
@ -108,6 +181,23 @@ ALTER TABLE testschema.test_tab ADD CONSTRAINT test_tab_pkey PRIMARY KEY (id);
SELECT * FROM testschema.test_tab;
DROP TABLE testschema.test_tab;
-- check that default_tablespace is handled correctly by multi-command
-- ALTER TABLE that includes a tablespace-preserving rewrite
CREATE TABLE testschema.test_tab(a int, b int, c int);
SET default_tablespace TO regress_tblspace;
ALTER TABLE testschema.test_tab ADD CONSTRAINT test_tab_unique UNIQUE (a);
CREATE INDEX test_tab_a_idx ON testschema.test_tab (a);
SET default_tablespace TO '';
CREATE INDEX test_tab_b_idx ON testschema.test_tab (b);
\d testschema.test_tab_unique
\d testschema.test_tab_a_idx
\d testschema.test_tab_b_idx
ALTER TABLE testschema.test_tab ALTER b TYPE bigint, ADD UNIQUE (c);
\d testschema.test_tab_unique
\d testschema.test_tab_a_idx
\d testschema.test_tab_b_idx
DROP TABLE testschema.test_tab;
-- let's try moving a table from one place to another
CREATE TABLE testschema.atable AS VALUES (1), (2);
CREATE UNIQUE INDEX anindex ON testschema.atable(column1);

View File

@ -102,11 +102,28 @@ Partitioned index "testschema.part_a_idx"
btree, for table "testschema.part"
Tablespace: "regress_tblspace"
-- partitioned rels cannot specify the default tablespace. These fail:
CREATE TABLE testschema.dflt (a int PRIMARY KEY) PARTITION BY LIST (a) TABLESPACE pg_default;
ERROR: cannot specify default tablespace for partitioned relations
CREATE TABLE testschema.dflt (a int PRIMARY KEY USING INDEX TABLESPACE pg_default) PARTITION BY LIST (a);
ERROR: cannot specify default tablespace for partitioned relation
SET default_tablespace TO 'pg_default';
CREATE TABLE testschema.dflt (a int PRIMARY KEY) PARTITION BY LIST (a) TABLESPACE regress_tblspace;
ERROR: cannot specify default tablespace for partitioned relations
CREATE TABLE testschema.dflt (a int PRIMARY KEY USING INDEX TABLESPACE regress_tblspace) PARTITION BY LIST (a);
ERROR: cannot specify default tablespace for partitioned relations
-- but these work:
CREATE TABLE testschema.dflt (a int PRIMARY KEY USING INDEX TABLESPACE regress_tblspace) PARTITION BY LIST (a) TABLESPACE regress_tblspace;
SET default_tablespace TO '';
CREATE TABLE testschema.dflt2 (a int PRIMARY KEY) PARTITION BY LIST (a);
DROP TABLE testschema.dflt, testschema.dflt2;
-- check that default_tablespace doesn't affect ALTER TABLE index rebuilds
CREATE TABLE testschema.test_default_tab(id bigint) TABLESPACE regress_tblspace;
INSERT INTO testschema.test_default_tab VALUES (1);
CREATE INDEX test_index1 on testschema.test_default_tab (id);
CREATE INDEX test_index2 on testschema.test_default_tab (id) TABLESPACE regress_tblspace;
ALTER TABLE testschema.test_default_tab ADD CONSTRAINT test_index3 PRIMARY KEY (id);
ALTER TABLE testschema.test_default_tab ADD CONSTRAINT test_index4 UNIQUE (id) USING INDEX TABLESPACE regress_tblspace;
\d testschema.test_index1
Index "testschema.test_index1"
Column | Type | Key? | Definition
@ -122,6 +139,21 @@ btree, for table "testschema.test_default_tab"
btree, for table "testschema.test_default_tab"
Tablespace: "regress_tblspace"
\d testschema.test_index3
Index "testschema.test_index3"
Column | Type | Key? | Definition
--------+--------+------+------------
id | bigint | yes | id
primary key, btree, for table "testschema.test_default_tab"
\d testschema.test_index4
Index "testschema.test_index4"
Column | Type | Key? | Definition
--------+--------+------+------------
id | bigint | yes | id
unique, btree, for table "testschema.test_default_tab"
Tablespace: "regress_tblspace"
-- use a custom tablespace for default_tablespace
SET default_tablespace TO regress_tblspace;
-- tablespace should not change if no rewrite
@ -141,6 +173,21 @@ btree, for table "testschema.test_default_tab"
btree, for table "testschema.test_default_tab"
Tablespace: "regress_tblspace"
\d testschema.test_index3
Index "testschema.test_index3"
Column | Type | Key? | Definition
--------+--------+------+------------
id | bigint | yes | id
primary key, btree, for table "testschema.test_default_tab"
\d testschema.test_index4
Index "testschema.test_index4"
Column | Type | Key? | Definition
--------+--------+------+------------
id | bigint | yes | id
unique, btree, for table "testschema.test_default_tab"
Tablespace: "regress_tblspace"
SELECT * FROM testschema.test_default_tab;
id
----
@ -164,6 +211,21 @@ btree, for table "testschema.test_default_tab"
btree, for table "testschema.test_default_tab"
Tablespace: "regress_tblspace"
\d testschema.test_index3
Index "testschema.test_index3"
Column | Type | Key? | Definition
--------+---------+------+------------
id | integer | yes | id
primary key, btree, for table "testschema.test_default_tab"
\d testschema.test_index4
Index "testschema.test_index4"
Column | Type | Key? | Definition
--------+---------+------+------------
id | integer | yes | id
unique, btree, for table "testschema.test_default_tab"
Tablespace: "regress_tblspace"
SELECT * FROM testschema.test_default_tab;
id
----
@ -189,6 +251,21 @@ btree, for table "testschema.test_default_tab"
btree, for table "testschema.test_default_tab"
Tablespace: "regress_tblspace"
\d testschema.test_index3
Index "testschema.test_index3"
Column | Type | Key? | Definition
--------+---------+------+------------
id | integer | yes | id
primary key, btree, for table "testschema.test_default_tab"
\d testschema.test_index4
Index "testschema.test_index4"
Column | Type | Key? | Definition
--------+---------+------+------------
id | integer | yes | id
unique, btree, for table "testschema.test_default_tab"
Tablespace: "regress_tblspace"
-- tablespace should not change even if there is an index rewrite
ALTER TABLE testschema.test_default_tab ALTER id TYPE bigint;
\d testschema.test_index1
@ -206,7 +283,208 @@ btree, for table "testschema.test_default_tab"
btree, for table "testschema.test_default_tab"
Tablespace: "regress_tblspace"
\d testschema.test_index3
Index "testschema.test_index3"
Column | Type | Key? | Definition
--------+--------+------+------------
id | bigint | yes | id
primary key, btree, for table "testschema.test_default_tab"
\d testschema.test_index4
Index "testschema.test_index4"
Column | Type | Key? | Definition
--------+--------+------+------------
id | bigint | yes | id
unique, btree, for table "testschema.test_default_tab"
Tablespace: "regress_tblspace"
DROP TABLE testschema.test_default_tab;
-- check that default_tablespace doesn't affect ALTER TABLE index rebuilds
-- (this time with a partitioned table)
CREATE TABLE testschema.test_default_tab_p(id bigint, val bigint)
PARTITION BY LIST (id) TABLESPACE regress_tblspace;
CREATE TABLE testschema.test_default_tab_p1 PARTITION OF testschema.test_default_tab_p
FOR VALUES IN (1);
INSERT INTO testschema.test_default_tab_p VALUES (1);
CREATE INDEX test_index1 on testschema.test_default_tab_p (val);
CREATE INDEX test_index2 on testschema.test_default_tab_p (val) TABLESPACE regress_tblspace;
ALTER TABLE testschema.test_default_tab_p ADD CONSTRAINT test_index3 PRIMARY KEY (id);
ALTER TABLE testschema.test_default_tab_p ADD CONSTRAINT test_index4 UNIQUE (id) USING INDEX TABLESPACE regress_tblspace;
\d testschema.test_index1
Partitioned index "testschema.test_index1"
Column | Type | Key? | Definition
--------+--------+------+------------
val | bigint | yes | val
btree, for table "testschema.test_default_tab_p"
\d testschema.test_index2
Partitioned index "testschema.test_index2"
Column | Type | Key? | Definition
--------+--------+------+------------
val | bigint | yes | val
btree, for table "testschema.test_default_tab_p"
Tablespace: "regress_tblspace"
\d testschema.test_index3
Partitioned index "testschema.test_index3"
Column | Type | Key? | Definition
--------+--------+------+------------
id | bigint | yes | id
primary key, btree, for table "testschema.test_default_tab_p"
\d testschema.test_index4
Partitioned index "testschema.test_index4"
Column | Type | Key? | Definition
--------+--------+------+------------
id | bigint | yes | id
unique, btree, for table "testschema.test_default_tab_p"
Tablespace: "regress_tblspace"
-- use a custom tablespace for default_tablespace
SET default_tablespace TO regress_tblspace;
-- tablespace should not change if no rewrite
ALTER TABLE testschema.test_default_tab_p ALTER val TYPE bigint;
\d testschema.test_index1
Partitioned index "testschema.test_index1"
Column | Type | Key? | Definition
--------+--------+------+------------
val | bigint | yes | val
btree, for table "testschema.test_default_tab_p"
\d testschema.test_index2
Partitioned index "testschema.test_index2"
Column | Type | Key? | Definition
--------+--------+------+------------
val | bigint | yes | val
btree, for table "testschema.test_default_tab_p"
Tablespace: "regress_tblspace"
\d testschema.test_index3
Partitioned index "testschema.test_index3"
Column | Type | Key? | Definition
--------+--------+------+------------
id | bigint | yes | id
primary key, btree, for table "testschema.test_default_tab_p"
\d testschema.test_index4
Partitioned index "testschema.test_index4"
Column | Type | Key? | Definition
--------+--------+------+------------
id | bigint | yes | id
unique, btree, for table "testschema.test_default_tab_p"
Tablespace: "regress_tblspace"
SELECT * FROM testschema.test_default_tab_p;
id | val
----+-----
1 |
(1 row)
-- tablespace should not change even if there is an index rewrite
ALTER TABLE testschema.test_default_tab_p ALTER val TYPE int;
\d testschema.test_index1
Partitioned index "testschema.test_index1"
Column | Type | Key? | Definition
--------+---------+------+------------
val | integer | yes | val
btree, for table "testschema.test_default_tab_p"
\d testschema.test_index2
Partitioned index "testschema.test_index2"
Column | Type | Key? | Definition
--------+---------+------+------------
val | integer | yes | val
btree, for table "testschema.test_default_tab_p"
Tablespace: "regress_tblspace"
\d testschema.test_index3
Partitioned index "testschema.test_index3"
Column | Type | Key? | Definition
--------+--------+------+------------
id | bigint | yes | id
primary key, btree, for table "testschema.test_default_tab_p"
\d testschema.test_index4
Partitioned index "testschema.test_index4"
Column | Type | Key? | Definition
--------+--------+------+------------
id | bigint | yes | id
unique, btree, for table "testschema.test_default_tab_p"
Tablespace: "regress_tblspace"
SELECT * FROM testschema.test_default_tab_p;
id | val
----+-----
1 |
(1 row)
-- now use the default tablespace for default_tablespace
SET default_tablespace TO '';
-- tablespace should not change if no rewrite
ALTER TABLE testschema.test_default_tab_p ALTER val TYPE int;
\d testschema.test_index1
Partitioned index "testschema.test_index1"
Column | Type | Key? | Definition
--------+---------+------+------------
val | integer | yes | val
btree, for table "testschema.test_default_tab_p"
\d testschema.test_index2
Partitioned index "testschema.test_index2"
Column | Type | Key? | Definition
--------+---------+------+------------
val | integer | yes | val
btree, for table "testschema.test_default_tab_p"
Tablespace: "regress_tblspace"
\d testschema.test_index3
Partitioned index "testschema.test_index3"
Column | Type | Key? | Definition
--------+--------+------+------------
id | bigint | yes | id
primary key, btree, for table "testschema.test_default_tab_p"
\d testschema.test_index4
Partitioned index "testschema.test_index4"
Column | Type | Key? | Definition
--------+--------+------+------------
id | bigint | yes | id
unique, btree, for table "testschema.test_default_tab_p"
Tablespace: "regress_tblspace"
-- tablespace should not change even if there is an index rewrite
ALTER TABLE testschema.test_default_tab_p ALTER val TYPE bigint;
\d testschema.test_index1
Partitioned index "testschema.test_index1"
Column | Type | Key? | Definition
--------+--------+------+------------
val | bigint | yes | val
btree, for table "testschema.test_default_tab_p"
\d testschema.test_index2
Partitioned index "testschema.test_index2"
Column | Type | Key? | Definition
--------+--------+------+------------
val | bigint | yes | val
btree, for table "testschema.test_default_tab_p"
Tablespace: "regress_tblspace"
\d testschema.test_index3
Partitioned index "testschema.test_index3"
Column | Type | Key? | Definition
--------+--------+------+------------
id | bigint | yes | id
primary key, btree, for table "testschema.test_default_tab_p"
\d testschema.test_index4
Partitioned index "testschema.test_index4"
Column | Type | Key? | Definition
--------+--------+------+------------
id | bigint | yes | id
unique, btree, for table "testschema.test_default_tab_p"
Tablespace: "regress_tblspace"
DROP TABLE testschema.test_default_tab_p;
-- check that default_tablespace affects index additions in ALTER TABLE
CREATE TABLE testschema.test_tab(id int) TABLESPACE regress_tblspace;
INSERT INTO testschema.test_tab VALUES (1);
@ -235,6 +513,62 @@ SELECT * FROM testschema.test_tab;
1
(1 row)
DROP TABLE testschema.test_tab;
-- check that default_tablespace is handled correctly by multi-command
-- ALTER TABLE that includes a tablespace-preserving rewrite
CREATE TABLE testschema.test_tab(a int, b int, c int);
SET default_tablespace TO regress_tblspace;
ALTER TABLE testschema.test_tab ADD CONSTRAINT test_tab_unique UNIQUE (a);
CREATE INDEX test_tab_a_idx ON testschema.test_tab (a);
SET default_tablespace TO '';
CREATE INDEX test_tab_b_idx ON testschema.test_tab (b);
\d testschema.test_tab_unique
Index "testschema.test_tab_unique"
Column | Type | Key? | Definition
--------+---------+------+------------
a | integer | yes | a
unique, btree, for table "testschema.test_tab"
Tablespace: "regress_tblspace"
\d testschema.test_tab_a_idx
Index "testschema.test_tab_a_idx"
Column | Type | Key? | Definition
--------+---------+------+------------
a | integer | yes | a
btree, for table "testschema.test_tab"
Tablespace: "regress_tblspace"
\d testschema.test_tab_b_idx
Index "testschema.test_tab_b_idx"
Column | Type | Key? | Definition
--------+---------+------+------------
b | integer | yes | b
btree, for table "testschema.test_tab"
ALTER TABLE testschema.test_tab ALTER b TYPE bigint, ADD UNIQUE (c);
\d testschema.test_tab_unique
Index "testschema.test_tab_unique"
Column | Type | Key? | Definition
--------+---------+------+------------
a | integer | yes | a
unique, btree, for table "testschema.test_tab"
Tablespace: "regress_tblspace"
\d testschema.test_tab_a_idx
Index "testschema.test_tab_a_idx"
Column | Type | Key? | Definition
--------+---------+------+------------
a | integer | yes | a
btree, for table "testschema.test_tab"
Tablespace: "regress_tblspace"
\d testschema.test_tab_b_idx
Index "testschema.test_tab_b_idx"
Column | Type | Key? | Definition
--------+--------+------+------------
b | bigint | yes | b
btree, for table "testschema.test_tab"
DROP TABLE testschema.test_tab;
-- let's try moving a table from one place to another
CREATE TABLE testschema.atable AS VALUES (1), (2);