mirror of
https://github.com/postgres/postgres.git
synced 2025-05-08 07:21:33 +03:00
Fix handling of CREATE TABLE LIKE with inheritance.
If a CREATE TABLE command uses both LIKE and traditional inheritance, Vars in CHECK constraints and expression indexes that are absorbed from a LIKE parent table tended to get mis-numbered, resulting in wrong answers and/or bizarre error messages (though probably not any actual crashes, thanks to validation occurring in the executor). In v12 and up, the same could happen to Vars in GENERATED expressions, even in cases with no LIKE clause but multiple traditional-inheritance parents. The cause of the problem for LIKE is that parse_utilcmd.c supposed it could renumber such Vars correctly during transformCreateStmt(), which it cannot since we have not yet accounted for columns added via inheritance. Fix that by postponing processing of LIKE INCLUDING CONSTRAINTS, DEFAULTS, GENERATED, INDEXES till after we've performed DefineRelation(). The error with GENERATED and multiple inheritance is a simple oversight in MergeAttributes(); it knows it has to renumber Vars in inherited CHECK constraints, but forgot to apply the same processing to inherited GENERATED expressions (a/k/a defaults). Per bug #16272 from Tom Gottfried. The non-GENERATED variants of the issue are ancient, presumably dating right back to the addition of CREATE TABLE LIKE; hence back-patch to all supported branches. Discussion: https://postgr.es/m/16272-6e32da020e9a9381@postgresql.org
This commit is contained in:
parent
3a45ac076a
commit
e22e29c258
@ -85,7 +85,6 @@ typedef struct
|
||||
List *ckconstraints; /* CHECK constraints */
|
||||
List *fkconstraints; /* FOREIGN KEY constraints */
|
||||
List *ixconstraints; /* index-creating constraints */
|
||||
List *inh_indexes; /* cloned indexes from INCLUDING INDEXES */
|
||||
List *extstats; /* cloned extended statistics */
|
||||
List *blist; /* "before list" of things to do before
|
||||
* creating the table */
|
||||
@ -120,7 +119,7 @@ static void transformTableLikeClause(CreateStmtContext *cxt,
|
||||
TableLikeClause *table_like_clause);
|
||||
static void transformOfType(CreateStmtContext *cxt,
|
||||
TypeName *ofTypename);
|
||||
static IndexStmt *generateClonedIndexStmt(CreateStmtContext *cxt,
|
||||
static IndexStmt *generateClonedIndexStmt(RangeVar *heapRel,
|
||||
Relation source_idx,
|
||||
const AttrNumber *attmap, int attmap_length);
|
||||
static CreateStatsStmt *generateClonedExtStatsStmt(RangeVar *heapRel,
|
||||
@ -153,6 +152,9 @@ static Const *transformPartitionBoundValue(ParseState *pstate, A_Const *con,
|
||||
* Returns a List of utility commands to be done in sequence. One of these
|
||||
* will be the transformed CreateStmt, but there may be additional actions
|
||||
* to be done before and after the actual DefineRelation() call.
|
||||
* In addition to normal utility commands such as AlterTableStmt and
|
||||
* IndexStmt, the result list may contain TableLikeClause(s), representing
|
||||
* the need to perform additional parse analysis after DefineRelation().
|
||||
*
|
||||
* SQL allows constraints to be scattered all over, so thumb through
|
||||
* the columns and collect all constraints into one place.
|
||||
@ -241,7 +243,6 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
|
||||
cxt.ckconstraints = NIL;
|
||||
cxt.fkconstraints = NIL;
|
||||
cxt.ixconstraints = NIL;
|
||||
cxt.inh_indexes = NIL;
|
||||
cxt.extstats = NIL;
|
||||
cxt.blist = NIL;
|
||||
cxt.alist = NIL;
|
||||
@ -926,8 +927,11 @@ transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint)
|
||||
* transformTableLikeClause
|
||||
*
|
||||
* Change the LIKE <srctable> portion of a CREATE TABLE statement into
|
||||
* column definitions which recreate the user defined column portions of
|
||||
* <srctable>.
|
||||
* column definitions that recreate the user defined column portions of
|
||||
* <srctable>. Also, if there are any LIKE options that we can't fully
|
||||
* process at this point, add the TableLikeClause to cxt->alist, which
|
||||
* will cause utility.c to call expandTableLikeClause() after the new
|
||||
* table has been created.
|
||||
*/
|
||||
static void
|
||||
transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_clause)
|
||||
@ -936,7 +940,6 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
|
||||
Relation relation;
|
||||
TupleDesc tupleDesc;
|
||||
TupleConstr *constr;
|
||||
AttrNumber *attmap;
|
||||
AclResult aclresult;
|
||||
char *comment;
|
||||
ParseCallbackState pcbstate;
|
||||
@ -950,6 +953,7 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("LIKE is not supported for creating foreign tables")));
|
||||
|
||||
/* Open the relation referenced by the LIKE clause */
|
||||
relation = relation_openrv(table_like_clause->relation, AccessShareLock);
|
||||
|
||||
if (relation->rd_rel->relkind != RELKIND_RELATION &&
|
||||
@ -988,15 +992,10 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
|
||||
tupleDesc = RelationGetDescr(relation);
|
||||
constr = tupleDesc->constr;
|
||||
|
||||
/*
|
||||
* Initialize column number map for map_variable_attnos(). We need this
|
||||
* since dropped columns in the source table aren't copied, so the new
|
||||
* table can have different column numbers.
|
||||
*/
|
||||
attmap = (AttrNumber *) palloc0(sizeof(AttrNumber) * tupleDesc->natts);
|
||||
|
||||
/*
|
||||
* Insert the copied attributes into the cxt for the new table definition.
|
||||
* We must do this now so that they appear in the table in the relative
|
||||
* position where the LIKE clause is, as required by SQL99.
|
||||
*/
|
||||
for (parent_attno = 1; parent_attno <= tupleDesc->natts;
|
||||
parent_attno++)
|
||||
@ -1006,7 +1005,7 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
|
||||
ColumnDef *def;
|
||||
|
||||
/*
|
||||
* Ignore dropped columns in the parent. attmap entry is left zero.
|
||||
* Ignore dropped columns in the parent.
|
||||
*/
|
||||
if (attribute->attisdropped)
|
||||
continue;
|
||||
@ -1038,8 +1037,6 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
|
||||
*/
|
||||
cxt->columns = lappend(cxt->columns, def);
|
||||
|
||||
attmap[parent_attno - 1] = list_length(cxt->columns);
|
||||
|
||||
/*
|
||||
* Copy default, if present and the default has been requested
|
||||
*/
|
||||
@ -1119,22 +1116,118 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
|
||||
/* We use oids if at least one LIKE'ed table has oids. */
|
||||
cxt->hasoids |= relation->rd_rel->relhasoids;
|
||||
|
||||
/*
|
||||
* We cannot yet deal with CHECK constraints or indexes, since we don't
|
||||
* yet know what column numbers the copied columns will have in the
|
||||
* finished table. If any of those options are specified, add the LIKE
|
||||
* clause to cxt->alist so that expandTableLikeClause will be called after
|
||||
* we do know that.
|
||||
*/
|
||||
if (table_like_clause->options &
|
||||
(CREATE_TABLE_LIKE_CONSTRAINTS |
|
||||
CREATE_TABLE_LIKE_INDEXES))
|
||||
cxt->alist = lappend(cxt->alist, table_like_clause);
|
||||
|
||||
/*
|
||||
* We may copy extended statistics if requested, since the representation
|
||||
* of CreateStatsStmt doesn't depend on column numbers.
|
||||
*/
|
||||
if (table_like_clause->options & CREATE_TABLE_LIKE_STATISTICS)
|
||||
{
|
||||
List *parent_extstats;
|
||||
ListCell *l;
|
||||
|
||||
parent_extstats = RelationGetStatExtList(relation);
|
||||
|
||||
foreach(l, parent_extstats)
|
||||
{
|
||||
Oid parent_stat_oid = lfirst_oid(l);
|
||||
CreateStatsStmt *stats_stmt;
|
||||
|
||||
stats_stmt = generateClonedExtStatsStmt(cxt->relation,
|
||||
RelationGetRelid(relation),
|
||||
parent_stat_oid);
|
||||
cxt->extstats = lappend(cxt->extstats, stats_stmt);
|
||||
|
||||
/*
|
||||
* We'd like to clone the comments too, but we lack the support
|
||||
* code to do it.
|
||||
*/
|
||||
}
|
||||
|
||||
list_free(parent_extstats);
|
||||
}
|
||||
|
||||
/*
|
||||
* Close the parent rel, but keep our AccessShareLock on it until xact
|
||||
* commit. That will prevent someone else from deleting or ALTERing the
|
||||
* parent before we can run expandTableLikeClause.
|
||||
*/
|
||||
heap_close(relation, NoLock);
|
||||
}
|
||||
|
||||
/*
|
||||
* expandTableLikeClause
|
||||
*
|
||||
* Process LIKE options that require knowing the final column numbers
|
||||
* assigned to the new table's columns. This executes after we have
|
||||
* run DefineRelation for the new table. It returns a list of utility
|
||||
* commands that should be run to generate indexes etc.
|
||||
*/
|
||||
List *
|
||||
expandTableLikeClause(RangeVar *heapRel, TableLikeClause *table_like_clause)
|
||||
{
|
||||
List *result = NIL;
|
||||
List *atsubcmds = NIL;
|
||||
Relation relation;
|
||||
Relation childrel;
|
||||
TupleDesc tupleDesc;
|
||||
TupleConstr *constr;
|
||||
AttrNumber *attmap;
|
||||
char *comment;
|
||||
|
||||
/*
|
||||
* Open the relation referenced by the LIKE clause. We should still have
|
||||
* the table lock obtained by transformTableLikeClause (and this'll throw
|
||||
* an assertion failure if not). Hence, no need to recheck privileges
|
||||
* etc.
|
||||
*/
|
||||
relation = relation_openrv(table_like_clause->relation, NoLock);
|
||||
|
||||
tupleDesc = RelationGetDescr(relation);
|
||||
constr = tupleDesc->constr;
|
||||
|
||||
/*
|
||||
* Open the newly-created child relation; we have lock on that too.
|
||||
*/
|
||||
childrel = relation_openrv(heapRel, NoLock);
|
||||
|
||||
/*
|
||||
* Construct a map from the LIKE relation's attnos to the child rel's.
|
||||
* This re-checks type match etc, although it shouldn't be possible to
|
||||
* have a failure since both tables are locked.
|
||||
*/
|
||||
attmap = convert_tuples_by_name_map(RelationGetDescr(childrel),
|
||||
tupleDesc,
|
||||
gettext_noop("could not convert row type"));
|
||||
|
||||
/*
|
||||
* Copy CHECK constraints if requested, being careful to adjust attribute
|
||||
* numbers so they match the child.
|
||||
*/
|
||||
if ((table_like_clause->options & CREATE_TABLE_LIKE_CONSTRAINTS) &&
|
||||
tupleDesc->constr)
|
||||
constr != NULL)
|
||||
{
|
||||
int ccnum;
|
||||
|
||||
for (ccnum = 0; ccnum < tupleDesc->constr->num_check; ccnum++)
|
||||
for (ccnum = 0; ccnum < constr->num_check; ccnum++)
|
||||
{
|
||||
char *ccname = tupleDesc->constr->check[ccnum].ccname;
|
||||
char *ccbin = tupleDesc->constr->check[ccnum].ccbin;
|
||||
Constraint *n = makeNode(Constraint);
|
||||
char *ccname = constr->check[ccnum].ccname;
|
||||
char *ccbin = constr->check[ccnum].ccbin;
|
||||
Node *ccbin_node;
|
||||
bool found_whole_row;
|
||||
Constraint *n;
|
||||
AlterTableCmd *atsubcmd;
|
||||
|
||||
ccbin_node = map_variable_attnos(stringToNode(ccbin),
|
||||
1, 0,
|
||||
@ -1155,12 +1248,21 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
|
||||
ccname,
|
||||
RelationGetRelationName(relation))));
|
||||
|
||||
n = makeNode(Constraint);
|
||||
n->contype = CONSTR_CHECK;
|
||||
n->location = -1;
|
||||
n->conname = pstrdup(ccname);
|
||||
n->raw_expr = NULL;
|
||||
n->cooked_expr = nodeToString(ccbin_node);
|
||||
cxt->ckconstraints = lappend(cxt->ckconstraints, n);
|
||||
|
||||
/* We can skip validation, since the new table should be empty. */
|
||||
n->skip_validation = true;
|
||||
n->initially_valid = true;
|
||||
|
||||
atsubcmd = makeNode(AlterTableCmd);
|
||||
atsubcmd->subtype = AT_AddConstraint;
|
||||
atsubcmd->def = (Node *) n;
|
||||
atsubcmds = lappend(atsubcmds, atsubcmd);
|
||||
|
||||
/* Copy comment on constraint */
|
||||
if ((table_like_clause->options & CREATE_TABLE_LIKE_COMMENTS) &&
|
||||
@ -1172,18 +1274,34 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
|
||||
CommentStmt *stmt = makeNode(CommentStmt);
|
||||
|
||||
stmt->objtype = OBJECT_TABCONSTRAINT;
|
||||
stmt->object = (Node *) list_make3(makeString(cxt->relation->schemaname),
|
||||
makeString(cxt->relation->relname),
|
||||
stmt->object = (Node *) list_make3(makeString(heapRel->schemaname),
|
||||
makeString(heapRel->relname),
|
||||
makeString(n->conname));
|
||||
stmt->comment = comment;
|
||||
|
||||
cxt->alist = lappend(cxt->alist, stmt);
|
||||
result = lappend(result, stmt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Likewise, copy indexes if requested
|
||||
* If we generated any ALTER TABLE actions above, wrap them into a single
|
||||
* ALTER TABLE command. Stick it at the front of the result, so it runs
|
||||
* before any CommentStmts we made above.
|
||||
*/
|
||||
if (atsubcmds)
|
||||
{
|
||||
AlterTableStmt *atcmd = makeNode(AlterTableStmt);
|
||||
|
||||
atcmd->relation = copyObject(heapRel);
|
||||
atcmd->cmds = atsubcmds;
|
||||
atcmd->relkind = OBJECT_TABLE;
|
||||
atcmd->missing_ok = false;
|
||||
result = lcons(atcmd, result);
|
||||
}
|
||||
|
||||
/*
|
||||
* Process indexes if required.
|
||||
*/
|
||||
if ((table_like_clause->options & CREATE_TABLE_LIKE_INDEXES) &&
|
||||
relation->rd_rel->relhasindex)
|
||||
@ -1202,7 +1320,7 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
|
||||
parent_index = index_open(parent_index_oid, AccessShareLock);
|
||||
|
||||
/* Build CREATE INDEX statement to recreate the parent_index */
|
||||
index_stmt = generateClonedIndexStmt(cxt, parent_index,
|
||||
index_stmt = generateClonedIndexStmt(heapRel, parent_index,
|
||||
attmap, tupleDesc->natts);
|
||||
|
||||
/* Copy comment on index, if requested */
|
||||
@ -1217,41 +1335,14 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
|
||||
index_stmt->idxcomment = comment;
|
||||
}
|
||||
|
||||
/* Save it in the inh_indexes list for the time being */
|
||||
cxt->inh_indexes = lappend(cxt->inh_indexes, index_stmt);
|
||||
result = lappend(result, index_stmt);
|
||||
|
||||
index_close(parent_index, AccessShareLock);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Likewise, copy extended statistics if requested
|
||||
*/
|
||||
if (table_like_clause->options & CREATE_TABLE_LIKE_STATISTICS)
|
||||
{
|
||||
List *parent_extstats;
|
||||
ListCell *l;
|
||||
|
||||
parent_extstats = RelationGetStatExtList(relation);
|
||||
|
||||
foreach(l, parent_extstats)
|
||||
{
|
||||
Oid parent_stat_oid = lfirst_oid(l);
|
||||
CreateStatsStmt *stats_stmt;
|
||||
|
||||
stats_stmt = generateClonedExtStatsStmt(cxt->relation,
|
||||
RelationGetRelid(relation),
|
||||
parent_stat_oid);
|
||||
cxt->extstats = lappend(cxt->extstats, stats_stmt);
|
||||
|
||||
/*
|
||||
* We'd like to clone the comments too, but we lack the support
|
||||
* code to do it.
|
||||
*/
|
||||
}
|
||||
|
||||
list_free(parent_extstats);
|
||||
}
|
||||
/* Done with child rel */
|
||||
heap_close(childrel, NoLock);
|
||||
|
||||
/*
|
||||
* Close the parent rel, but keep our AccessShareLock on it until xact
|
||||
@ -1259,6 +1350,8 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
|
||||
* parent before the child is committed.
|
||||
*/
|
||||
heap_close(relation, NoLock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1311,7 +1404,7 @@ transformOfType(CreateStmtContext *cxt, TypeName *ofTypename)
|
||||
* "source_idx". Attribute numbers should be adjusted according to attmap.
|
||||
*/
|
||||
static IndexStmt *
|
||||
generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx,
|
||||
generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
|
||||
const AttrNumber *attmap, int attmap_length)
|
||||
{
|
||||
Oid source_relid = RelationGetRelid(source_idx);
|
||||
@ -1368,7 +1461,7 @@ generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx,
|
||||
|
||||
/* Begin building the IndexStmt */
|
||||
index = makeNode(IndexStmt);
|
||||
index->relation = cxt->relation;
|
||||
index->relation = heapRel;
|
||||
index->accessMethod = pstrdup(NameStr(amrec->amname));
|
||||
if (OidIsValid(idxrelrec->reltablespace))
|
||||
index->tableSpace = get_tablespace_name(idxrelrec->reltablespace);
|
||||
@ -1795,24 +1888,6 @@ transformIndexConstraints(CreateStmtContext *cxt)
|
||||
indexlist = lappend(indexlist, index);
|
||||
}
|
||||
|
||||
/* Add in any indexes defined by LIKE ... INCLUDING INDEXES */
|
||||
foreach(lc, cxt->inh_indexes)
|
||||
{
|
||||
index = (IndexStmt *) lfirst(lc);
|
||||
|
||||
if (index->primary)
|
||||
{
|
||||
if (cxt->pkey != NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
|
||||
errmsg("multiple primary keys for table \"%s\" are not allowed",
|
||||
cxt->relation->relname)));
|
||||
cxt->pkey = index;
|
||||
}
|
||||
|
||||
indexlist = lappend(indexlist, index);
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan the index list and remove any redundant index specifications. This
|
||||
* can happen if, for instance, the user writes UNIQUE PRIMARY KEY. A
|
||||
@ -2837,7 +2912,6 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
|
||||
cxt.ckconstraints = NIL;
|
||||
cxt.fkconstraints = NIL;
|
||||
cxt.ixconstraints = NIL;
|
||||
cxt.inh_indexes = NIL;
|
||||
cxt.extstats = NIL;
|
||||
cxt.blist = NIL;
|
||||
cxt.alist = NIL;
|
||||
|
@ -1059,6 +1059,28 @@ ProcessUtilitySlow(ParseState *pstate,
|
||||
secondaryObject,
|
||||
stmt);
|
||||
}
|
||||
else if (IsA(stmt, TableLikeClause))
|
||||
{
|
||||
/*
|
||||
* Do delayed processing of LIKE options. This
|
||||
* will result in additional sub-statements for us
|
||||
* to process. We can just tack those onto the
|
||||
* to-do list.
|
||||
*/
|
||||
TableLikeClause *like = (TableLikeClause *) stmt;
|
||||
RangeVar *rv = ((CreateStmt *) parsetree)->relation;
|
||||
List *morestmts;
|
||||
|
||||
morestmts = expandTableLikeClause(rv, like);
|
||||
stmts = list_concat(stmts, morestmts);
|
||||
|
||||
/*
|
||||
* We don't need a CCI now, besides which the "l"
|
||||
* list pointer is now possibly invalid, so just
|
||||
* skip the CCI test below.
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
@ -1306,6 +1328,7 @@ ProcessUtilitySlow(ParseState *pstate,
|
||||
IndexStmt *stmt = (IndexStmt *) parsetree;
|
||||
Oid relid;
|
||||
LOCKMODE lockmode;
|
||||
bool is_alter_table;
|
||||
|
||||
if (stmt->concurrent)
|
||||
PreventTransactionChain(isTopLevel,
|
||||
@ -1328,6 +1351,17 @@ ProcessUtilitySlow(ParseState *pstate,
|
||||
RangeVarCallbackOwnsRelation,
|
||||
NULL);
|
||||
|
||||
/*
|
||||
* If the IndexStmt is already transformed, it must have
|
||||
* come from generateClonedIndexStmt, which in current
|
||||
* usage means it came from expandTableLikeClause rather
|
||||
* than from original parse analysis. And that means we
|
||||
* must treat it like ALTER TABLE ADD INDEX, not CREATE.
|
||||
* (This is a bit grotty, but currently it doesn't seem
|
||||
* worth adding a separate bool field for the purpose.)
|
||||
*/
|
||||
is_alter_table = stmt->transformed;
|
||||
|
||||
/* Run parse analysis ... */
|
||||
stmt = transformIndexStmt(relid, stmt, queryString);
|
||||
|
||||
@ -1337,7 +1371,7 @@ ProcessUtilitySlow(ParseState *pstate,
|
||||
DefineIndex(relid, /* OID of heap relation */
|
||||
stmt,
|
||||
InvalidOid, /* no predefined OID */
|
||||
false, /* is_alter_table */
|
||||
is_alter_table,
|
||||
true, /* check_rights */
|
||||
true, /* check_not_in_use */
|
||||
false, /* skip_build */
|
||||
|
@ -27,5 +27,7 @@ extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
|
||||
extern List *transformCreateSchemaStmt(CreateSchemaStmt *stmt);
|
||||
extern PartitionBoundSpec *transformPartitionBound(ParseState *pstate, Relation parent,
|
||||
PartitionBoundSpec *spec);
|
||||
extern List *expandTableLikeClause(RangeVar *heapRel,
|
||||
TableLikeClause *table_like_clause);
|
||||
|
||||
#endif /* PARSE_UTILCMD_H */
|
||||
|
@ -113,6 +113,35 @@ SELECT * FROM test_like_id_3; -- identity was copied and applied
|
||||
(1 row)
|
||||
|
||||
DROP TABLE test_like_id_1, test_like_id_2, test_like_id_3;
|
||||
-- Test renumbering of Vars when combining LIKE with inheritance
|
||||
CREATE TABLE test_like_4 (b int DEFAULT 42,
|
||||
c int NOT NULL,
|
||||
a int CHECK (a > 0));
|
||||
CREATE TABLE test_like_5 (x point, y point, z point);
|
||||
CREATE TABLE test_like_5x (p int CHECK (p > 0),
|
||||
q int DEFAULT 99);
|
||||
CREATE TABLE test_like_5c (LIKE test_like_4 INCLUDING ALL)
|
||||
INHERITS (test_like_5, test_like_5x);
|
||||
\d test_like_5c
|
||||
Table "public.test_like_5c"
|
||||
Column | Type | Collation | Nullable | Default
|
||||
--------+---------+-----------+----------+---------
|
||||
x | point | | |
|
||||
y | point | | |
|
||||
z | point | | |
|
||||
p | integer | | |
|
||||
q | integer | | | 99
|
||||
b | integer | | | 42
|
||||
c | integer | | not null |
|
||||
a | integer | | |
|
||||
Check constraints:
|
||||
"test_like_4_a_check" CHECK (a > 0)
|
||||
"test_like_5x_p_check" CHECK (p > 0)
|
||||
Inherits: test_like_5,
|
||||
test_like_5x
|
||||
|
||||
DROP TABLE test_like_4;
|
||||
DROP TABLE test_like_5, test_like_5x, test_like_5c;
|
||||
CREATE TABLE inhg (x text, LIKE inhx INCLUDING INDEXES, y text); /* copies indexes */
|
||||
INSERT INTO inhg VALUES (5, 10);
|
||||
INSERT INTO inhg VALUES (20, 10); -- should fail
|
||||
@ -148,9 +177,10 @@ ALTER TABLE ctlt1 ALTER COLUMN a SET STORAGE MAIN;
|
||||
CREATE TABLE ctlt2 (c text);
|
||||
ALTER TABLE ctlt2 ALTER COLUMN c SET STORAGE EXTERNAL;
|
||||
COMMENT ON COLUMN ctlt2.c IS 'C';
|
||||
CREATE TABLE ctlt3 (a text CHECK (length(a) < 5), c text);
|
||||
CREATE TABLE ctlt3 (a text CHECK (length(a) < 5), c text CHECK (length(c) < 7));
|
||||
ALTER TABLE ctlt3 ALTER COLUMN c SET STORAGE EXTERNAL;
|
||||
ALTER TABLE ctlt3 ALTER COLUMN a SET STORAGE MAIN;
|
||||
CREATE INDEX ctlt3_fnidx ON ctlt3 ((a || c));
|
||||
COMMENT ON COLUMN ctlt3.a IS 'A3';
|
||||
COMMENT ON COLUMN ctlt3.c IS 'C';
|
||||
COMMENT ON CONSTRAINT ctlt3_a_check ON ctlt3 IS 't3_a_check';
|
||||
@ -206,10 +236,11 @@ NOTICE: merging multiple inherited definitions of column "a"
|
||||
Check constraints:
|
||||
"ctlt1_a_check" CHECK (length(a) > 2)
|
||||
"ctlt3_a_check" CHECK (length(a) < 5)
|
||||
"ctlt3_c_check" CHECK (length(c) < 7)
|
||||
Inherits: ctlt1,
|
||||
ctlt3
|
||||
|
||||
CREATE TABLE ctlt13_like (LIKE ctlt3 INCLUDING CONSTRAINTS INCLUDING COMMENTS INCLUDING STORAGE) INHERITS (ctlt1);
|
||||
CREATE TABLE ctlt13_like (LIKE ctlt3 INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING COMMENTS INCLUDING STORAGE) INHERITS (ctlt1);
|
||||
NOTICE: merging column "a" with inherited definition
|
||||
\d+ ctlt13_like
|
||||
Table "public.ctlt13_like"
|
||||
@ -218,9 +249,12 @@ NOTICE: merging column "a" with inherited definition
|
||||
a | text | | not null | | main | | A3
|
||||
b | text | | | | extended | |
|
||||
c | text | | | | external | | C
|
||||
Indexes:
|
||||
"ctlt13_like_expr_idx" btree ((a || c))
|
||||
Check constraints:
|
||||
"ctlt1_a_check" CHECK (length(a) > 2)
|
||||
"ctlt3_a_check" CHECK (length(a) < 5)
|
||||
"ctlt3_c_check" CHECK (length(c) < 7)
|
||||
Inherits: ctlt1
|
||||
|
||||
SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_constraint'::regclass AND objoid = c.oid AND c.conrelid = 'ctlt13_like'::regclass;
|
||||
|
@ -51,6 +51,19 @@ INSERT INTO test_like_id_3 (b) VALUES ('b3');
|
||||
SELECT * FROM test_like_id_3; -- identity was copied and applied
|
||||
DROP TABLE test_like_id_1, test_like_id_2, test_like_id_3;
|
||||
|
||||
-- Test renumbering of Vars when combining LIKE with inheritance
|
||||
CREATE TABLE test_like_4 (b int DEFAULT 42,
|
||||
c int NOT NULL,
|
||||
a int CHECK (a > 0));
|
||||
CREATE TABLE test_like_5 (x point, y point, z point);
|
||||
CREATE TABLE test_like_5x (p int CHECK (p > 0),
|
||||
q int DEFAULT 99);
|
||||
CREATE TABLE test_like_5c (LIKE test_like_4 INCLUDING ALL)
|
||||
INHERITS (test_like_5, test_like_5x);
|
||||
\d test_like_5c
|
||||
DROP TABLE test_like_4;
|
||||
DROP TABLE test_like_5, test_like_5x, test_like_5c;
|
||||
|
||||
CREATE TABLE inhg (x text, LIKE inhx INCLUDING INDEXES, y text); /* copies indexes */
|
||||
INSERT INTO inhg VALUES (5, 10);
|
||||
INSERT INTO inhg VALUES (20, 10); -- should fail
|
||||
@ -84,9 +97,10 @@ CREATE TABLE ctlt2 (c text);
|
||||
ALTER TABLE ctlt2 ALTER COLUMN c SET STORAGE EXTERNAL;
|
||||
COMMENT ON COLUMN ctlt2.c IS 'C';
|
||||
|
||||
CREATE TABLE ctlt3 (a text CHECK (length(a) < 5), c text);
|
||||
CREATE TABLE ctlt3 (a text CHECK (length(a) < 5), c text CHECK (length(c) < 7));
|
||||
ALTER TABLE ctlt3 ALTER COLUMN c SET STORAGE EXTERNAL;
|
||||
ALTER TABLE ctlt3 ALTER COLUMN a SET STORAGE MAIN;
|
||||
CREATE INDEX ctlt3_fnidx ON ctlt3 ((a || c));
|
||||
COMMENT ON COLUMN ctlt3.a IS 'A3';
|
||||
COMMENT ON COLUMN ctlt3.c IS 'C';
|
||||
COMMENT ON CONSTRAINT ctlt3_a_check ON ctlt3 IS 't3_a_check';
|
||||
@ -103,7 +117,7 @@ CREATE TABLE ctlt1_inh (LIKE ctlt1 INCLUDING CONSTRAINTS INCLUDING COMMENTS) INH
|
||||
SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_constraint'::regclass AND objoid = c.oid AND c.conrelid = 'ctlt1_inh'::regclass;
|
||||
CREATE TABLE ctlt13_inh () INHERITS (ctlt1, ctlt3);
|
||||
\d+ ctlt13_inh
|
||||
CREATE TABLE ctlt13_like (LIKE ctlt3 INCLUDING CONSTRAINTS INCLUDING COMMENTS INCLUDING STORAGE) INHERITS (ctlt1);
|
||||
CREATE TABLE ctlt13_like (LIKE ctlt3 INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING COMMENTS INCLUDING STORAGE) INHERITS (ctlt1);
|
||||
\d+ ctlt13_like
|
||||
SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_constraint'::regclass AND objoid = c.oid AND c.conrelid = 'ctlt13_like'::regclass;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user