* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: analyze.c,v 1.179 2001/02/14 21:35:01 tgl Exp $
+ * $Id: analyze.c,v 1.180 2001/02/14 23:32:38 tgl Exp $
*
*-------------------------------------------------------------------------
*/
}
static char *
-CreateIndexName(char *table_name, char *column_name, char *label, List *indices)
+CreateIndexName(char *table_name, char *column_name,
+ char *label, List *indices)
{
int pass = 0;
char *iname = NULL;
* The type name for makeObjectName is label, or labelN if that's
* necessary to prevent collisions among multiple indexes for the same
* table. Note there is no check for collisions with already-existing
- * indexes; this ought to be rethought someday.
+ * indexes, only among the indexes we're about to create now; this ought
+ * to be improved someday.
*/
strcpy(typename, label);
{
IndexStmt *index = lfirst(ilist);
- if (strcmp(iname, index->idxname) == 0)
+ if (index->idxname != NULL &&
+ strcmp(iname, index->idxname) == 0)
break;
}
/* ran through entire list? then no name conflict found so done */
if (ilist == NIL)
break;
- /* the last one conflicted, so try a new name component */
+ /* found a conflict, so try a new name component */
pfree(iname);
sprintf(typename, "%s%d", label, ++pass);
}
constraint = makeNode(Constraint);
constraint->contype = CONSTR_UNIQUE;
- constraint->name = makeObjectName(stmt->relname,
- column->colname,
- "key");
+ constraint->name = NULL; /* assign later */
column->constraints = lappend(column->constraints,
constraint);
stmt->constraints = constraints;
/* Now run through the "deferred list" to complete the query transformation.
- * For PRIMARY KEYs, mark each column as NOT NULL and create an index.
- * For UNIQUE, create an index as for PRIMARY KEYS, but do not insist on NOT NULL.
- *
- * Note that this code does not currently look for all possible redundant cases
- * and either ignore or stop with warning. The create might fail later when
- * names for indices turn out to be duplicated, or a user might have specified
- * extra useless indices which might hurt performance. - thomas 1997-12-08
+ * For PRIMARY KEY, mark each column as NOT NULL and create an index.
+ * For UNIQUE, create an index as for PRIMARY KEY, but do not insist on
+ * NOT NULL.
*/
while (dlist != NIL)
{
if (pkey != NULL)
elog(ERROR, "CREATE TABLE/PRIMARY KEY multiple primary keys"
" for table '%s' are not allowed", stmt->relname);
- pkey = (IndexStmt *) index;
+ pkey = index;
}
if (constraint->name != NULL)
else if (constraint->contype == CONSTR_PRIMARY)
index->idxname = makeObjectName(stmt->relname, NULL, "pkey");
else
- index->idxname = NULL;
+ index->idxname = NULL; /* will set it later */
index->relname = stmt->relname;
index->accessMethod = "btree";
int count;
Assert(IsA(inh, String));
- rel = heap_openr(inh->val.str, AccessShareLock);
+ rel = heap_openr(strVal(inh), AccessShareLock);
if (rel->rd_rel->relkind != RELKIND_RELATION)
elog(ERROR, "inherited table \"%s\" is not a relation",
- inh->val.str);
+ strVal(inh));
for (count = 0; count < rel->rd_att->natts; count++)
{
Form_pg_attribute inhattr = rel->rd_att->attrs[count];
* error if the inherited column won't be NOT NULL.
* (Would a NOTICE be more reasonable?)
*/
- if (! inhattr->attnotnull)
+ if (constraint->contype == CONSTR_PRIMARY &&
+ ! inhattr->attnotnull)
elog(ERROR, "inherited attribute \"%s\" cannot be a PRIMARY KEY because it is not marked NOT NULL",
inhname);
break;
iparam->args = NIL;
iparam->class = NULL;
index->indexParams = lappend(index->indexParams, iparam);
-
- if (index->idxname == NULL)
- index->idxname = CreateIndexName(stmt->relname, iparam->name,
- "key", ilist);
}
- if (index->idxname == NULL) /* should not happen */
- elog(ERROR, "CREATE TABLE: failed to make implicit index name");
-
ilist = lappend(ilist, index);
dlist = lnext(dlist);
}
-/* OK, now finally, if there is a primary key, then make sure that there aren't any redundant
- * unique indices defined on columns. This can arise if someone specifies UNIQUE explicitly
- * or if a SERIAL column was defined along with a table PRIMARY KEY constraint.
- * - thomas 1999-05-11
- */
+ /*
+ * Scan the index list and remove any redundant index specifications.
+ * This can happen if, for instance, the user writes SERIAL PRIMARY KEY
+ * or SERIAL UNIQUE. A strict reading of SQL92 would suggest raising
+ * an error instead, but that strikes me as too anal-retentive.
+ * - tgl 2001-02-14
+ */
+ dlist = ilist;
+ ilist = NIL;
if (pkey != NULL)
{
- dlist = ilist;
- ilist = NIL;
- while (dlist != NIL)
- {
- List *pcols,
- *icols;
- int plen,
- ilen;
- int keep = TRUE;
-
- index = lfirst(dlist);
- pcols = pkey->indexParams;
- icols = index->indexParams;
+ /* Make sure we keep the PKEY index in preference to others... */
+ ilist = makeList1(pkey);
+ }
+ while (dlist != NIL)
+ {
+ index = lfirst(dlist);
- plen = length(pcols);
- ilen = length(icols);
+ /* if it's pkey, it's already in ilist */
+ if (index != pkey)
+ {
+ bool keep = true;
+ List *priorlist;
- /* Not the same as the primary key? Then we should look... */
- if ((index != pkey) && (ilen == plen))
+ foreach(priorlist, ilist)
{
- keep = FALSE;
- while ((pcols != NIL) && (icols != NIL))
- {
- IndexElem *pcol = lfirst(pcols);
- IndexElem *icol = lfirst(icols);
- char *pname = pcol->name;
- char *iname = icol->name;
+ IndexStmt *priorindex = lfirst(priorlist);
- /* different names? then no match... */
- if (strcmp(iname, pname) != 0)
- {
- keep = TRUE;
- break;
- }
- pcols = lnext(pcols);
- icols = lnext(icols);
+ if (equal(index->indexParams, priorindex->indexParams))
+ {
+ /*
+ * If the prior index is as yet unnamed, and this one
+ * is named, then transfer the name to the prior index.
+ * This ensures that if we have named and unnamed
+ * constraints, we'll use (at least one of) the names
+ * for the index.
+ */
+ if (priorindex->idxname == NULL)
+ priorindex->idxname = index->idxname;
+ keep = false;
+ break;
}
}
if (keep)
ilist = lappend(ilist, index);
- dlist = lnext(dlist);
}
+
+ dlist = lnext(dlist);
}
+ /*
+ * Finally, select unique names for all not-previously-named indices,
+ * and display notice messages.
+ */
dlist = ilist;
while (dlist != NIL)
{
index = lfirst(dlist);
+
+ if (index->idxname == NULL && index->indexParams != NIL)
+ {
+ iparam = lfirst(index->indexParams);
+ index->idxname = CreateIndexName(stmt->relname, iparam->name,
+ "key", ilist);
+ }
+ if (index->idxname == NULL) /* should not happen */
+ elog(ERROR, "CREATE TABLE: failed to make implicit index name");
+
elog(NOTICE, "CREATE TABLE/%s will create implicit index '%s' for table '%s'",
(index->primary ? "PRIMARY KEY" : "UNIQUE"),
index->idxname, stmt->relname);
+
dlist = lnext(dlist);
}
/*
* Now process the FOREIGN KEY constraints and add appropriate queries
* to the extras_after statements list.
- *
*/
if (fkconstraints != NIL)
{
List *inhRelnames=stmt->inhRelnames;
Relation rel;
foreach (inher, inhRelnames) {
- int count=0;
Value *inh=lfirst(inher);
- if (inh->type!=T_String) {
- elog(ERROR, "inherited table name list returns a non-string");
- }
- rel=heap_openr(inh->val.str, AccessShareLock);
+ int count;
+
+ Assert(IsA(inh, String));
+ rel=heap_openr(strVal(inh), AccessShareLock);
if (rel->rd_rel->relkind != RELKIND_RELATION)
elog(ERROR, "inherited table \"%s\" is not a relation",
- inh->val.str);
- for (; count<rel->rd_att->natts; count++) {
+ strVal(inh));
+ for (count = 0; count < rel->rd_att->natts; count++) {
char *name=NameStr(rel->rd_att->attrs[count]->attname);
if (strcmp(fkattr->name, name) == 0) {
found=1;