Commit c7d2ce7b authored by Tom Lane's avatar Tom Lane

Repair problems with duplicate index names generated when CREATE TABLE

specifies redundant UNIQUE conditions.
parent 4a66f9dd
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -606,7 +606,8 @@ makeObjectName(char *name1, char *name2, char *typename) ...@@ -606,7 +606,8 @@ makeObjectName(char *name1, char *name2, char *typename)
} }
static char * 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; int pass = 0;
char *iname = NULL; char *iname = NULL;
...@@ -617,7 +618,8 @@ CreateIndexName(char *table_name, char *column_name, char *label, List *indices) ...@@ -617,7 +618,8 @@ CreateIndexName(char *table_name, char *column_name, char *label, List *indices)
* The type name for makeObjectName is label, or labelN if that's * The type name for makeObjectName is label, or labelN if that's
* necessary to prevent collisions among multiple indexes for the same * necessary to prevent collisions among multiple indexes for the same
* table. Note there is no check for collisions with already-existing * 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); strcpy(typename, label);
...@@ -629,14 +631,15 @@ CreateIndexName(char *table_name, char *column_name, char *label, List *indices) ...@@ -629,14 +631,15 @@ CreateIndexName(char *table_name, char *column_name, char *label, List *indices)
{ {
IndexStmt *index = lfirst(ilist); IndexStmt *index = lfirst(ilist);
if (strcmp(iname, index->idxname) == 0) if (index->idxname != NULL &&
strcmp(iname, index->idxname) == 0)
break; break;
} }
/* ran through entire list? then no name conflict found so done */ /* ran through entire list? then no name conflict found so done */
if (ilist == NIL) if (ilist == NIL)
break; break;
/* the last one conflicted, so try a new name component */ /* found a conflict, so try a new name component */
pfree(iname); pfree(iname);
sprintf(typename, "%s%d", label, ++pass); sprintf(typename, "%s%d", label, ++pass);
} }
...@@ -745,9 +748,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) ...@@ -745,9 +748,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
constraint = makeNode(Constraint); constraint = makeNode(Constraint);
constraint->contype = CONSTR_UNIQUE; constraint->contype = CONSTR_UNIQUE;
constraint->name = makeObjectName(stmt->relname, constraint->name = NULL; /* assign later */
column->colname,
"key");
column->constraints = lappend(column->constraints, column->constraints = lappend(column->constraints,
constraint); constraint);
...@@ -919,13 +920,9 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) ...@@ -919,13 +920,9 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
stmt->constraints = constraints; stmt->constraints = constraints;
/* Now run through the "deferred list" to complete the query transformation. /* 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 PRIMARY KEY, 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. * For UNIQUE, create an index as for PRIMARY KEY, 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
*/ */
while (dlist != NIL) while (dlist != NIL)
{ {
...@@ -943,7 +940,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) ...@@ -943,7 +940,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
if (pkey != NULL) if (pkey != NULL)
elog(ERROR, "CREATE TABLE/PRIMARY KEY multiple primary keys" elog(ERROR, "CREATE TABLE/PRIMARY KEY multiple primary keys"
" for table '%s' are not allowed", stmt->relname); " for table '%s' are not allowed", stmt->relname);
pkey = (IndexStmt *) index; pkey = index;
} }
if (constraint->name != NULL) if (constraint->name != NULL)
...@@ -951,7 +948,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) ...@@ -951,7 +948,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
else if (constraint->contype == CONSTR_PRIMARY) else if (constraint->contype == CONSTR_PRIMARY)
index->idxname = makeObjectName(stmt->relname, NULL, "pkey"); index->idxname = makeObjectName(stmt->relname, NULL, "pkey");
else else
index->idxname = NULL; index->idxname = NULL; /* will set it later */
index->relname = stmt->relname; index->relname = stmt->relname;
index->accessMethod = "btree"; index->accessMethod = "btree";
...@@ -995,10 +992,10 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) ...@@ -995,10 +992,10 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
int count; int count;
Assert(IsA(inh, String)); Assert(IsA(inh, String));
rel = heap_openr(inh->val.str, AccessShareLock); rel = heap_openr(strVal(inh), AccessShareLock);
if (rel->rd_rel->relkind != RELKIND_RELATION) if (rel->rd_rel->relkind != RELKIND_RELATION)
elog(ERROR, "inherited table \"%s\" is not a relation", elog(ERROR, "inherited table \"%s\" is not a relation",
inh->val.str); strVal(inh));
for (count = 0; count < rel->rd_att->natts; count++) for (count = 0; count < rel->rd_att->natts; count++)
{ {
Form_pg_attribute inhattr = rel->rd_att->attrs[count]; Form_pg_attribute inhattr = rel->rd_att->attrs[count];
...@@ -1020,7 +1017,8 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) ...@@ -1020,7 +1017,8 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
* error if the inherited column won't be NOT NULL. * error if the inherited column won't be NOT NULL.
* (Would a NOTICE be more reasonable?) * (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", elog(ERROR, "inherited attribute \"%s\" cannot be a PRIMARY KEY because it is not marked NOT NULL",
inhname); inhname);
break; break;
...@@ -1041,78 +1039,85 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) ...@@ -1041,78 +1039,85 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
iparam->args = NIL; iparam->args = NIL;
iparam->class = NULL; iparam->class = NULL;
index->indexParams = lappend(index->indexParams, iparam); 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); ilist = lappend(ilist, index);
dlist = lnext(dlist); 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 * Scan the index list and remove any redundant index specifications.
* or if a SERIAL column was defined along with a table PRIMARY KEY constraint. * This can happen if, for instance, the user writes SERIAL PRIMARY KEY
* - thomas 1999-05-11 * 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
*/ */
if (pkey != NULL)
{
dlist = ilist; dlist = ilist;
ilist = NIL; ilist = NIL;
if (pkey != NULL)
{
/* Make sure we keep the PKEY index in preference to others... */
ilist = makeList1(pkey);
}
while (dlist != NIL) while (dlist != NIL)
{ {
List *pcols,
*icols;
int plen,
ilen;
int keep = TRUE;
index = lfirst(dlist); index = lfirst(dlist);
pcols = pkey->indexParams;
icols = index->indexParams;
plen = length(pcols);
ilen = length(icols);
/* Not the same as the primary key? Then we should look... */ /* if it's pkey, it's already in ilist */
if ((index != pkey) && (ilen == plen)) if (index != pkey)
{ {
keep = FALSE; bool keep = true;
while ((pcols != NIL) && (icols != NIL)) List *priorlist;
foreach(priorlist, ilist)
{ {
IndexElem *pcol = lfirst(pcols); IndexStmt *priorindex = lfirst(priorlist);
IndexElem *icol = lfirst(icols);
char *pname = pcol->name;
char *iname = icol->name;
/* different names? then no match... */ if (equal(index->indexParams, priorindex->indexParams))
if (strcmp(iname, pname) != 0)
{ {
keep = TRUE; /*
* 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; break;
} }
pcols = lnext(pcols);
icols = lnext(icols);
}
} }
if (keep) if (keep)
ilist = lappend(ilist, index); 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; dlist = ilist;
while (dlist != NIL) while (dlist != NIL)
{ {
index = lfirst(dlist); 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'", elog(NOTICE, "CREATE TABLE/%s will create implicit index '%s' for table '%s'",
(index->primary ? "PRIMARY KEY" : "UNIQUE"), (index->primary ? "PRIMARY KEY" : "UNIQUE"),
index->idxname, stmt->relname); index->idxname, stmt->relname);
dlist = lnext(dlist); dlist = lnext(dlist);
} }
...@@ -1123,7 +1128,6 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) ...@@ -1123,7 +1128,6 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
/* /*
* Now process the FOREIGN KEY constraints and add appropriate queries * Now process the FOREIGN KEY constraints and add appropriate queries
* to the extras_after statements list. * to the extras_after statements list.
*
*/ */
if (fkconstraints != NIL) if (fkconstraints != NIL)
{ {
...@@ -1173,16 +1177,15 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) ...@@ -1173,16 +1177,15 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
List *inhRelnames=stmt->inhRelnames; List *inhRelnames=stmt->inhRelnames;
Relation rel; Relation rel;
foreach (inher, inhRelnames) { foreach (inher, inhRelnames) {
int count=0;
Value *inh=lfirst(inher); Value *inh=lfirst(inher);
if (inh->type!=T_String) { int count;
elog(ERROR, "inherited table name list returns a non-string");
} Assert(IsA(inh, String));
rel=heap_openr(inh->val.str, AccessShareLock); rel=heap_openr(strVal(inh), AccessShareLock);
if (rel->rd_rel->relkind != RELKIND_RELATION) if (rel->rd_rel->relkind != RELKIND_RELATION)
elog(ERROR, "inherited table \"%s\" is not a relation", elog(ERROR, "inherited table \"%s\" is not a relation",
inh->val.str); strVal(inh));
for (; count<rel->rd_att->natts; count++) { for (count = 0; count < rel->rd_att->natts; count++) {
char *name=NameStr(rel->rd_att->attrs[count]->attname); char *name=NameStr(rel->rd_att->attrs[count]->attname);
if (strcmp(fkattr->name, name) == 0) { if (strcmp(fkattr->name, name) == 0) {
found=1; found=1;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment