aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/parse_utilcmd.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2009-07-29 20:56:21 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2009-07-29 20:56:21 +0000
commit25d9bf2e3e66ee2e546c5c523d148ecab6ee1dcc (patch)
treeb0dee0f1d6111fd6658d432ec30e5ddb88adc02f /src/backend/parser/parse_utilcmd.c
parent850490579318ff52097eec92ce535357dd0c7a3a (diff)
downloadpostgresql-25d9bf2e3e66ee2e546c5c523d148ecab6ee1dcc.tar.gz
postgresql-25d9bf2e3e66ee2e546c5c523d148ecab6ee1dcc.zip
Support deferrable uniqueness constraints.
The current implementation fires an AFTER ROW trigger for each tuple that looks like it might be non-unique according to the index contents at the time of insertion. This works well as long as there aren't many conflicts, but won't scale to massive unique-key reassignments. Improving that case is a TODO item. Dean Rasheed
Diffstat (limited to 'src/backend/parser/parse_utilcmd.c')
-rw-r--r--src/backend/parser/parse_utilcmd.c137
1 files changed, 103 insertions, 34 deletions
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index a5d805aa98b..94c8c5977bc 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -19,7 +19,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/backend/parser/parse_utilcmd.c,v 2.23 2009/07/16 06:33:43 petere Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_utilcmd.c,v 2.24 2009/07/29 20:56:19 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -33,6 +33,7 @@
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/namespace.h"
+#include "catalog/pg_constraint.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
@@ -801,15 +802,38 @@ generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx,
/*
* If the index is marked PRIMARY, it's certainly from a constraint; else,
- * if it's not marked UNIQUE, it certainly isn't; else, we have to search
- * pg_depend to see if there's an associated unique constraint.
+ * if it's not marked UNIQUE, it certainly isn't. If it is or might be
+ * from a constraint, we have to fetch the constraint to check for
+ * deferrability attributes.
*/
- if (index->primary)
- index->isconstraint = true;
- else if (!index->unique)
- index->isconstraint = false;
+ if (index->primary || index->unique)
+ {
+ Oid constraintId = get_index_constraint(source_relid);
+
+ if (OidIsValid(constraintId))
+ {
+ HeapTuple ht_constr;
+ Form_pg_constraint conrec;
+
+ ht_constr = SearchSysCache(CONSTROID,
+ ObjectIdGetDatum(constraintId),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(ht_constr))
+ elog(ERROR, "cache lookup failed for constraint %u",
+ constraintId);
+ conrec = (Form_pg_constraint) GETSTRUCT(ht_constr);
+
+ index->isconstraint = true;
+ index->deferrable = conrec->condeferrable;
+ index->initdeferred = conrec->condeferred;
+
+ ReleaseSysCache(ht_constr);
+ }
+ else
+ index->isconstraint = false;
+ }
else
- index->isconstraint = OidIsValid(get_index_constraint(source_relid));
+ index->isconstraint = false;
/* Get the index expressions, if any */
datum = SysCacheGetAttr(INDEXRELID, ht_idx,
@@ -1039,7 +1063,9 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
if (equal(index->indexParams, priorindex->indexParams) &&
equal(index->whereClause, priorindex->whereClause) &&
- strcmp(index->accessMethod, priorindex->accessMethod) == 0)
+ strcmp(index->accessMethod, priorindex->accessMethod) == 0 &&
+ index->deferrable == priorindex->deferrable &&
+ index->initdeferred == priorindex->initdeferred)
{
priorindex->unique |= index->unique;
@@ -1092,6 +1118,8 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
*/
}
index->isconstraint = true;
+ index->deferrable = constraint->deferrable;
+ index->initdeferred = constraint->initdeferred;
if (constraint->name != NULL)
index->idxname = pstrdup(constraint->name);
@@ -1853,8 +1881,9 @@ transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString)
* to attach constraint attributes to their primary constraint nodes
* and detect inconsistent/misplaced constraint attributes.
*
- * NOTE: currently, attributes are only supported for FOREIGN KEY primary
- * constraints, but someday they ought to be supported for other constraints.
+ * NOTE: currently, attributes are only supported for FOREIGN KEY, UNIQUE,
+ * and PRIMARY KEY constraints, but someday they ought to be supported
+ * for other constraint types.
*/
static void
transformConstraintAttrs(List *constraintList)
@@ -1864,6 +1893,13 @@ transformConstraintAttrs(List *constraintList)
bool saw_initially = false;
ListCell *clist;
+#define SUPPORTS_ATTRS(node) \
+ ((node) != NULL && \
+ (IsA((node), FkConstraint) || \
+ (IsA((node), Constraint) && \
+ (((Constraint *) (node))->contype == CONSTR_PRIMARY || \
+ ((Constraint *) (node))->contype == CONSTR_UNIQUE))))
+
foreach(clist, constraintList)
{
Node *node = lfirst(clist);
@@ -1882,8 +1918,7 @@ transformConstraintAttrs(List *constraintList)
switch (con->contype)
{
case CONSTR_ATTR_DEFERRABLE:
- if (lastprimarynode == NULL ||
- !IsA(lastprimarynode, FkConstraint))
+ if (!SUPPORTS_ATTRS(lastprimarynode))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("misplaced DEFERRABLE clause")));
@@ -1892,11 +1927,14 @@ transformConstraintAttrs(List *constraintList)
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("multiple DEFERRABLE/NOT DEFERRABLE clauses not allowed")));
saw_deferrability = true;
- ((FkConstraint *) lastprimarynode)->deferrable = true;
+ if (IsA(lastprimarynode, FkConstraint))
+ ((FkConstraint *) lastprimarynode)->deferrable = true;
+ else
+ ((Constraint *) lastprimarynode)->deferrable = true;
break;
+
case CONSTR_ATTR_NOT_DEFERRABLE:
- if (lastprimarynode == NULL ||
- !IsA(lastprimarynode, FkConstraint))
+ if (!SUPPORTS_ATTRS(lastprimarynode))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("misplaced NOT DEFERRABLE clause")));
@@ -1905,16 +1943,28 @@ transformConstraintAttrs(List *constraintList)
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("multiple DEFERRABLE/NOT DEFERRABLE clauses not allowed")));
saw_deferrability = true;
- ((FkConstraint *) lastprimarynode)->deferrable = false;
- if (saw_initially &&
- ((FkConstraint *) lastprimarynode)->initdeferred)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE")));
+ if (IsA(lastprimarynode, FkConstraint))
+ {
+ ((FkConstraint *) lastprimarynode)->deferrable = false;
+ if (saw_initially &&
+ ((FkConstraint *) lastprimarynode)->initdeferred)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE")));
+ }
+ else
+ {
+ ((Constraint *) lastprimarynode)->deferrable = false;
+ if (saw_initially &&
+ ((Constraint *) lastprimarynode)->initdeferred)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE")));
+ }
break;
+
case CONSTR_ATTR_DEFERRED:
- if (lastprimarynode == NULL ||
- !IsA(lastprimarynode, FkConstraint))
+ if (!SUPPORTS_ATTRS(lastprimarynode))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("misplaced INITIALLY DEFERRED clause")));
@@ -1923,21 +1973,36 @@ transformConstraintAttrs(List *constraintList)
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("multiple INITIALLY IMMEDIATE/DEFERRED clauses not allowed")));
saw_initially = true;
- ((FkConstraint *) lastprimarynode)->initdeferred = true;
/*
* If only INITIALLY DEFERRED appears, assume DEFERRABLE
*/
- if (!saw_deferrability)
- ((FkConstraint *) lastprimarynode)->deferrable = true;
- else if (!((FkConstraint *) lastprimarynode)->deferrable)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE")));
+ if (IsA(lastprimarynode, FkConstraint))
+ {
+ ((FkConstraint *) lastprimarynode)->initdeferred = true;
+
+ if (!saw_deferrability)
+ ((FkConstraint *) lastprimarynode)->deferrable = true;
+ else if (!((FkConstraint *) lastprimarynode)->deferrable)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE")));
+ }
+ else
+ {
+ ((Constraint *) lastprimarynode)->initdeferred = true;
+
+ if (!saw_deferrability)
+ ((Constraint *) lastprimarynode)->deferrable = true;
+ else if (!((Constraint *) lastprimarynode)->deferrable)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE")));
+ }
break;
+
case CONSTR_ATTR_IMMEDIATE:
- if (lastprimarynode == NULL ||
- !IsA(lastprimarynode, FkConstraint))
+ if (!SUPPORTS_ATTRS(lastprimarynode))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("misplaced INITIALLY IMMEDIATE clause")));
@@ -1946,8 +2011,12 @@ transformConstraintAttrs(List *constraintList)
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("multiple INITIALLY IMMEDIATE/DEFERRED clauses not allowed")));
saw_initially = true;
- ((FkConstraint *) lastprimarynode)->initdeferred = false;
+ if (IsA(lastprimarynode, FkConstraint))
+ ((FkConstraint *) lastprimarynode)->initdeferred = false;
+ else
+ ((Constraint *) lastprimarynode)->initdeferred = false;
break;
+
default:
/* Otherwise it's not an attribute */
lastprimarynode = node;