*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.141 2001/08/21 16:36:01 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.142 2001/09/07 21:57:53 momjian Exp $
*
* NOTES
* The PerformAddAttribute() code, like most of the relation
#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"
#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"
break;
}
- case CONSTR_PRIMARY:
+ case CONSTR_UNIQUE:
{
-
- break;
- }
+ 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.");
}