mirror of
https://github.com/postgres/postgres.git
synced 2025-05-22 00:02:02 -04:00
Repair problems with duplicate index names generated when CREATE TABLE
specifies redundant UNIQUE conditions.
This commit is contained in:
parent
4a66f9dd54
commit
c7d2ce7bc6
@ -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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|||||||
* 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)
|
|||||||
{
|
{
|
||||||
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)
|
|||||||
|
|
||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
* 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)
|
|||||||
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);
|
/* if it's pkey, it's already in ilist */
|
||||||
ilen = length(icols);
|
if (index != pkey)
|
||||||
|
{
|
||||||
|
bool keep = true;
|
||||||
|
List *priorlist;
|
||||||
|
|
||||||
/* Not the same as the primary key? Then we should look... */
|
foreach(priorlist, ilist)
|
||||||
if ((index != pkey) && (ilen == plen))
|
|
||||||
{
|
{
|
||||||
keep = FALSE;
|
IndexStmt *priorindex = lfirst(priorlist);
|
||||||
while ((pcols != NIL) && (icols != NIL))
|
|
||||||
{
|
|
||||||
IndexElem *pcol = lfirst(pcols);
|
|
||||||
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)
|
|||||||
/*
|
/*
|
||||||
* 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)
|
|||||||
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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user