mirror of
https://github.com/postgres/postgres.git
synced 2025-05-05 09:19:17 +03:00
Here is my much-promised patch to let people add UNIQUE constraints after
table creation time. Big deal you say - but this patch is the basis of the next thing which is adding PRIMARY KEYs after table creation time. (Which is currently impossible without twiddling catalogs) Rundown ------- * I have made the makeObjectName function of analyze.c non-static, and exported it in analyze.h * I have included analyze.h and defrem.h into command.c, to support makingObjectNames and creating indices * I removed the 'case CONSTR_PRIMARY' clause so that it properly fails and says you can't add primary keys, rather than just doing nothing and reporting nothing!!! * I have modified the docs. Algorithm --------- * If name specified is null, search for a new valid constraint name. I'm not sure if I should "lock" my generated name somehow tho - should I open the relation before doing this step? * Open relation in access exclusive mode * Check that the constraint does not already exist * Define the new index * Warn if they're doubling up on an existing index Christopher Kings-Lynne
This commit is contained in:
parent
68e190cfa4
commit
bd9b32803b
@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
$Header: /cvsroot/pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.26 2001/09/03 12:57:49 petere Exp $
|
$Header: /cvsroot/pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.27 2001/09/07 21:57:53 momjian Exp $
|
||||||
Postgres documentation
|
Postgres documentation
|
||||||
-->
|
-->
|
||||||
|
|
||||||
@ -211,9 +211,9 @@ ALTER TABLE <replaceable class="PARAMETER">table</replaceable>
|
|||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
In the current implementation, only FOREIGN KEY and CHECK constraints can
|
In the current implementation, only UNIQUE, FOREIGN KEY and CHECK constraints can
|
||||||
be added to a table. To create a unique constraint, create
|
be added to a table. To create a primary constraint, create
|
||||||
a unique index (see <xref linkend="SQL-CREATEINDEX"
|
a unique, not null index (see <xref linkend="SQL-CREATEINDEX"
|
||||||
endterm="SQL-CREATEINDEX-title">).
|
endterm="SQL-CREATEINDEX-title">).
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
@ -297,6 +297,13 @@ ALTER TABLE distributors DROP CONSTRAINT zipchk
|
|||||||
ALTER TABLE distributors ADD CONSTRAINT distfk FOREIGN KEY (address) REFERENCES addresses(address) MATCH FULL
|
ALTER TABLE distributors ADD CONSTRAINT distfk FOREIGN KEY (address) REFERENCES addresses(address) MATCH FULL
|
||||||
</programlisting>
|
</programlisting>
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
To add a (multi-column) unique constraint to a table:
|
||||||
|
<programlisting>
|
||||||
|
ALTER TABLE distributors ADD CONSTRAINT dist_id_zipcode_key UNIQUE (dist_id, zipcode)
|
||||||
|
</programlisting>
|
||||||
|
</para>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
<refsect1 id="R1-SQL-ALTERTABLE-3">
|
<refsect1 id="R1-SQL-ALTERTABLE-3">
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* NOTES
|
||||||
* The PerformAddAttribute() code, like most of the relation
|
* The PerformAddAttribute() code, like most of the relation
|
||||||
@ -32,6 +32,7 @@
|
|||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "commands/command.h"
|
#include "commands/command.h"
|
||||||
#include "commands/trigger.h"
|
#include "commands/trigger.h"
|
||||||
|
#include "commands/defrem.h" /* For add constraint unique, primary */
|
||||||
#include "executor/execdefs.h"
|
#include "executor/execdefs.h"
|
||||||
#include "executor/executor.h"
|
#include "executor/executor.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
@ -42,6 +43,7 @@
|
|||||||
#include "parser/parse_expr.h"
|
#include "parser/parse_expr.h"
|
||||||
#include "parser/parse_oper.h"
|
#include "parser/parse_oper.h"
|
||||||
#include "parser/parse_relation.h"
|
#include "parser/parse_relation.h"
|
||||||
|
#include "parser/analyze.h" /* For add constraint unique, primary */
|
||||||
#include "utils/acl.h"
|
#include "utils/acl.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/fmgroids.h"
|
#include "utils/fmgroids.h"
|
||||||
@ -1339,8 +1341,157 @@ AlterTableAddConstraint(char *relationName,
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CONSTR_PRIMARY:
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
*
|
*
|
||||||
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.197 2001/08/24 20:03:45 petere Exp $
|
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.198 2001/09/07 21:57:53 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -562,7 +562,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
|
|||||||
* from the truncated characters. Currently it seems best to keep it simple,
|
* from the truncated characters. Currently it seems best to keep it simple,
|
||||||
* so that the generated names are easily predictable by a person.
|
* so that the generated names are easily predictable by a person.
|
||||||
*/
|
*/
|
||||||
static char *
|
char *
|
||||||
makeObjectName(char *name1, char *name2, char *typename)
|
makeObjectName(char *name1, char *name2, char *typename)
|
||||||
{
|
{
|
||||||
char *name;
|
char *name;
|
||||||
|
@ -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.h,v 1.14 2001/01/24 19:43:26 momjian Exp $
|
* $Id: analyze.h,v 1.15 2001/09/07 21:57:53 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -19,4 +19,7 @@ extern List *parse_analyze(Node *parseTree, ParseState *parentParseState);
|
|||||||
|
|
||||||
extern void CheckSelectForUpdate(Query *qry);
|
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 */
|
#endif /* ANALYZE_H */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user