ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> SET ( <replaceable class="PARAMETER">attribute_option</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )
ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> RESET ( <replaceable class="PARAMETER">attribute_option</replaceable> [, ... ] )
ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN }
- ADD <replaceable class="PARAMETER">table_constraint</replaceable>
- ADD <replaceable class="PARAMETER">table_constraint_using_index</replaceable>
ADD <replaceable class="PARAMETER">table_constraint</replaceable> [ NOT VALID ]
+ ADD <replaceable class="PARAMETER">table_constraint_using_index</replaceable>
VALIDATE CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable>
DROP CONSTRAINT [ IF EXISTS ] <replaceable class="PARAMETER">constraint_name</replaceable> [ RESTRICT | CASCADE ]
DISABLE TRIGGER [ <replaceable class="PARAMETER">trigger_name</replaceable> | ALL | USER ]
</varlistentry>
<varlistentry>
- <term><literal>ADD <replaceable class="PARAMETER">table_constraint</replaceable>
- [ NOT VALID ]</literal></term>
+ <term><literal>ADD <replaceable class="PARAMETER">table_constraint</replaceable> [ NOT VALID ]</literal></term>
<listitem>
<para>
This form adds a new constraint to a table using the same syntax as
- <xref linkend="SQL-CREATETABLE">. Newly added foreign key constraints can
- also be defined as <literal>NOT VALID</literal> to avoid the
- potentially lengthy initial check that must otherwise be performed.
- Constraint checks are skipped at create table time, so
- <xref linkend="SQL-CREATETABLE"> does not contain this option.
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><literal>VALIDATE CONSTRAINT</literal></term>
- <listitem>
- <para>
- This form validates a foreign key constraint that was previously created
- as <literal>NOT VALID</literal>. Constraints already marked valid do not
- cause an error response.
+ <xref linkend="SQL-CREATETABLE">, plus the option <literal>NOT
+ VALID</literal>, which is currently only allowed for foreign key
+ constraints.
+ If the constraint is marked <literal>NOT VALID</literal>, the
+ potentially-lengthy initial check to verify that all rows in the table
+ satisfy the constraint is skipped. The constraint will still be
+ enforced against subsequent inserts or updates (that is, they'll fail
+ unless there is a matching row in the referenced table). But the
+ database will not assume that the constraint holds for all rows in
+ the table, until it is validated by using the <literal>VALIDATE
+ CONSTRAINT</literal> option.
</para>
</listitem>
</varlistentry>
</varlistentry>
<varlistentry>
+ <term><literal>VALIDATE CONSTRAINT</literal></term>
+ <listitem>
+ <para>
+ This form validates a foreign key constraint that was previously created
+ as <literal>NOT VALID</literal>, by scanning the table to ensure there
+ are no unmatched rows. Nothing happens if the constraint is
+ already marked valid.
+ The value of separating validation from initial creation of the
+ constraint is that validation requires a lesser lock on the table
+ than constraint creation does.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><literal>DROP CONSTRAINT [ IF EXISTS ]</literal></term>
<listitem>
<para>
List *objs;
} PrivTarget;
+/* ConstraintAttributeSpec yields an integer bitmask of these flags: */
+#define CAS_NOT_DEFERRABLE 0x01
+#define CAS_DEFERRABLE 0x02
+#define CAS_INITIALLY_IMMEDIATE 0x04
+#define CAS_INITIALLY_DEFERRED 0x08
+#define CAS_NOT_VALID 0x10
+
#define parser_yyerror(msg) scanner_yyerror(msg, yyscanner)
#define parser_errposition(pos) scanner_errposition(pos, yyscanner)
static void SplitColQualList(List *qualList,
List **constraintList, CollateClause **collClause,
core_yyscan_t yyscanner);
+static void processCASbits(int cas_bits, int location, const char *constrType,
+ bool *deferrable, bool *initdeferred, bool *not_valid,
+ core_yyscan_t yyscanner);
%}
%type <list> ColQualList
%type <node> ColConstraint ColConstraintElem ConstraintAttr
%type <ival> key_actions key_delete key_match key_update key_action
-%type <ival> ConstraintAttributeSpec ConstraintDeferrabilitySpec
- ConstraintTimeSpec
+%type <ival> ConstraintAttributeSpec ConstraintAttributeElem
%type <str> ExistingIndex
%type <list> constraints_set_list
n->fk_matchtype = $4;
n->fk_upd_action = (char) ($5 >> 8);
n->fk_del_action = (char) ($5 & 0xFF);
- n->skip_validation = FALSE;
+ n->skip_validation = false;
n->initially_valid = true;
$$ = (Node *)n;
}
* combinations.
*
* See also ConstraintAttributeSpec, which can be used in places where
- * there is no parsing conflict.
+ * there is no parsing conflict. (Note: currently, NOT VALID is an allowed
+ * clause in ConstraintAttributeSpec, but not here. Someday we might need
+ * to allow it here too, but for the moment it doesn't seem useful in the
+ * statements that use ConstraintAttr.)
*/
ConstraintAttr:
DEFERRABLE
n->location = @1;
n->raw_expr = $3;
n->cooked_expr = NULL;
- if ($5 != 0)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("CHECK constraints cannot be deferred"),
- parser_errposition(@5)));
+ processCASbits($5, @5, "CHECK",
+ NULL, NULL, NULL,
+ yyscanner);
$$ = (Node *)n;
}
| UNIQUE '(' columnList ')' opt_definition OptConsTableSpace
n->options = $5;
n->indexname = NULL;
n->indexspace = $6;
- n->deferrable = ($7 & 1) != 0;
- n->initdeferred = ($7 & 2) != 0;
+ processCASbits($7, @7, "UNIQUE",
+ &n->deferrable, &n->initdeferred, NULL,
+ yyscanner);
$$ = (Node *)n;
}
| UNIQUE ExistingIndex ConstraintAttributeSpec
n->options = NIL;
n->indexname = $2;
n->indexspace = NULL;
- n->deferrable = ($3 & 1) != 0;
- n->initdeferred = ($3 & 2) != 0;
+ processCASbits($3, @3, "UNIQUE",
+ &n->deferrable, &n->initdeferred, NULL,
+ yyscanner);
$$ = (Node *)n;
}
| PRIMARY KEY '(' columnList ')' opt_definition OptConsTableSpace
n->options = $6;
n->indexname = NULL;
n->indexspace = $7;
- n->deferrable = ($8 & 1) != 0;
- n->initdeferred = ($8 & 2) != 0;
+ processCASbits($8, @8, "PRIMARY KEY",
+ &n->deferrable, &n->initdeferred, NULL,
+ yyscanner);
$$ = (Node *)n;
}
| PRIMARY KEY ExistingIndex ConstraintAttributeSpec
n->options = NIL;
n->indexname = $3;
n->indexspace = NULL;
- n->deferrable = ($4 & 1) != 0;
- n->initdeferred = ($4 & 2) != 0;
+ processCASbits($4, @4, "PRIMARY KEY",
+ &n->deferrable, &n->initdeferred, NULL,
+ yyscanner);
$$ = (Node *)n;
}
| EXCLUDE access_method_clause '(' ExclusionConstraintList ')'
n->indexname = NULL;
n->indexspace = $7;
n->where_clause = $8;
- n->deferrable = ($9 & 1) != 0;
- n->initdeferred = ($9 & 2) != 0;
+ processCASbits($9, @9, "EXCLUDE",
+ &n->deferrable, &n->initdeferred, NULL,
+ yyscanner);
$$ = (Node *)n;
}
| FOREIGN KEY '(' columnList ')' REFERENCES qualified_name
n->fk_matchtype = $9;
n->fk_upd_action = (char) ($10 >> 8);
n->fk_del_action = (char) ($10 & 0xFF);
- n->deferrable = ($11 & 1) != 0;
- n->initdeferred = ($11 & 2) != 0;
- n->skip_validation = false;
- n->initially_valid = true;
- $$ = (Node *)n;
- }
- | FOREIGN KEY '(' columnList ')' REFERENCES qualified_name
- opt_column_list key_match key_actions
- NOT VALID
- {
- Constraint *n = makeNode(Constraint);
- n->contype = CONSTR_FOREIGN;
- n->location = @1;
- n->pktable = $7;
- n->fk_attrs = $4;
- n->pk_attrs = $8;
- n->fk_matchtype = $9;
- n->fk_upd_action = (char) ($10 >> 8);
- n->fk_del_action = (char) ($10 & 0xFF);
- n->skip_validation = true;
- n->initially_valid = false;
+ processCASbits($11, @11, "FOREIGN KEY",
+ &n->deferrable, &n->initdeferred,
+ &n->skip_validation,
+ yyscanner);
+ n->initially_valid = !n->skip_validation;
$$ = (Node *)n;
}
;
n->columns = (List *) lsecond($6);
n->whenClause = $14;
n->isconstraint = TRUE;
- n->deferrable = ($10 & 1) != 0;
- n->initdeferred = ($10 & 2) != 0;
+ processCASbits($10, @10, "TRIGGER",
+ &n->deferrable, &n->initdeferred, NULL,
+ yyscanner);
n->constrrel = $9;
$$ = (Node *)n;
}
;
ConstraintAttributeSpec:
- ConstraintDeferrabilitySpec
- { $$ = $1; }
- | ConstraintDeferrabilitySpec ConstraintTimeSpec
+ /*EMPTY*/
+ { $$ = 0; }
+ | ConstraintAttributeSpec ConstraintAttributeElem
{
- if ($1 == 0 && $2 != 0)
+ /*
+ * We must complain about conflicting options.
+ * We could, but choose not to, complain about redundant
+ * options (ie, where $2's bit is already set in $1).
+ */
+ int newspec = $1 | $2;
+
+ /* special message for this case */
+ if ((newspec & (CAS_NOT_DEFERRABLE | CAS_INITIALLY_DEFERRED)) == (CAS_NOT_DEFERRABLE | CAS_INITIALLY_DEFERRED))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE"),
- parser_errposition(@1)));
- $$ = $1 | $2;
- }
- | ConstraintTimeSpec
- {
- if ($1 != 0)
- $$ = 3;
- else
- $$ = 0;
- }
- | ConstraintTimeSpec ConstraintDeferrabilitySpec
- {
- if ($2 == 0 && $1 != 0)
+ parser_errposition(@2)));
+ /* generic message for other conflicts */
+ if ((newspec & (CAS_NOT_DEFERRABLE | CAS_DEFERRABLE)) == (CAS_NOT_DEFERRABLE | CAS_DEFERRABLE) ||
+ (newspec & (CAS_INITIALLY_IMMEDIATE | CAS_INITIALLY_DEFERRED)) == (CAS_INITIALLY_IMMEDIATE | CAS_INITIALLY_DEFERRED))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE"),
- parser_errposition(@1)));
- $$ = $1 | $2;
+ errmsg("conflicting constraint properties"),
+ parser_errposition(@2)));
+ $$ = newspec;
}
- | /*EMPTY*/
- { $$ = 0; }
;
-ConstraintDeferrabilitySpec:
- NOT DEFERRABLE { $$ = 0; }
- | DEFERRABLE { $$ = 1; }
- ;
-
-ConstraintTimeSpec:
- INITIALLY IMMEDIATE { $$ = 0; }
- | INITIALLY DEFERRED { $$ = 2; }
+ConstraintAttributeElem:
+ NOT DEFERRABLE { $$ = CAS_NOT_DEFERRABLE; }
+ | DEFERRABLE { $$ = CAS_DEFERRABLE; }
+ | INITIALLY IMMEDIATE { $$ = CAS_INITIALLY_IMMEDIATE; }
+ | INITIALLY DEFERRED { $$ = CAS_INITIALLY_DEFERRED; }
+ | NOT VALID { $$ = CAS_NOT_VALID; }
;
n->trigname = $3;
n->args = list_make1($6);
n->isconstraint = TRUE;
- n->deferrable = ($8 & 1) != 0;
- n->initdeferred = ($8 & 2) != 0;
+ processCASbits($8, @8, "ASSERTION",
+ &n->deferrable, &n->initdeferred, NULL,
+ yyscanner);
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
*constraintList = qualList;
}
+/*
+ * Process result of ConstraintAttributeSpec, and set appropriate bool flags
+ * in the output command node. Pass NULL for any flags the particular
+ * command doesn't support.
+ */
+static void
+processCASbits(int cas_bits, int location, const char *constrType,
+ bool *deferrable, bool *initdeferred, bool *not_valid,
+ core_yyscan_t yyscanner)
+{
+ /* defaults */
+ if (deferrable)
+ *deferrable = false;
+ if (initdeferred)
+ *initdeferred = false;
+ if (not_valid)
+ *not_valid = false;
+
+ if (cas_bits & (CAS_DEFERRABLE | CAS_INITIALLY_DEFERRED))
+ {
+ if (deferrable)
+ *deferrable = true;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ /* translator: %s is CHECK, UNIQUE, or similar */
+ errmsg("%s constraints cannot be marked DEFERRABLE",
+ constrType),
+ parser_errposition(location)));
+ }
+
+ if (cas_bits & CAS_INITIALLY_DEFERRED)
+ {
+ if (initdeferred)
+ *initdeferred = true;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ /* translator: %s is CHECK, UNIQUE, or similar */
+ errmsg("%s constraints cannot be marked DEFERRABLE",
+ constrType),
+ parser_errposition(location)));
+ }
+
+ if (cas_bits & CAS_NOT_VALID)
+ {
+ if (not_valid)
+ *not_valid = true;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ /* translator: %s is CHECK, UNIQUE, or similar */
+ errmsg("%s constraints cannot be marked NOT VALID",
+ constrType),
+ parser_errposition(location)));
+ }
+}
+
/* parser_init()
* Initialize to parse one query string
*/