diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 46d2803aa8..5c8a64d52e 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -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 */ @@ -150,6 +149,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. @@ -238,7 +240,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; @@ -888,8 +889,11 @@ transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint) * transformTableLikeClause * * Change the LIKE portion of a CREATE TABLE statement into - * column definitions which recreate the user defined column portions of - * . + * column definitions that recreate the user defined column portions of + * . 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) @@ -898,7 +902,6 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla Relation relation; TupleDesc tupleDesc; TupleConstr *constr; - AttrNumber *attmap; AclResult aclresult; char *comment; ParseCallbackState pcbstate; @@ -912,6 +915,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 && @@ -950,15 +954,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++) @@ -969,7 +968,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; @@ -1001,8 +1000,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 */ @@ -1083,113 +1080,20 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla cxt->hasoids |= relation->rd_rel->relhasoids; /* - * Copy CHECK constraints if requested, being careful to adjust attribute - * numbers so they match the child. + * 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) && - tupleDesc->constr) - { - int ccnum; - - for (ccnum = 0; ccnum < tupleDesc->constr->num_check; ccnum++) - { - char *ccname = tupleDesc->constr->check[ccnum].ccname; - char *ccbin = tupleDesc->constr->check[ccnum].ccbin; - Constraint *n = makeNode(Constraint); - Node *ccbin_node; - bool found_whole_row; - - ccbin_node = map_variable_attnos(stringToNode(ccbin), - 1, 0, - attmap, tupleDesc->natts, - InvalidOid, &found_whole_row); - - /* - * We reject whole-row variables because the whole point of LIKE - * is that the new table's rowtype might later diverge from the - * parent's. So, while translation might be possible right now, - * it wouldn't be possible to guarantee it would work in future. - */ - if (found_whole_row) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot convert whole-row table reference"), - errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".", - ccname, - RelationGetRelationName(relation)))); - - 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); - - /* Copy comment on constraint */ - if ((table_like_clause->options & CREATE_TABLE_LIKE_COMMENTS) && - (comment = GetComment(get_relation_constraint_oid(RelationGetRelid(relation), - n->conname, false), - ConstraintRelationId, - 0)) != NULL) - { - CommentStmt *stmt = makeNode(CommentStmt); - - stmt->objtype = OBJECT_TABCONSTRAINT; - stmt->object = (Node *) list_make3(makeString(cxt->relation->schemaname), - makeString(cxt->relation->relname), - makeString(n->conname)); - stmt->comment = comment; - - cxt->alist = lappend(cxt->alist, stmt); - } - } - } + if (table_like_clause->options & + (CREATE_TABLE_LIKE_CONSTRAINTS | + CREATE_TABLE_LIKE_INDEXES)) + cxt->alist = lappend(cxt->alist, table_like_clause); /* - * Likewise, copy indexes if requested - */ - if ((table_like_clause->options & CREATE_TABLE_LIKE_INDEXES) && - relation->rd_rel->relhasindex) - { - List *parent_indexes; - ListCell *l; - - parent_indexes = RelationGetIndexList(relation); - - foreach(l, parent_indexes) - { - Oid parent_index_oid = lfirst_oid(l); - Relation parent_index; - IndexStmt *index_stmt; - - parent_index = index_open(parent_index_oid, AccessShareLock); - - /* Build CREATE INDEX statement to recreate the parent_index */ - index_stmt = generateClonedIndexStmt(cxt->relation, InvalidOid, - parent_index, - attmap, tupleDesc->natts, NULL); - - /* Copy comment on index, if requested */ - if (table_like_clause->options & CREATE_TABLE_LIKE_COMMENTS) - { - comment = GetComment(parent_index_oid, RelationRelationId, 0); - - /* - * We make use of IndexStmt's idxcomment option, so as not to - * need to know now what name the index will have. - */ - index_stmt->idxcomment = comment; - } - - /* Save it in the inh_indexes list for the time being */ - cxt->inh_indexes = lappend(cxt->inh_indexes, index_stmt); - - index_close(parent_index, AccessShareLock); - } - } - - /* - * Likewise, copy extended statistics if requested + * 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) { @@ -1225,12 +1129,201 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla 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) && + constr != NULL) + { + int ccnum; + + for (ccnum = 0; ccnum < constr->num_check; ccnum++) + { + 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, + attmap, tupleDesc->natts, + InvalidOid, &found_whole_row); + + /* + * We reject whole-row variables because the whole point of LIKE + * is that the new table's rowtype might later diverge from the + * parent's. So, while translation might be possible right now, + * it wouldn't be possible to guarantee it would work in future. + */ + if (found_whole_row) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot convert whole-row table reference"), + errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".", + 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); + + /* 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) && + (comment = GetComment(get_relation_constraint_oid(RelationGetRelid(relation), + n->conname, false), + ConstraintRelationId, + 0)) != NULL) + { + CommentStmt *stmt = makeNode(CommentStmt); + + stmt->objtype = OBJECT_TABCONSTRAINT; + stmt->object = (Node *) list_make3(makeString(heapRel->schemaname), + makeString(heapRel->relname), + makeString(n->conname)); + stmt->comment = comment; + + result = lappend(result, stmt); + } + } + } + + /* + * 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) + { + List *parent_indexes; + ListCell *l; + + parent_indexes = RelationGetIndexList(relation); + + foreach(l, parent_indexes) + { + Oid parent_index_oid = lfirst_oid(l); + Relation parent_index; + IndexStmt *index_stmt; + + parent_index = index_open(parent_index_oid, AccessShareLock); + + /* Build CREATE INDEX statement to recreate the parent_index */ + index_stmt = generateClonedIndexStmt(heapRel, InvalidOid, + parent_index, + attmap, tupleDesc->natts, NULL); + + /* Copy comment on index, if requested */ + if (table_like_clause->options & CREATE_TABLE_LIKE_COMMENTS) + { + comment = GetComment(parent_index_oid, RelationRelationId, 0); + + /* + * We make use of IndexStmt's idxcomment option, so as not to + * need to know now what name the index will have. + */ + index_stmt->idxcomment = comment; + } + + result = lappend(result, index_stmt); + + index_close(parent_index, AccessShareLock); + } + } + + /* Done with child rel */ + heap_close(childrel, NoLock); + /* * 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 the child is committed. */ heap_close(relation, NoLock); + + return result; } static void @@ -1810,24 +1903,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 @@ -2962,7 +3037,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; diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 9f8321a3b2..984726ccca 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -1051,6 +1051,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 { /* @@ -1298,6 +1320,7 @@ ProcessUtilitySlow(ParseState *pstate, IndexStmt *stmt = (IndexStmt *) parsetree; Oid relid; LOCKMODE lockmode; + bool is_alter_table; if (stmt->concurrent) PreventInTransactionBlock(isTopLevel, @@ -1359,6 +1382,17 @@ ProcessUtilitySlow(ParseState *pstate, list_free(inheritors); } + /* + * 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); @@ -1370,7 +1404,7 @@ ProcessUtilitySlow(ParseState *pstate, InvalidOid, /* no predefined OID */ InvalidOid, /* no parent index */ InvalidOid, /* no parent constraint */ - false, /* is_alter_table */ + is_alter_table, true, /* check_rights */ true, /* check_not_in_use */ false, /* skip_build */ diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h index 35ac97940a..0efac550b9 100644 --- a/src/include/parser/parse_utilcmd.h +++ b/src/include/parser/parse_utilcmd.h @@ -27,6 +27,8 @@ 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); extern IndexStmt *generateClonedIndexStmt(RangeVar *heapRel, Oid heapOid, Relation source_idx, const AttrNumber *attmap, int attmap_length, diff --git a/src/test/regress/expected/create_table_like.out b/src/test/regress/expected/create_table_like.out index 8d4543bfe8..6eca4b33d3 100644 --- a/src/test/regress/expected/create_table_like.out +++ b/src/test/regress/expected/create_table_like.out @@ -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; diff --git a/src/test/regress/sql/create_table_like.sql b/src/test/regress/sql/create_table_like.sql index 42cad6826b..e4b5571ca8 100644 --- a/src/test/regress/sql/create_table_like.sql +++ b/src/test/regress/sql/create_table_like.sql @@ -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;