mirror of
https://github.com/postgres/postgres.git
synced 2025-07-08 11:42:09 +03:00
Fix tablespace inheritance for partitioned rels
Commitca4103025d
left a few loose ends. The most important one (broken pg_dump output) is already fixed by virtue of commit3b23552ad8
, 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:
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user