summaryrefslogtreecommitdiff
path: root/src/backend/parser
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
parent850490579318ff52097eec92ce535357dd0c7a3a (diff)
downloadpostgresql-25d9bf2e3e66ee2e546c5c523d148ecab6ee1dcc.tar.gz
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')
-rw-r--r--src/backend/parser/gram.y29
-rw-r--r--src/backend/parser/parse_utilcmd.c137
2 files changed, 130 insertions, 36 deletions
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index c44bbe75c3..0547b64a6e 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.673 2009/07/26 23:34:18 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.674 2009/07/29 20:56:19 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -2220,6 +2220,8 @@ ColConstraintElem:
n->cooked_expr = NULL;
n->keys = NULL;
n->indexspace = NULL;
+ n->deferrable = FALSE;
+ n->initdeferred = FALSE;
$$ = (Node *)n;
}
| NULL_P
@@ -2231,6 +2233,8 @@ ColConstraintElem:
n->cooked_expr = NULL;
n->keys = NULL;
n->indexspace = NULL;
+ n->deferrable = FALSE;
+ n->initdeferred = FALSE;
$$ = (Node *)n;
}
| UNIQUE opt_definition OptConsTableSpace
@@ -2243,6 +2247,8 @@ ColConstraintElem:
n->keys = NULL;
n->options = $2;
n->indexspace = $3;
+ n->deferrable = FALSE;
+ n->initdeferred = FALSE;
$$ = (Node *)n;
}
| PRIMARY KEY opt_definition OptConsTableSpace
@@ -2255,6 +2261,8 @@ ColConstraintElem:
n->keys = NULL;
n->options = $3;
n->indexspace = $4;
+ n->deferrable = FALSE;
+ n->initdeferred = FALSE;
$$ = (Node *)n;
}
| CHECK '(' a_expr ')'
@@ -2266,6 +2274,8 @@ ColConstraintElem:
n->cooked_expr = NULL;
n->keys = NULL;
n->indexspace = NULL;
+ n->deferrable = FALSE;
+ n->initdeferred = FALSE;
$$ = (Node *)n;
}
| DEFAULT b_expr
@@ -2277,6 +2287,8 @@ ColConstraintElem:
n->cooked_expr = NULL;
n->keys = NULL;
n->indexspace = NULL;
+ n->deferrable = FALSE;
+ n->initdeferred = FALSE;
$$ = (Node *)n;
}
| REFERENCES qualified_name opt_column_list key_match key_actions
@@ -2398,7 +2410,7 @@ TableConstraint:
;
ConstraintElem:
- CHECK '(' a_expr ')'
+ CHECK '(' a_expr ')' ConstraintAttributeSpec
{
Constraint *n = makeNode(Constraint);
n->contype = CONSTR_CHECK;
@@ -2406,9 +2418,17 @@ ConstraintElem:
n->raw_expr = $3;
n->cooked_expr = NULL;
n->indexspace = NULL;
+ if ($5 != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("CHECK constraints cannot be deferred"),
+ parser_errposition(@5)));
+ n->deferrable = FALSE;
+ n->initdeferred = FALSE;
$$ = (Node *)n;
}
| UNIQUE '(' columnList ')' opt_definition OptConsTableSpace
+ ConstraintAttributeSpec
{
Constraint *n = makeNode(Constraint);
n->contype = CONSTR_UNIQUE;
@@ -2418,9 +2438,12 @@ ConstraintElem:
n->keys = $3;
n->options = $5;
n->indexspace = $6;
+ n->deferrable = ($7 & 1) != 0;
+ n->initdeferred = ($7 & 2) != 0;
$$ = (Node *)n;
}
| PRIMARY KEY '(' columnList ')' opt_definition OptConsTableSpace
+ ConstraintAttributeSpec
{
Constraint *n = makeNode(Constraint);
n->contype = CONSTR_PRIMARY;
@@ -2430,6 +2453,8 @@ ConstraintElem:
n->keys = $4;
n->options = $6;
n->indexspace = $7;
+ n->deferrable = ($8 & 1) != 0;
+ n->initdeferred = ($8 & 2) != 0;
$$ = (Node *)n;
}
| FOREIGN KEY '(' columnList ')' REFERENCES qualified_name
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index a5d805aa98..94c8c5977b 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;