Index: src/backend/commands/command.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/commands/command.c,v retrieving revision 1.141 diff -c -r1.141 command.c *** src/backend/commands/command.c 2001/08/21 16:36:01 1.141 --- src/backend/commands/command.c 2001/08/23 13:13:24 *************** *** 32,37 **** --- 32,38 ---- #include "catalog/pg_type.h" #include "commands/command.h" #include "commands/trigger.h" + #include "commands/defrem.h" /* For add constraint unique, primary */ #include "executor/execdefs.h" #include "executor/executor.h" #include "miscadmin.h" *************** *** 42,47 **** --- 43,49 ---- #include "parser/parse_expr.h" #include "parser/parse_oper.h" #include "parser/parse_relation.h" + #include "parser/analyze.h" /* For add constraint unique, primary */ #include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" *************** *** 1339,1349 **** break; } ! case CONSTR_PRIMARY: { ! break; ! } default: elog(ERROR, "ALTER TABLE / ADD CONSTRAINT is not implemented for that constraint type."); } --- 1341,1500 ---- break; } ! case CONSTR_UNIQUE: { + char *iname = constr->name; + bool istemp = is_temp_rel_name(relationName); + Relation rel; + List *indexoidlist; + List *indexoidscan; + Form_pg_attribute *rel_attrs; + int num_keys = 0; + int keys_matched = 0; + bool index_found = false; + bool index_found_unique = false; + bool index_found_primary = false; + + /* If the constraint name is not specified, generate a name */ + if (iname == NULL) { + Oid indoid; + int pass = 0; + char *typename = palloc(NAMEDATALEN); + Ident *key; + + /* Assume that the length of the attr list is already > 0 */ + + /* Get the first attribute so we can use its name */ + key = (Ident *)lfirst(constr->keys); + + /* Initialise typename to 'key' */ + snprintf(typename, NAMEDATALEN, "key"); + + for (;;) + { + iname = makeObjectName(relationName, key->name, typename); + + /* Check for a conflict */ + indoid = RelnameFindRelid(iname); + + /* If the oid was not found, then we have a safe name */ + if ((!istemp && !OidIsValid(indoid)) || + (istemp && !is_temp_rel_name(iname))) + break; + + /* Found a conflict, so try a new name component */ + pfree(iname); + snprintf(typename, NAMEDATALEN, "key%d", ++pass); + } + } + + /* Need to check for unique key already on field(s) */ + rel = heap_openr(relationName, AccessExclusiveLock); + + /* + * First we check for limited correctness of the + * constraint + */ + + rel_attrs = rel->rd_att->attrs; + + /* Retrieve the oids of all indices on the relation */ + indexoidlist = RelationGetIndexList(rel); + index_found = false; + index_found_unique = false; + index_found_primary = false; + + /* Loop over all indices on the relation */ + foreach(indexoidscan, indexoidlist) + { + Oid indexoid = lfirsti(indexoidscan); + HeapTuple indexTuple; + Form_pg_index indexStruct; + List *keyl; + int i; + + indexTuple = SearchSysCache(INDEXRELID, + ObjectIdGetDatum(indexoid), + 0, 0, 0); + + if (!HeapTupleIsValid(indexTuple)) + elog(ERROR, "ALTER TABLE/ADD CONSTRAINT: Index \"%u\" not found", + indexoid); + indexStruct = (Form_pg_index) GETSTRUCT(indexTuple); + + /* + * Make sure this index has the same number of + * keys as the constraint -- It obviously won't match otherwise. + */ + for (i = 0; i < INDEX_MAX_KEYS && indexStruct->indkey[i] != 0; i++); + num_keys = length(constr->keys); + keys_matched = 0; + + if (i == num_keys) + { + /* Loop over each key in the constraint and check that there is a + corresponding key in the index. */ + i = 0; + foreach(keyl, constr->keys) + { + Ident *key = lfirst(keyl); + + /* Look at key[i] in the index and check that it is over the same column + as key[i] in the constraint. This is to differentiate between (a,b) + and (b,a) */ + if (i < INDEX_MAX_KEYS && indexStruct->indkey[i] != 0) + { + int keyno = indexStruct->indkey[i]; + + if (keyno > 0) + { + char *name = NameStr(rel_attrs[keyno - 1]->attname); + if (strcmp(name, key->name) == 0) keys_matched++; + } + } + else elog(ERROR, "ALTER TABLE/ADD CONSTRAINT: Key \"%u[%u]\" not found", indexoid, i); + i++; + } + if (keys_matched == num_keys) { + index_found = true; + index_found_unique = indexStruct->indisunique; + index_found_primary = indexStruct->indisprimary; + if (index_found_unique || index_found_primary) break; + } + } + ReleaseSysCache(indexTuple); + } + + freeList(indexoidlist); + + if (index_found_primary) + elog(ERROR, "Unique primary key already defined on relation \"%s\"", relationName); + else if (index_found_unique) + elog(ERROR, "Unique constraint already defined on the specified attributes in relation \"%s\"", relationName); + + /* If everything is ok, create the new index (constraint) */ + DefineIndex( + relationName, + iname, + "btree", + constr->keys, + true, + false, + NULL, + NIL); + + /* Issue notice */ + elog(NOTICE, "ALTER TABLE/ADD UNIQUE will create implicit index '%s' for table '%s'", + iname, relationName); + if (index_found) + elog(NOTICE, "Unique constraint supercedes existing index on relation \"%s\". Drop the existing index to remove redundancy.", relationName); + pfree(iname); ! /* Finally, close relation */ ! heap_close(rel, NoLock); ! ! break; ! } default: elog(ERROR, "ALTER TABLE / ADD CONSTRAINT is not implemented for that constraint type."); } Index: src/backend/parser/analyze.c =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/backend/parser/analyze.c,v retrieving revision 1.196 diff -c -r1.196 analyze.c *** src/backend/parser/analyze.c 2001/08/21 16:36:03 1.196 --- src/backend/parser/analyze.c 2001/08/23 13:14:34 *************** *** 562,568 **** * from the truncated characters. Currently it seems best to keep it simple, * so that the generated names are easily predictable by a person. */ ! static char * makeObjectName(char *name1, char *name2, char *typename) { char *name; --- 562,568 ---- * from the truncated characters. Currently it seems best to keep it simple, * so that the generated names are easily predictable by a person. */ ! char * makeObjectName(char *name1, char *name2, char *typename) { char *name; Index: src/include/parser/analyze.h =================================================================== RCS file: /home/projects/pgsql/cvsroot/pgsql/src/include/parser/analyze.h,v retrieving revision 1.14 diff -c -r1.14 analyze.h *** src/include/parser/analyze.h 2001/01/24 19:43:26 1.14 --- src/include/parser/analyze.h 2001/08/23 13:14:34 *************** *** 19,22 **** --- 19,25 ---- extern void CheckSelectForUpdate(Query *qry); + /* This was exported to allow ADD CONSTRAINT to make use of it */ + extern char *makeObjectName(char *name1, char *name2, char *typename); + #endif /* ANALYZE_H */