aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands/creatinh.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2001-03-30 20:50:36 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2001-03-30 20:50:36 +0000
commit648c09181eae5459ba39b2f8310c274158783aa9 (patch)
treee68dee175f4a9b2416cd4757298212064046147a /src/backend/commands/creatinh.c
parent188e0fe4576734c6bf7ab7fc7fd96a2d2d443e70 (diff)
downloadpostgresql-648c09181eae5459ba39b2f8310c274158783aa9.tar.gz
postgresql-648c09181eae5459ba39b2f8310c274158783aa9.zip
Re-allow specification of a new default value for an inherited column
in CREATE TABLE, but give a warning notice. Clean up inconsistent handling of defaults and NOT NULL flags from multiply-inherited columns. Per pghackers discussion 28-Mar through 30-Mar.
Diffstat (limited to 'src/backend/commands/creatinh.c')
-rw-r--r--src/backend/commands/creatinh.c334
1 files changed, 194 insertions, 140 deletions
diff --git a/src/backend/commands/creatinh.c b/src/backend/commands/creatinh.c
index 602ceb54328..7aaaa3cdebf 100644
--- a/src/backend/commands/creatinh.c
+++ b/src/backend/commands/creatinh.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/Attic/creatinh.c,v 1.74 2001/03/22 06:16:11 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/Attic/creatinh.c,v 1.75 2001/03/30 20:50:36 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -34,11 +34,11 @@
* ----------------
*/
-static int checkAttrExists(const char *attributeName,
- const char *attributeType, List *schema);
static List *MergeAttributes(List *schema, List *supers, bool istemp,
List **supOids, List **supconstr);
+static bool change_varattnos_of_a_node(Node *node, const AttrNumber *newattno);
static void StoreCatalogInheritance(Oid relationId, List *supers);
+static int findAttrByName(const char *attributeName, List *schema);
static void setRelhassubclassInRelation(Oid relationId, bool relhassubclass);
@@ -63,9 +63,10 @@ DefineRelation(CreateStmt *stmt, char relkind)
int i;
AttrNumber attnum;
- if (strlen(stmt->relname) >= NAMEDATALEN)
- elog(ERROR, "the relation name %s is >= %d characters long",
- stmt->relname, NAMEDATALEN);
+ /*
+ * Truncate relname to appropriate length (probably a waste of time,
+ * as parser should have done this already).
+ */
StrNCpy(relname, stmt->relname, NAMEDATALEN);
/*
@@ -80,7 +81,7 @@ DefineRelation(CreateStmt *stmt, char relkind)
elog(ERROR, "DefineRelation: please inherit from a relation or define an attribute");
/*
- * create a relation descriptor from the relation schema and create
+ * Create a relation descriptor from the relation schema and create
* the relation. Note that in this stage only inherited (pre-cooked)
* defaults and constraints will be included into the new relation.
* (BuildDescForRelation takes care of the inherited defaults, but we
@@ -234,51 +235,11 @@ TruncateRelation(char *name)
heap_truncate(name);
}
-/*
- * complementary static functions for MergeAttributes().
- * Varattnos of pg_relcheck.rcbin should be rewritten when
- * subclasses inherit the constraints from the super class.
- * Note that these functions rewrite varattnos while walking
- * through a node tree.
- */
-static bool
-change_varattnos_walker(Node *node, const AttrNumber *newattno)
-{
- if (node == NULL)
- return false;
- if (IsA(node, Var))
- {
- Var *var = (Var *) node;
-
- if (var->varlevelsup == 0 && var->varno == 1)
- {
-
- /*
- * ??? the following may be a problem when the node is
- * multiply referenced though stringToNode() doesn't create
- * such a node currently.
- */
- Assert(newattno[var->varattno - 1] > 0);
- var->varattno = newattno[var->varattno - 1];
- }
- return false;
- }
- return expression_tree_walker(node, change_varattnos_walker,
- (void *) newattno);
-}
-
-static bool
-change_varattnos_of_a_node(Node *node, const AttrNumber *newattno)
-{
- return change_varattnos_walker(node, newattno);
-}
-
-/*
+/*----------
* MergeAttributes
- * Returns new schema given initial schema and supers.
+ * Returns new schema given initial schema and superclasses.
*
* Input arguments:
- *
* 'schema' is the column/attribute definition for the table. (It's a list
* of ColumnDef's.) It is destructively changed.
* 'supers' is a list of names (as Value objects) of parent relations.
@@ -286,7 +247,11 @@ change_varattnos_of_a_node(Node *node, const AttrNumber *newattno)
*
* Output arguments:
* 'supOids' receives an integer list of the OIDs of the parent relations.
- * 'supconstr' receives a list of constraints belonging to the parents.
+ * 'supconstr' receives a list of constraints belonging to the parents,
+ * updated as necessary to be valid for the child.
+ *
+ * Return value:
+ * Completed schema list.
*
* Notes:
* The order in which the attributes are inherited is very important.
@@ -301,14 +266,17 @@ change_varattnos_of_a_node(Node *node, const AttrNumber *newattno)
* create table student (gpa float8) inherits (person);
* create table stud_emp (percent int4) inherits (emp, student);
*
- * the order of the attributes of stud_emp is as follow:
- *
+ * The order of the attributes of stud_emp is:
*
* person {1:name, 2:age, 3:location}
* / \
* {6:gpa} student emp {4:salary, 5:manager}
* \ /
* stud_emp {7:percent}
+ *
+ * If the same attribute name appears multiple times, then it appears
+ * in the result table in the proper location for its first appearance.
+ *----------
*/
static List *
MergeAttributes(List *schema, List *supers, bool istemp,
@@ -318,11 +286,14 @@ MergeAttributes(List *schema, List *supers, bool istemp,
List *inhSchema = NIL;
List *parentOids = NIL;
List *constraints = NIL;
- int attnums;
+ int child_attno;
/*
- * Validates that there are no duplications. Validity checking of
- * types occurs later.
+ * Check for duplicate names in the explicit list of attributes.
+ *
+ * Although we might consider merging such entries in the same way that
+ * we handle name conflicts for inherited attributes, it seems to make
+ * more sense to assume such conflicts are errors.
*/
foreach(entry, schema)
{
@@ -331,10 +302,6 @@ MergeAttributes(List *schema, List *supers, bool istemp,
foreach(rest, lnext(entry))
{
-
- /*
- * check for duplicated names within the new relation
- */
ColumnDef *restdef = lfirst(rest);
if (strcmp(coldef->colname, restdef->colname) == 0)
@@ -344,6 +311,9 @@ MergeAttributes(List *schema, List *supers, bool istemp,
}
}
}
+ /*
+ * Reject duplicate names in the list of parents, too.
+ */
foreach(entry, supers)
{
List *rest;
@@ -359,23 +329,18 @@ MergeAttributes(List *schema, List *supers, bool istemp,
}
/*
- * merge the inherited attributes into the schema
+ * Scan the parents left-to-right, and merge their attributes to form
+ * a list of inherited attributes (inhSchema).
*/
- attnums = 0;
+ child_attno = 0;
foreach(entry, supers)
{
char *name = strVal(lfirst(entry));
Relation relation;
- List *partialResult = NIL;
- AttrNumber attrno;
TupleDesc tupleDesc;
TupleConstr *constr;
- AttrNumber *newattno,
- *partialAttidx;
- Node *expr;
- int i,
- attidx,
- attno_exist;
+ AttrNumber *newattno;
+ AttrNumber parent_attno;
relation = heap_openr(name, AccessShareLock);
@@ -394,33 +359,30 @@ MergeAttributes(List *schema, List *supers, bool istemp,
parentOids = lappendi(parentOids, relation->rd_id);
setRelhassubclassInRelation(relation->rd_id, true);
+
tupleDesc = RelationGetDescr(relation);
- /* allocate a new attribute number table and initialize */
- newattno = (AttrNumber *) palloc(tupleDesc->natts * sizeof(AttrNumber));
- for (i = 0; i < tupleDesc->natts; i++)
- newattno[i] = 0;
+ constr = tupleDesc->constr;
/*
- * searching and storing order are different. another table is
- * needed.
+ * newattno[] will contain the child-table attribute numbers for
+ * the attributes of this parent table. (They are not the same
+ * for parents after the first one.)
*/
- partialAttidx = (AttrNumber *) palloc(tupleDesc->natts * sizeof(AttrNumber));
- for (i = 0; i < tupleDesc->natts; i++)
- partialAttidx[i] = 0;
- constr = tupleDesc->constr;
+ newattno = (AttrNumber *) palloc(tupleDesc->natts*sizeof(AttrNumber));
- attidx = 0;
- for (attrno = relation->rd_rel->relnatts - 1; attrno >= 0; attrno--)
+ for (parent_attno = 1; parent_attno <= tupleDesc->natts;
+ parent_attno++)
{
- Form_pg_attribute attribute = tupleDesc->attrs[attrno];
+ Form_pg_attribute attribute = tupleDesc->attrs[parent_attno - 1];
char *attributeName;
char *attributeType;
HeapTuple tuple;
+ int exist_attno;
ColumnDef *def;
TypeName *typename;
/*
- * form name, type and constraints
+ * Get name and type name of attribute
*/
attributeName = NameStr(attribute->attname);
tuple = SearchSysCache(TYPEOID,
@@ -433,65 +395,78 @@ MergeAttributes(List *schema, List *supers, bool istemp,
ReleaseSysCache(tuple);
/*
- * check validity
- *
+ * Does it conflict with some previously inherited column?
*/
- if (checkAttrExists(attributeName, attributeType, schema) != 0)
- elog(ERROR, "CREATE TABLE: attribute \"%s\" already exists in inherited schema",
+ exist_attno = findAttrByName(attributeName, inhSchema);
+ if (exist_attno > 0)
+ {
+ /*
+ * Yes, try to merge the two column definitions.
+ * They must have the same type and typmod.
+ */
+ elog(NOTICE, "CREATE TABLE: merging multiple inherited definitions of attribute \"%s\"",
attributeName);
-
- if (0 < (attno_exist = checkAttrExists(attributeName, attributeType, inhSchema)))
+ def = (ColumnDef *) nth(exist_attno - 1, inhSchema);
+ if (strcmp(def->typename->name, attributeType) != 0 ||
+ def->typename->typmod != attribute->atttypmod)
+ elog(ERROR, "CREATE TABLE: inherited attribute \"%s\" type conflict (%s and %s)",
+ attributeName, def->typename->name, attributeType);
+ /* Merge of NOT NULL constraints = OR 'em together */
+ def->is_not_null |= attribute->attnotnull;
+ /* Default and other constraints are handled below */
+ newattno[parent_attno - 1] = exist_attno;
+ }
+ else
{
-
/*
- * this entry already exists
+ * No, create a new inherited column
*/
- newattno[attribute->attnum - 1] = attno_exist;
- continue;
+ def = makeNode(ColumnDef);
+ def->colname = pstrdup(attributeName);
+ typename = makeNode(TypeName);
+ typename->name = attributeType;
+ typename->typmod = attribute->atttypmod;
+ def->typename = typename;
+ def->is_not_null = attribute->attnotnull;
+ def->is_sequence = false;
+ def->raw_default = NULL;
+ def->cooked_default = NULL;
+ def->constraints = NIL;
+ inhSchema = lappend(inhSchema, def);
+ newattno[parent_attno - 1] = ++child_attno;
}
- attidx++;
- partialAttidx[attribute->attnum - 1] = attidx;
-
/*
- * add an entry to the schema
+ * Copy default if any, overriding any default from earlier parent
*/
- def = makeNode(ColumnDef);
- typename = makeNode(TypeName);
- def->colname = pstrdup(attributeName);
- typename->name = pstrdup(attributeType);
- typename->typmod = attribute->atttypmod;
- def->typename = typename;
- def->is_not_null = attribute->attnotnull;
- def->raw_default = NULL;
- def->cooked_default = NULL;
if (attribute->atthasdef)
{
AttrDefault *attrdef;
int i;
- Assert(constr != NULL);
+ def->raw_default = NULL;
+ def->cooked_default = NULL;
+ Assert(constr != NULL);
attrdef = constr->defval;
for (i = 0; i < constr->num_defval; i++)
{
- if (attrdef[i].adnum == attrno + 1)
+ if (attrdef[i].adnum == parent_attno)
{
+ /*
+ * if default expr could contain any vars, we'd
+ * need to fix 'em, but it can't ...
+ */
def->cooked_default = pstrdup(attrdef[i].adbin);
break;
}
}
Assert(def->cooked_default != NULL);
}
- partialResult = lcons(def, partialResult);
}
- for (i = 0; i < tupleDesc->natts; i++)
- {
- if (partialAttidx[i] > 0)
- newattno[i] = attnums + attidx + 1 - partialAttidx[i];
- }
- attnums += attidx;
- pfree(partialAttidx);
-
+ /*
+ * Now copy the constraints of this parent, adjusting attnos using
+ * the completed newattno[] map
+ */
if (constr && constr->num_check > 0)
{
ConstrCheck *check = constr->check;
@@ -500,6 +475,7 @@ MergeAttributes(List *schema, List *supers, bool istemp,
for (i = 0; i < constr->num_check; i++)
{
Constraint *cdef = makeNode(Constraint);
+ Node *expr;
cdef->contype = CONSTR_CHECK;
if (check[i].ccname[0] == '$')
@@ -514,6 +490,7 @@ MergeAttributes(List *schema, List *supers, bool istemp,
constraints = lappend(constraints, cdef);
}
}
+
pfree(newattno);
/*
@@ -522,18 +499,61 @@ MergeAttributes(List *schema, List *supers, bool istemp,
* ALTERing the parent before the child is committed.
*/
heap_close(relation, NoLock);
-
- /*
- * wants the inherited schema to appear in the order they are
- * specified in CREATE TABLE
- */
- inhSchema = nconc(inhSchema, partialResult);
}
/*
- * put the inherited schema before our the schema for this table
+ * If we had no inherited attributes, the result schema is just the
+ * explicitly declared columns. Otherwise, we need to merge the
+ * declared columns into the inherited schema list.
*/
- schema = nconc(inhSchema, schema);
+ if (inhSchema != NIL)
+ {
+ foreach(entry, schema)
+ {
+ ColumnDef *newdef = lfirst(entry);
+ char *attributeName = newdef->colname;
+ char *attributeType = newdef->typename->name;
+ int exist_attno;
+
+ /*
+ * Does it conflict with some previously inherited column?
+ */
+ exist_attno = findAttrByName(attributeName, inhSchema);
+ if (exist_attno > 0)
+ {
+ ColumnDef *def;
+
+ /*
+ * Yes, try to merge the two column definitions.
+ * They must have the same type and typmod.
+ */
+ elog(NOTICE, "CREATE TABLE: merging attribute \"%s\" with inherited definition",
+ attributeName);
+ def = (ColumnDef *) nth(exist_attno - 1, inhSchema);
+ if (strcmp(def->typename->name, attributeType) != 0 ||
+ def->typename->typmod != newdef->typename->typmod)
+ elog(ERROR, "CREATE TABLE: attribute \"%s\" type conflict (%s and %s)",
+ attributeName, def->typename->name, attributeType);
+ /* Merge of NOT NULL constraints = OR 'em together */
+ def->is_not_null |= newdef->is_not_null;
+ /* If new def has a default, override previous default */
+ if (newdef->raw_default != NULL)
+ {
+ def->raw_default = newdef->raw_default;
+ def->cooked_default = newdef->cooked_default;
+ }
+ }
+ else
+ {
+ /*
+ * No, attach new column to result schema
+ */
+ inhSchema = lappend(inhSchema, newdef);
+ }
+ }
+
+ schema = inhSchema;
+ }
*supOids = parentOids;
*supconstr = constraints;
@@ -541,6 +561,48 @@ MergeAttributes(List *schema, List *supers, bool istemp,
}
/*
+ * complementary static functions for MergeAttributes().
+ *
+ * Varattnos of pg_relcheck.rcbin 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!
+ */
+static bool
+change_varattnos_walker(Node *node, const AttrNumber *newattno)
+{
+ if (node == NULL)
+ return false;
+ if (IsA(node, Var))
+ {
+ Var *var = (Var *) node;
+
+ if (var->varlevelsup == 0 && var->varno == 1 &&
+ var->varattno > 0)
+ {
+
+ /*
+ * ??? the following may be a problem when the node is
+ * multiply referenced though stringToNode() doesn't create
+ * such a node currently.
+ */
+ Assert(newattno[var->varattno - 1] > 0);
+ var->varattno = newattno[var->varattno - 1];
+ }
+ return false;
+ }
+ return expression_tree_walker(node, change_varattnos_walker,
+ (void *) newattno);
+}
+
+static bool
+change_varattnos_of_a_node(Node *node, const AttrNumber *newattno)
+{
+ return change_varattnos_walker(node, newattno);
+}
+
+/*
* StoreCatalogInheritance
* Updates the system catalogs with proper inheritance information.
*
@@ -716,15 +778,14 @@ again:
heap_close(relation, RowExclusiveLock);
}
-
-
/*
- * returns the index (starting with 1) if attribute already exists in schema,
+ * Look for an existing schema entry with the given name.
+ *
+ * Returns the index (starting with 1) if attribute already exists in schema,
* 0 if it doesn't.
*/
static int
-checkAttrExists(const char *attributeName, const char *attributeType,
- List *schema)
+findAttrByName(const char *attributeName, List *schema)
{
List *s;
int i = 0;
@@ -735,21 +796,14 @@ checkAttrExists(const char *attributeName, const char *attributeType,
++i;
if (strcmp(attributeName, def->colname) == 0)
- {
-
- /*
- * attribute exists. Make sure the types are the same.
- */
- if (strcmp(attributeType, def->typename->name) != 0)
- elog(ERROR, "CREATE TABLE: attribute \"%s\" type conflict (%s and %s)",
- attributeName, attributeType, def->typename->name);
return i;
- }
}
return 0;
}
-
+/*
+ * Update a relation's pg_class.relhassubclass entry to the given value
+ */
static void
setRelhassubclassInRelation(Oid relationId, bool relhassubclass)
{