From 8f2f180ff10034494d947162d080363aab554cfa Mon Sep 17 00:00:00 2001 From: Tom Lane <tgl@sss.pgh.pa.us> Date: Wed, 11 Oct 2006 16:42:59 +0000 Subject: [PATCH] Code review for LIKE INCLUDING CONSTRAINTS patch --- improve comments, don't cheat on the raw-vs-cooked status of a constraint. --- src/backend/commands/tablecmds.c | 197 ++++++++++++++++++------------- src/backend/parser/analyze.c | 27 +++-- src/include/nodes/parsenodes.h | 24 ++-- 3 files changed, 142 insertions(+), 106 deletions(-) diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 9ad11b6f329..631b02e0fcb 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.204 2006/10/06 17:13:59 petere Exp $ + * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.205 2006/10/11 16:42:58 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -163,6 +163,8 @@ static List *MergeAttributes(List *schema, List *supers, bool istemp, List **supOids, List **supconstr, int *supOidCount); static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel); static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel); +static void add_nonduplicate_constraint(Constraint *cdef, + ConstrCheck *check, int *ncheck); static bool change_varattnos_walker(Node *node, const AttrNumber *newattno); static void StoreCatalogInheritance(Oid relationId, List *supers); static void StoreCatalogInheritance1(Oid relationId, Oid parentOid, @@ -285,7 +287,6 @@ DefineRelation(CreateStmt *stmt, char relkind) List *rawDefaults; Datum reloptions; ListCell *listptr; - int i; AttrNumber attnum; /* @@ -378,49 +379,35 @@ DefineRelation(CreateStmt *stmt, char relkind) localHasOids = interpretOidsOption(stmt->options); descriptor->tdhasoid = (localHasOids || parentOidCount > 0); - if (old_constraints != NIL) + if (old_constraints || stmt->constraints) { - ConstrCheck *check = (ConstrCheck *) - palloc0(list_length(old_constraints) * sizeof(ConstrCheck)); + ConstrCheck *check; int ncheck = 0; + /* make array that's certainly big enough */ + check = (ConstrCheck *) + palloc((list_length(old_constraints) + + list_length(stmt->constraints)) * sizeof(ConstrCheck)); + /* deal with constraints from MergeAttributes */ foreach(listptr, old_constraints) { Constraint *cdef = (Constraint *) lfirst(listptr); - bool dup = false; - if (cdef->contype != CONSTR_CHECK) - continue; - Assert(cdef->name != NULL); - Assert(cdef->raw_expr == NULL && cdef->cooked_expr != NULL); + if (cdef->contype == CONSTR_CHECK) + add_nonduplicate_constraint(cdef, check, &ncheck); + } + /* + * analyze.c might have passed some precooked constraints too, + * due to LIKE tab INCLUDING CONSTRAINTS + */ + foreach(listptr, stmt->constraints) + { + Constraint *cdef = (Constraint *) lfirst(listptr); - /* - * In multiple-inheritance situations, it's possible to inherit - * the same grandparent constraint through multiple parents. - * Hence, discard inherited constraints that match as to both name - * and expression. Otherwise, gripe if the names conflict. - */ - for (i = 0; i < ncheck; i++) - { - if (strcmp(check[i].ccname, cdef->name) != 0) - continue; - if (strcmp(check[i].ccbin, cdef->cooked_expr) == 0) - { - dup = true; - break; - } - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_OBJECT), - errmsg("duplicate check constraint name \"%s\"", - cdef->name))); - } - if (!dup) - { - check[ncheck].ccname = cdef->name; - check[ncheck].ccbin = pstrdup(cdef->cooked_expr); - ncheck++; - } + if (cdef->contype == CONSTR_CHECK && cdef->cooked_expr != NULL) + add_nonduplicate_constraint(cdef, check, &ncheck); } + /* if we found any, insert 'em into the descriptor */ if (ncheck > 0) { if (descriptor->constr == NULL) @@ -1118,66 +1105,57 @@ MergeAttributes(List *schema, List *supers, bool istemp, return schema; } + /* - * Varattnos of pg_constraint.conbin must be rewritten when subclasses inherit - * constraints from parent classes, since the inherited attributes could - * be given different column numbers in multiple-inheritance cases. - * - * Note that the passed node tree is modified in place! - * - * This function is used elsewhere such as in analyze.c - * + * In multiple-inheritance situations, it's possible to inherit + * the same grandparent constraint through multiple parents. + * Hence, we want to discard inherited constraints that match as to + * both name and expression. Otherwise, gripe if there are conflicting + * names. Nonconflicting constraints are added to the array check[] + * of length *ncheck ... caller must ensure there is room! */ - -void -change_varattnos_of_a_node(Node *node, const AttrNumber *newattno) +static void +add_nonduplicate_constraint(Constraint *cdef, ConstrCheck *check, int *ncheck) { - change_varattnos_walker(node, newattno); -} - -/* Generate a map for change_varattnos_of_a_node from two tupledesc's. */ + int i; -AttrNumber * -varattnos_map(TupleDesc old, TupleDesc new) -{ - int i, - j; - AttrNumber *attmap = palloc0(sizeof(AttrNumber) * old->natts); + /* Should only see precooked constraints here */ + Assert(cdef->contype == CONSTR_CHECK); + Assert(cdef->name != NULL); + Assert(cdef->raw_expr == NULL && cdef->cooked_expr != NULL); - for (i = 1; i <= old->natts; i++) + for (i = 0; i < *ncheck; i++) { - if (old->attrs[i - 1]->attisdropped) - { - attmap[i - 1] = 0; + if (strcmp(check[i].ccname, cdef->name) != 0) continue; - } - for (j = 1; j <= new->natts; j++) - if (!strcmp(NameStr(old->attrs[i - 1]->attname), NameStr(new->attrs[j - 1]->attname))) - attmap[i - 1] = j; + if (strcmp(check[i].ccbin, cdef->cooked_expr) == 0) + return; /* duplicate constraint, so ignore it */ + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("duplicate check constraint name \"%s\"", + cdef->name))); } - return attmap; + /* No match on name, so add it to array */ + check[*ncheck].ccname = cdef->name; + check[*ncheck].ccbin = pstrdup(cdef->cooked_expr); + (*ncheck)++; } + /* - * Generate a map for change_varattnos_of_a_node from a tupledesc and a list of - * ColumnDefs + * Replace varattno values in an expression tree according to the given + * map array, that is, varattno N is replaced by newattno[N-1]. It is + * caller's responsibility to ensure that the array is long enough to + * define values for all user varattnos present in the tree. System column + * attnos remain unchanged. + * + * Note that the passed node tree is modified in-place! */ -AttrNumber * -varattnos_map_schema(TupleDesc old, List *schema) +void +change_varattnos_of_a_node(Node *node, const AttrNumber *newattno) { - int i; - AttrNumber *attmap = palloc0(sizeof(AttrNumber) * old->natts); - - for (i = 1; i <= old->natts; i++) - { - if (old->attrs[i - 1]->attisdropped) - { - attmap[i - 1] = 0; - continue; - } - attmap[i - 1] = findAttrByName(NameStr(old->attrs[i - 1]->attname), schema); - } - return attmap; + /* no setup needed, so away we go */ + (void) change_varattnos_walker(node, newattno); } static bool @@ -1206,6 +1184,59 @@ change_varattnos_walker(Node *node, const AttrNumber *newattno) (void *) newattno); } +/* + * Generate a map for change_varattnos_of_a_node from old and new TupleDesc's, + * matching according to column name. + */ +AttrNumber * +varattnos_map(TupleDesc old, TupleDesc new) +{ + AttrNumber *attmap; + int i, + j; + + attmap = (AttrNumber *) palloc0(sizeof(AttrNumber) * old->natts); + for (i = 1; i <= old->natts; i++) + { + if (old->attrs[i - 1]->attisdropped) + continue; /* leave the entry as zero */ + + for (j = 1; j <= new->natts; j++) + { + if (strcmp(NameStr(old->attrs[i - 1]->attname), + NameStr(new->attrs[j - 1]->attname)) == 0) + { + attmap[i - 1] = j; + break; + } + } + } + return attmap; +} + +/* + * Generate a map for change_varattnos_of_a_node from a TupleDesc and a list + * of ColumnDefs + */ +AttrNumber * +varattnos_map_schema(TupleDesc old, List *schema) +{ + AttrNumber *attmap; + int i; + + attmap = (AttrNumber *) palloc0(sizeof(AttrNumber) * old->natts); + for (i = 1; i <= old->natts; i++) + { + if (old->attrs[i - 1]->attisdropped) + continue; /* leave the entry as zero */ + + attmap[i - 1] = findAttrByName(NameStr(old->attrs[i - 1]->attname), + schema); + } + return attmap; +} + + /* * StoreCatalogInheritance * Updates the system catalogs with proper inheritance information. diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index f4e58a3b8d1..0df3bec1694 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.352 2006/10/04 00:29:55 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.353 2006/10/11 16:42:59 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1286,12 +1286,10 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt, InhRelation *inhRelation) { AttrNumber parent_attno; - Relation relation; TupleDesc tupleDesc; TupleConstr *constr; AclResult aclresult; - bool including_defaults = false; bool including_constraints = false; bool including_indexes = false; @@ -1342,15 +1340,18 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt, including_indexes = false; break; default: - elog(ERROR, "unrecognized CREATE TABLE LIKE option: %d", option); + elog(ERROR, "unrecognized CREATE TABLE LIKE option: %d", + option); } } if (including_indexes) - elog(ERROR, "TODO"); + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("LIKE INCLUDING INDEXES is not implemented"))); /* - * Insert the inherited attributes into the cxt for the new table + * Insert the copied attributes into the cxt for the new table * definition. */ for (parent_attno = 1; parent_attno <= tupleDesc->natts; @@ -1367,7 +1368,7 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt, continue; /* - * Create a new inherited column. + * Create a new column, which is marked as NOT inherited. * * For constraints, ONLY the NOT NULL constraint is inherited by the * new column definition per SQL99. @@ -1389,7 +1390,7 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt, cxt->columns = lappend(cxt->columns, def); /* - * Copy default if any, and the default has been requested + * Copy default, if present and the default has been requested */ if (attribute->atthasdef && including_defaults) { @@ -1419,10 +1420,14 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt, } } + /* + * Copy CHECK constraints if requested, being careful to adjust + * attribute numbers + */ if (including_constraints && tupleDesc->constr) { - int ccnum; AttrNumber *attmap = varattnos_map_schema(tupleDesc, cxt->columns); + int ccnum; for (ccnum = 0; ccnum < tupleDesc->constr->num_check; ccnum++) { @@ -1435,8 +1440,8 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt, n->contype = CONSTR_CHECK; n->name = pstrdup(ccname); - n->raw_expr = ccbin_node; - n->cooked_expr = NULL; + n->raw_expr = NULL; + n->cooked_expr = nodeToString(ccbin_node); n->indexspace = NULL; cxt->ckconstraints = lappend(cxt->ckconstraints, (Node *) n); } diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 29f68f7fff3..8afe97cb1e2 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.331 2006/10/04 00:30:09 momjian Exp $ + * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.332 2006/10/11 16:42:59 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -414,9 +414,19 @@ typedef struct InhRelation { NodeTag type; RangeVar *relation; - List *options; + List *options; /* integer List of CreateStmtLikeOption */ } InhRelation; +typedef enum CreateStmtLikeOption +{ + CREATE_TABLE_LIKE_INCLUDING_DEFAULTS, + CREATE_TABLE_LIKE_EXCLUDING_DEFAULTS, + CREATE_TABLE_LIKE_INCLUDING_CONSTRAINTS, + CREATE_TABLE_LIKE_EXCLUDING_CONSTRAINTS, + CREATE_TABLE_LIKE_INCLUDING_INDEXES, + CREATE_TABLE_LIKE_EXCLUDING_INDEXES +} CreateStmtLikeOption; + /* * IndexElem - index parameters (used in CREATE INDEX) * @@ -1055,16 +1065,6 @@ typedef struct CreateStmt char *tablespacename; /* table space to use, or NULL */ } CreateStmt; -typedef enum CreateStmtLikeOption -{ - CREATE_TABLE_LIKE_INCLUDING_DEFAULTS, - CREATE_TABLE_LIKE_EXCLUDING_DEFAULTS, - CREATE_TABLE_LIKE_INCLUDING_CONSTRAINTS, - CREATE_TABLE_LIKE_EXCLUDING_CONSTRAINTS, - CREATE_TABLE_LIKE_INCLUDING_INDEXES, - CREATE_TABLE_LIKE_EXCLUDING_INDEXES -} CreateStmtLikeOption; - /* ---------- * Definitions for plain (non-FOREIGN KEY) constraints in CreateStmt * -- GitLab