CHECK NO INHERIT syntax
Sorry to raise this once again, but I still find this CHECK NO INHERIT
syntax to a bit funny. We are currently using something like
CHECK NO INHERIT (foo > 0)
But we already have a different syntax for attaching attributes to
constraints (NOT DEFERRABLE, NOT VALID, etc.), so it would make more
sense to have
CHECK (foo > 0) NO INHERIT
Besides consistency, this makes more sense, because the attribute is a
property of the constraint as a whole, not of the "checking".
This would also extend more easily to other constraint types. For
example, when unifying CHECK and NOT NULL constraints, as is planned, or
when allowing inherited unique constraints, as is planned further down
the road.
There is also a hole in the current implementation. Domain constraints
silently allow NO INHERIT to be specified, even though other senseless
attributes are rejected.
On Wed, Jul 18, 2012 at 5:49 PM, Peter Eisentraut <peter_e@gmx.net> wrote:
Sorry to raise this once again, but I still find this CHECK NO INHERIT
syntax to a bit funny. We are currently using something likeCHECK NO INHERIT (foo > 0)
But we already have a different syntax for attaching attributes to
constraints (NOT DEFERRABLE, NOT VALID, etc.), so it would make more
sense to haveCHECK (foo > 0) NO INHERIT
Besides consistency, this makes more sense, because the attribute is a
property of the constraint as a whole, not of the "checking".This would also extend more easily to other constraint types. For
example, when unifying CHECK and NOT NULL constraints, as is planned, or
when allowing inherited unique constraints, as is planned further down
the road.
+1.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Thu, Jul 19, 2012 at 12:49:37AM +0300, Peter Eisentraut wrote:
Sorry to raise this once again, but I still find this CHECK NO INHERIT
syntax to a bit funny. We are currently using something likeCHECK NO INHERIT (foo > 0)
But we already have a different syntax for attaching attributes to
constraints (NOT DEFERRABLE, NOT VALID, etc.), so it would make more
sense to haveCHECK (foo > 0) NO INHERIT
How about this?
CHECK (foo > 0) (INHERIT FALSE)
That leaves an obvious place for other options, which will doubtless
come. EXPLAIN's options inspired this API design.
Besides consistency, this makes more sense, because the attribute is a
property of the constraint as a whole, not of the "checking".
Good point. The above change preserves this property.
This would also extend more easily to other constraint types. For
example, when unifying CHECK and NOT NULL constraints, as is
planned, or when allowing inherited unique constraints, as is
planned further down the road.There is also a hole in the current implementation. Domain
constraints silently allow NO INHERIT to be specified, even though
other senseless attributes are rejected.
That's probably a bug.
Cheers,
David.
--
David Fetter <david@fetter.org> http://fetter.org/
Phone: +1 415 235 3778 AIM: dfetter666 Yahoo!: dfetter
Skype: davidfetter XMPP: david.fetter@gmail.com
iCal: webcal://www.tripit.com/feed/ical/people/david74/tripit.ics
Remember to vote!
Consider donating to Postgres: http://www.postgresql.org/about/donate
David Fetter <david@fetter.org> writes:
On Thu, Jul 19, 2012 at 12:49:37AM +0300, Peter Eisentraut wrote:
But we already have a different syntax for attaching attributes to
constraints (NOT DEFERRABLE, NOT VALID, etc.), so it would make more
sense to haveCHECK (foo > 0) NO INHERIT
How about this?
CHECK (foo > 0) (INHERIT FALSE)
The SQL spec already says what the syntax is for options attached to
constraints, and that's not it.
regards, tom lane
Excerpts from Peter Eisentraut's message of mié jul 18 17:49:37 -0400 2012:
Sorry to raise this once again, but I still find this CHECK NO INHERIT
syntax to a bit funny. We are currently using something likeCHECK NO INHERIT (foo > 0)
But we already have a different syntax for attaching attributes to
constraints (NOT DEFERRABLE, NOT VALID, etc.), so it would make more
sense to haveCHECK (foo > 0) NO INHERIT
Okay, given the astounding acceptance of your proposal, the attached patch
fixes things in that way. This only include changes to the core code;
I'll prepare documentation and regression tests tweaks while I wait for an
answer to the request below.
There is also a hole in the current implementation. Domain constraints
silently allow NO INHERIT to be specified, even though other senseless
attributes are rejected.
True. I have added an error check at creation time. Please suggest
improved wording for the message:
alvherre=# create domain positiveint2 as int check (value > 0) no inherit;
ERROR: CHECK constraints for domains cannot be NO INHERIT
--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support
Attachments:
check-no-inherit.patchapplication/octet-stream; name=check-no-inherit.patchDownload
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 30850b2..353043d 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -921,8 +921,14 @@ DefineDomain(CreateDomainStmt *stmt)
/*
* Check constraints are handled after domain creation, as
- * they require the Oid of the domain
+ * they require the Oid of the domain; at this point we can
+ * only check that they're not marked NO INHERIT, because
+ * that would be bogus.
*/
+ if (constr->is_no_inherit)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("CHECK constraints for domains cannot be marked NO INHERIT")));
break;
/*
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 777da11..a84fb63 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -2709,13 +2709,13 @@ ColConstraintElem:
n->indexspace = $4;
$$ = (Node *)n;
}
- | CHECK opt_no_inherit '(' a_expr ')'
+ | CHECK '(' a_expr ')' opt_no_inherit
{
Constraint *n = makeNode(Constraint);
n->contype = CONSTR_CHECK;
n->location = @1;
- n->is_no_inherit = $2;
- n->raw_expr = $4;
+ n->is_no_inherit = $5;
+ n->raw_expr = $3;
n->cooked_expr = NULL;
$$ = (Node *)n;
}
@@ -2835,13 +2835,13 @@ TableConstraint:
;
ConstraintElem:
- CHECK opt_no_inherit '(' a_expr ')' ConstraintAttributeSpec
+ CHECK '(' a_expr ')' opt_no_inherit ConstraintAttributeSpec
{
Constraint *n = makeNode(Constraint);
n->contype = CONSTR_CHECK;
n->location = @1;
- n->is_no_inherit = $2;
- n->raw_expr = $4;
+ n->is_no_inherit = $5;
+ n->raw_expr = $3;
n->cooked_expr = NULL;
processCASbits($6, @6, "CHECK",
NULL, NULL, &n->skip_validation,
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index ec93149..412dfe6 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -1343,10 +1343,9 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
* Note that simply checking for leading '(' and trailing ')'
* would NOT be good enough, consider "(x > 0) AND (y > 0)".
*/
- appendStringInfo(&buf, "CHECK %s(%s)",
- conForm->connoinherit ? "NO INHERIT " : "",
- consrc);
-
+ appendStringInfo(&buf, "CHECK (%s)%s",
+ consrc,
+ conForm->connoinherit ? " NO INHERIT" : "");
break;
}
case CONSTRAINT_TRIGGER:
Alvaro Herrera <alvherre@commandprompt.com> writes:
True. I have added an error check at creation time. Please suggest
improved wording for the message:
alvherre=# create domain positiveint2 as int check (value > 0) no inherit;
ERROR: CHECK constraints for domains cannot be NO INHERIT
I think "CHECK constraints for domains cannot be marked NO INHERIT"
would be fine.
ConstraintElem: - CHECK opt_no_inherit '(' a_expr ')' ConstraintAttributeSpec + CHECK '(' a_expr ')' opt_no_inherit ConstraintAttributeSpec
This doesn't seem to me to meet the principle of least surprise. Surely
NO INHERIT ought to be folded into ConstraintAttributeSpec so that it
acts like other constraint decorations, ie order isn't significant.
regards, tom lane
Excerpts from Tom Lane's message of vie jul 20 16:12:05 -0400 2012:
Alvaro Herrera <alvherre@commandprompt.com> writes:
True. I have added an error check at creation time. Please suggest
improved wording for the message:alvherre=# create domain positiveint2 as int check (value > 0) no inherit;
ERROR: CHECK constraints for domains cannot be NO INHERITI think "CHECK constraints for domains cannot be marked NO INHERIT"
would be fine.
Thanks.
ConstraintElem: - CHECK opt_no_inherit '(' a_expr ')' ConstraintAttributeSpec + CHECK '(' a_expr ')' opt_no_inherit ConstraintAttributeSpecThis doesn't seem to me to meet the principle of least surprise. Surely
NO INHERIT ought to be folded into ConstraintAttributeSpec so that it
acts like other constraint decorations, ie order isn't significant.
Oh, true; that's a bit more involved. I verified it works correctly to
have a constraint marked NOT VALID NO INHERIT or the other way around.
I haven't checked whether the changes to ConstraintAttributeSpec have
side effects -- I think it's OK but I might be missing something.
Here's a (hopefully) complete patch.
--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support
Attachments:
check-no-inherit-2.patchapplication/octet-stream; name=check-no-inherit-2.patchDownload
*** a/doc/src/sgml/ref/alter_table.sgml
--- b/doc/src/sgml/ref/alter_table.sgml
***************
*** 1013,1019 **** ALTER TABLE distributors ADD CONSTRAINT zipchk CHECK (char_length(zipcode) = 5);
<para>
To add a check constraint only to a table and not to its children:
<programlisting>
! ALTER TABLE distributors ADD CONSTRAINT zipchk CHECK NO INHERIT (char_length(zipcode) = 5);
</programlisting>
(The check constraint will not be inherited by future children, either.)
</para>
--- 1013,1019 ----
<para>
To add a check constraint only to a table and not to its children:
<programlisting>
! ALTER TABLE distributors ADD CONSTRAINT zipchk CHECK (char_length(zipcode) = 5) NO INHERIT;
</programlisting>
(The check constraint will not be inherited by future children, either.)
</para>
*** a/doc/src/sgml/ref/create_table.sgml
--- b/doc/src/sgml/ref/create_table.sgml
***************
*** 47,53 **** CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
[ CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> ]
{ NOT NULL |
NULL |
! CHECK [ NO INHERIT ] ( <replaceable class="PARAMETER">expression</replaceable> ) |
DEFAULT <replaceable>default_expr</replaceable> |
UNIQUE <replaceable class="PARAMETER">index_parameters</replaceable> |
PRIMARY KEY <replaceable class="PARAMETER">index_parameters</replaceable> |
--- 47,53 ----
[ CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> ]
{ NOT NULL |
NULL |
! CHECK ( <replaceable class="PARAMETER">expression</replaceable> ) [ NO INHERIT ] |
DEFAULT <replaceable>default_expr</replaceable> |
UNIQUE <replaceable class="PARAMETER">index_parameters</replaceable> |
PRIMARY KEY <replaceable class="PARAMETER">index_parameters</replaceable> |
***************
*** 58,64 **** CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
<phrase>and <replaceable class="PARAMETER">table_constraint</replaceable> is:</phrase>
[ CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> ]
! { CHECK [ NO INHERIT ] ( <replaceable class="PARAMETER">expression</replaceable> ) |
UNIQUE ( <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) <replaceable class="PARAMETER">index_parameters</replaceable> |
PRIMARY KEY ( <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) <replaceable class="PARAMETER">index_parameters</replaceable> |
EXCLUDE [ USING <replaceable class="parameter">index_method</replaceable> ] ( <replaceable class="parameter">exclude_element</replaceable> WITH <replaceable class="parameter">operator</replaceable> [, ... ] ) <replaceable class="parameter">index_parameters</replaceable> [ WHERE ( <replaceable class="parameter">predicate</replaceable> ) ] |
--- 58,64 ----
<phrase>and <replaceable class="PARAMETER">table_constraint</replaceable> is:</phrase>
[ CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> ]
! { CHECK ( <replaceable class="PARAMETER">expression</replaceable> ) [ NO INHERIT ] |
UNIQUE ( <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) <replaceable class="PARAMETER">index_parameters</replaceable> |
PRIMARY KEY ( <replaceable class="PARAMETER">column_name</replaceable> [, ... ] ) <replaceable class="PARAMETER">index_parameters</replaceable> |
EXCLUDE [ USING <replaceable class="parameter">index_method</replaceable> ] ( <replaceable class="parameter">exclude_element</replaceable> WITH <replaceable class="parameter">operator</replaceable> [, ... ] ) <replaceable class="parameter">index_parameters</replaceable> [ WHERE ( <replaceable class="parameter">predicate</replaceable> ) ] |
***************
*** 417,423 **** CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
</varlistentry>
<varlistentry>
! <term><literal>CHECK [ NO INHERIT ] ( <replaceable class="PARAMETER">expression</replaceable> )</literal></term>
<listitem>
<para>
The <literal>CHECK</> clause specifies an expression producing a
--- 417,423 ----
</varlistentry>
<varlistentry>
! <term><literal>CHECK ( <replaceable class="PARAMETER">expression</replaceable> ) [ NO INHERIT ] </literal></term>
<listitem>
<para>
The <literal>CHECK</> clause specifies an expression producing a
*** a/src/backend/commands/typecmds.c
--- b/src/backend/commands/typecmds.c
***************
*** 921,928 **** DefineDomain(CreateDomainStmt *stmt)
/*
* Check constraints are handled after domain creation, as
! * they require the Oid of the domain
*/
break;
/*
--- 921,934 ----
/*
* Check constraints are handled after domain creation, as
! * they require the Oid of the domain; at this point we can
! * only check that they're not marked NO INHERIT, because
! * that would be bogus.
*/
+ if (constr->is_no_inherit)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("CHECK constraints for domains cannot be marked NO INHERIT")));
break;
/*
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 101,106 **** typedef struct PrivTarget
--- 101,107 ----
#define CAS_INITIALLY_IMMEDIATE 0x04
#define CAS_INITIALLY_DEFERRED 0x08
#define CAS_NOT_VALID 0x10
+ #define CAS_NO_INHERIT 0x20
#define parser_yyerror(msg) scanner_yyerror(msg, yyscanner)
***************
*** 144,150 **** static void SplitColQualList(List *qualList,
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);
%}
--- 145,151 ----
core_yyscan_t yyscanner);
static void processCASbits(int cas_bits, int location, const char *constrType,
bool *deferrable, bool *initdeferred, bool *not_valid,
! bool *no_inherit, core_yyscan_t yyscanner);
%}
***************
*** 2709,2721 **** ColConstraintElem:
n->indexspace = $4;
$$ = (Node *)n;
}
! | CHECK opt_no_inherit '(' a_expr ')'
{
Constraint *n = makeNode(Constraint);
n->contype = CONSTR_CHECK;
n->location = @1;
! n->is_no_inherit = $2;
! n->raw_expr = $4;
n->cooked_expr = NULL;
$$ = (Node *)n;
}
--- 2710,2722 ----
n->indexspace = $4;
$$ = (Node *)n;
}
! | CHECK '(' a_expr ')' opt_no_inherit
{
Constraint *n = makeNode(Constraint);
n->contype = CONSTR_CHECK;
n->location = @1;
! n->is_no_inherit = $5;
! n->raw_expr = $3;
n->cooked_expr = NULL;
$$ = (Node *)n;
}
***************
*** 2755,2764 **** ColConstraintElem:
* combinations.
*
* See also ConstraintAttributeSpec, which can be used in places where
! * 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
--- 2756,2765 ----
* combinations.
*
* See also ConstraintAttributeSpec, which can be used in places where
! * there is no parsing conflict. (Note: currently, NOT VALID and NO INHERIT
! * are allowed clauses in ConstraintAttributeSpec, but not here. Someday we
! * might need to allow them here too, but for the moment it doesn't seem
! * useful in the statements that use ConstraintAttr.)
*/
ConstraintAttr:
DEFERRABLE
***************
*** 2835,2851 **** TableConstraint:
;
ConstraintElem:
! CHECK opt_no_inherit '(' a_expr ')' ConstraintAttributeSpec
{
Constraint *n = makeNode(Constraint);
n->contype = CONSTR_CHECK;
n->location = @1;
! n->is_no_inherit = $2;
! n->raw_expr = $4;
n->cooked_expr = NULL;
! processCASbits($6, @6, "CHECK",
NULL, NULL, &n->skip_validation,
! yyscanner);
n->initially_valid = !n->skip_validation;
$$ = (Node *)n;
}
--- 2836,2851 ----
;
ConstraintElem:
! CHECK '(' a_expr ')' ConstraintAttributeSpec
{
Constraint *n = makeNode(Constraint);
n->contype = CONSTR_CHECK;
n->location = @1;
! n->raw_expr = $3;
n->cooked_expr = NULL;
! processCASbits($5, @5, "CHECK",
NULL, NULL, &n->skip_validation,
! &n->is_no_inherit, yyscanner);
n->initially_valid = !n->skip_validation;
$$ = (Node *)n;
}
***************
*** 2861,2867 **** ConstraintElem:
n->indexspace = $6;
processCASbits($7, @7, "UNIQUE",
&n->deferrable, &n->initdeferred, NULL,
! yyscanner);
$$ = (Node *)n;
}
| UNIQUE ExistingIndex ConstraintAttributeSpec
--- 2861,2867 ----
n->indexspace = $6;
processCASbits($7, @7, "UNIQUE",
&n->deferrable, &n->initdeferred, NULL,
! NULL, yyscanner);
$$ = (Node *)n;
}
| UNIQUE ExistingIndex ConstraintAttributeSpec
***************
*** 2875,2881 **** ConstraintElem:
n->indexspace = NULL;
processCASbits($3, @3, "UNIQUE",
&n->deferrable, &n->initdeferred, NULL,
! yyscanner);
$$ = (Node *)n;
}
| PRIMARY KEY '(' columnList ')' opt_definition OptConsTableSpace
--- 2875,2881 ----
n->indexspace = NULL;
processCASbits($3, @3, "UNIQUE",
&n->deferrable, &n->initdeferred, NULL,
! NULL, yyscanner);
$$ = (Node *)n;
}
| PRIMARY KEY '(' columnList ')' opt_definition OptConsTableSpace
***************
*** 2890,2896 **** ConstraintElem:
n->indexspace = $7;
processCASbits($8, @8, "PRIMARY KEY",
&n->deferrable, &n->initdeferred, NULL,
! yyscanner);
$$ = (Node *)n;
}
| PRIMARY KEY ExistingIndex ConstraintAttributeSpec
--- 2890,2896 ----
n->indexspace = $7;
processCASbits($8, @8, "PRIMARY KEY",
&n->deferrable, &n->initdeferred, NULL,
! NULL, yyscanner);
$$ = (Node *)n;
}
| PRIMARY KEY ExistingIndex ConstraintAttributeSpec
***************
*** 2904,2910 **** ConstraintElem:
n->indexspace = NULL;
processCASbits($4, @4, "PRIMARY KEY",
&n->deferrable, &n->initdeferred, NULL,
! yyscanner);
$$ = (Node *)n;
}
| EXCLUDE access_method_clause '(' ExclusionConstraintList ')'
--- 2904,2910 ----
n->indexspace = NULL;
processCASbits($4, @4, "PRIMARY KEY",
&n->deferrable, &n->initdeferred, NULL,
! NULL, yyscanner);
$$ = (Node *)n;
}
| EXCLUDE access_method_clause '(' ExclusionConstraintList ')'
***************
*** 2922,2928 **** ConstraintElem:
n->where_clause = $8;
processCASbits($9, @9, "EXCLUDE",
&n->deferrable, &n->initdeferred, NULL,
! yyscanner);
$$ = (Node *)n;
}
| FOREIGN KEY '(' columnList ')' REFERENCES qualified_name
--- 2922,2928 ----
n->where_clause = $8;
processCASbits($9, @9, "EXCLUDE",
&n->deferrable, &n->initdeferred, NULL,
! NULL, yyscanner);
$$ = (Node *)n;
}
| FOREIGN KEY '(' columnList ')' REFERENCES qualified_name
***************
*** 2939,2945 **** ConstraintElem:
n->fk_del_action = (char) ($10 & 0xFF);
processCASbits($11, @11, "FOREIGN KEY",
&n->deferrable, &n->initdeferred,
! &n->skip_validation,
yyscanner);
n->initially_valid = !n->skip_validation;
$$ = (Node *)n;
--- 2939,2945 ----
n->fk_del_action = (char) ($10 & 0xFF);
processCASbits($11, @11, "FOREIGN KEY",
&n->deferrable, &n->initdeferred,
! &n->skip_validation, NULL,
yyscanner);
n->initially_valid = !n->skip_validation;
$$ = (Node *)n;
***************
*** 4133,4139 **** CreateTrigStmt:
n->isconstraint = TRUE;
processCASbits($10, @10, "TRIGGER",
&n->deferrable, &n->initdeferred, NULL,
! yyscanner);
n->constrrel = $9;
$$ = (Node *)n;
}
--- 4133,4139 ----
n->isconstraint = TRUE;
processCASbits($10, @10, "TRIGGER",
&n->deferrable, &n->initdeferred, NULL,
! NULL, yyscanner);
n->constrrel = $9;
$$ = (Node *)n;
}
***************
*** 4270,4275 **** ConstraintAttributeElem:
--- 4270,4276 ----
| INITIALLY IMMEDIATE { $$ = CAS_INITIALLY_IMMEDIATE; }
| INITIALLY DEFERRED { $$ = CAS_INITIALLY_DEFERRED; }
| NOT VALID { $$ = CAS_NOT_VALID; }
+ | NO INHERIT { $$ = CAS_NO_INHERIT; }
;
***************
*** 4386,4392 **** CreateAssertStmt:
n->isconstraint = TRUE;
processCASbits($8, @8, "ASSERTION",
&n->deferrable, &n->initdeferred, NULL,
! yyscanner);
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
--- 4387,4393 ----
n->isconstraint = TRUE;
processCASbits($8, @8, "ASSERTION",
&n->deferrable, &n->initdeferred, NULL,
! NULL, yyscanner);
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
***************
*** 13380,13386 **** SplitColQualList(List *qualList,
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)
--- 13381,13387 ----
static void
processCASbits(int cas_bits, int location, const char *constrType,
bool *deferrable, bool *initdeferred, bool *not_valid,
! bool *no_inherit, core_yyscan_t yyscanner)
{
/* defaults */
if (deferrable)
***************
*** 13428,13433 **** processCASbits(int cas_bits, int location, const char *constrType,
--- 13429,13447 ----
constrType),
parser_errposition(location)));
}
+
+ if (cas_bits & CAS_NO_INHERIT)
+ {
+ if (no_inherit)
+ *no_inherit = true;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ /* translator: %s is CHECK, UNIQUE, or similar */
+ errmsg("%s constraints cannot be marked NO INHERIT",
+ constrType),
+ parser_errposition(location)));
+ }
}
/* parser_init()
*** a/src/backend/utils/adt/ruleutils.c
--- b/src/backend/utils/adt/ruleutils.c
***************
*** 1343,1352 **** pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
* Note that simply checking for leading '(' and trailing ')'
* would NOT be good enough, consider "(x > 0) AND (y > 0)".
*/
! appendStringInfo(&buf, "CHECK %s(%s)",
! conForm->connoinherit ? "NO INHERIT " : "",
! consrc);
!
break;
}
case CONSTRAINT_TRIGGER:
--- 1343,1351 ----
* Note that simply checking for leading '(' and trailing ')'
* would NOT be good enough, consider "(x > 0) AND (y > 0)".
*/
! appendStringInfo(&buf, "CHECK (%s)%s",
! consrc,
! conForm->connoinherit ? " NO INHERIT" : "");
break;
}
case CONSTRAINT_TRIGGER:
*** a/src/test/regress/expected/alter_table.out
--- b/src/test/regress/expected/alter_table.out
***************
*** 233,239 **** Check constraints:
"con1foo" CHECK (a > 0)
Inherits: constraint_rename_test
! ALTER TABLE constraint_rename_test ADD CONSTRAINT con2 CHECK NO INHERIT (b > 0);
ALTER TABLE ONLY constraint_rename_test RENAME CONSTRAINT con2 TO con2foo; -- ok
ALTER TABLE constraint_rename_test RENAME CONSTRAINT con2foo TO con2bar; -- ok
\d constraint_rename_test
--- 233,239 ----
"con1foo" CHECK (a > 0)
Inherits: constraint_rename_test
! ALTER TABLE constraint_rename_test ADD CONSTRAINT con2 CHECK (b > 0) NO INHERIT;
ALTER TABLE ONLY constraint_rename_test RENAME CONSTRAINT con2 TO con2foo; -- ok
ALTER TABLE constraint_rename_test RENAME CONSTRAINT con2foo TO con2bar; -- ok
\d constraint_rename_test
***************
*** 245,251 **** Table "public.constraint_rename_test"
c | integer |
Check constraints:
"con1foo" CHECK (a > 0)
! "con2bar" CHECK NO INHERIT (b > 0)
Number of child tables: 1 (Use \d+ to list them.)
\d constraint_rename_test2
--- 245,251 ----
c | integer |
Check constraints:
"con1foo" CHECK (a > 0)
! "con2bar" CHECK (b > 0) NO INHERIT
Number of child tables: 1 (Use \d+ to list them.)
\d constraint_rename_test2
***************
*** 273,279 **** Indexes:
"con3foo" PRIMARY KEY, btree (a)
Check constraints:
"con1foo" CHECK (a > 0)
! "con2bar" CHECK NO INHERIT (b > 0)
Number of child tables: 1 (Use \d+ to list them.)
\d constraint_rename_test2
--- 273,279 ----
"con3foo" PRIMARY KEY, btree (a)
Check constraints:
"con1foo" CHECK (a > 0)
! "con2bar" CHECK (b > 0) NO INHERIT
Number of child tables: 1 (Use \d+ to list them.)
\d constraint_rename_test2
***************
*** 635,641 **** drop table atacc1;
create table atacc1 (test int);
create table atacc2 (test2 int) inherits (atacc1);
-- ok:
! alter table atacc1 add constraint foo check no inherit (test>0);
-- check constraint is not there on child
insert into atacc2 (test) values (-3);
-- check constraint is there on parent
--- 635,641 ----
create table atacc1 (test int);
create table atacc2 (test2 int) inherits (atacc1);
-- ok:
! alter table atacc1 add constraint foo check (test>0) no inherit;
-- check constraint is not there on child
insert into atacc2 (test) values (-3);
-- check constraint is there on parent
***************
*** 644,650 **** ERROR: new row for relation "atacc1" violates check constraint "foo"
DETAIL: Failing row contains (-3).
insert into atacc1 (test) values (3);
-- fail, violating row:
! alter table atacc2 add constraint foo check no inherit (test>0);
ERROR: check constraint "foo" is violated by some row
drop table atacc2;
drop table atacc1;
--- 644,650 ----
DETAIL: Failing row contains (-3).
insert into atacc1 (test) values (3);
-- fail, violating row:
! alter table atacc2 add constraint foo check (test>0) no inherit;
ERROR: check constraint "foo" is violated by some row
drop table atacc2;
drop table atacc1;
*** a/src/test/regress/expected/inherit.out
--- b/src/test/regress/expected/inherit.out
***************
*** 597,603 **** select * from d;
-- Test non-inheritable parent constraints
create table p1(ff1 int);
! alter table p1 add constraint p1chk check no inherit (ff1 > 0);
alter table p1 add constraint p2chk check (ff1 > 10);
-- connoinherit should be true for NO INHERIT constraint
select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.connoinherit from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname = 'p1' order by 1,2;
--- 597,603 ----
-- Test non-inheritable parent constraints
create table p1(ff1 int);
! alter table p1 add constraint p1chk check (ff1 > 0) no inherit;
alter table p1 add constraint p2chk check (ff1 > 10);
-- connoinherit should be true for NO INHERIT constraint
select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.connoinherit from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname = 'p1' order by 1,2;
***************
*** 615,621 **** create table c1 () inherits (p1);
--------+---------+-----------
ff1 | integer |
Check constraints:
! "p1chk" CHECK NO INHERIT (ff1 > 0)
"p2chk" CHECK (ff1 > 10)
Number of child tables: 1 (Use \d+ to list them.)
--- 615,621 ----
--------+---------+-----------
ff1 | integer |
Check constraints:
! "p1chk" CHECK (ff1 > 0) NO INHERIT
"p2chk" CHECK (ff1 > 10)
Number of child tables: 1 (Use \d+ to list them.)
*** a/src/test/regress/input/constraints.source
--- b/src/test/regress/input/constraints.source
***************
*** 148,154 **** DROP TABLE INSERT_CHILD;
--
CREATE TABLE ATACC1 (TEST INT
! CHECK NO INHERIT (TEST > 0));
CREATE TABLE ATACC2 (TEST2 INT) INHERITS (ATACC1);
-- check constraint is not there on child
--- 148,154 ----
--
CREATE TABLE ATACC1 (TEST INT
! CHECK (TEST > 0) NO INHERIT);
CREATE TABLE ATACC2 (TEST2 INT) INHERITS (ATACC1);
-- check constraint is not there on child
***************
*** 158,164 **** INSERT INTO ATACC1 (TEST) VALUES (-3);
DROP TABLE ATACC1 CASCADE;
CREATE TABLE ATACC1 (TEST INT, TEST2 INT
! CHECK (TEST > 0), CHECK NO INHERIT (TEST2 > 10));
CREATE TABLE ATACC2 () INHERITS (ATACC1);
-- check constraint is there on child
--- 158,164 ----
DROP TABLE ATACC1 CASCADE;
CREATE TABLE ATACC1 (TEST INT, TEST2 INT
! CHECK (TEST > 0), CHECK (TEST2 > 10) NO INHERIT);
CREATE TABLE ATACC2 () INHERITS (ATACC1);
-- check constraint is there on child
*** a/src/test/regress/output/constraints.source
--- b/src/test/regress/output/constraints.source
***************
*** 231,237 **** DROP TABLE INSERT_CHILD;
-- Check NO INHERIT type of constraints and inheritance
--
CREATE TABLE ATACC1 (TEST INT
! CHECK NO INHERIT (TEST > 0));
CREATE TABLE ATACC2 (TEST2 INT) INHERITS (ATACC1);
-- check constraint is not there on child
INSERT INTO ATACC2 (TEST) VALUES (-3);
--- 231,237 ----
-- Check NO INHERIT type of constraints and inheritance
--
CREATE TABLE ATACC1 (TEST INT
! CHECK (TEST > 0) NO INHERIT);
CREATE TABLE ATACC2 (TEST2 INT) INHERITS (ATACC1);
-- check constraint is not there on child
INSERT INTO ATACC2 (TEST) VALUES (-3);
***************
*** 242,248 **** DETAIL: Failing row contains (-3).
DROP TABLE ATACC1 CASCADE;
NOTICE: drop cascades to table atacc2
CREATE TABLE ATACC1 (TEST INT, TEST2 INT
! CHECK (TEST > 0), CHECK NO INHERIT (TEST2 > 10));
CREATE TABLE ATACC2 () INHERITS (ATACC1);
-- check constraint is there on child
INSERT INTO ATACC2 (TEST) VALUES (-3);
--- 242,248 ----
DROP TABLE ATACC1 CASCADE;
NOTICE: drop cascades to table atacc2
CREATE TABLE ATACC1 (TEST INT, TEST2 INT
! CHECK (TEST > 0), CHECK (TEST2 > 10) NO INHERIT);
CREATE TABLE ATACC2 () INHERITS (ATACC1);
-- check constraint is there on child
INSERT INTO ATACC2 (TEST) VALUES (-3);
*** a/src/test/regress/sql/alter_table.sql
--- b/src/test/regress/sql/alter_table.sql
***************
*** 218,224 **** ALTER TABLE ONLY constraint_rename_test RENAME CONSTRAINT con1 TO con1foo; -- fa
ALTER TABLE constraint_rename_test RENAME CONSTRAINT con1 TO con1foo; -- ok
\d constraint_rename_test
\d constraint_rename_test2
! ALTER TABLE constraint_rename_test ADD CONSTRAINT con2 CHECK NO INHERIT (b > 0);
ALTER TABLE ONLY constraint_rename_test RENAME CONSTRAINT con2 TO con2foo; -- ok
ALTER TABLE constraint_rename_test RENAME CONSTRAINT con2foo TO con2bar; -- ok
\d constraint_rename_test
--- 218,224 ----
ALTER TABLE constraint_rename_test RENAME CONSTRAINT con1 TO con1foo; -- ok
\d constraint_rename_test
\d constraint_rename_test2
! ALTER TABLE constraint_rename_test ADD CONSTRAINT con2 CHECK (b > 0) NO INHERIT;
ALTER TABLE ONLY constraint_rename_test RENAME CONSTRAINT con2 TO con2foo; -- ok
ALTER TABLE constraint_rename_test RENAME CONSTRAINT con2foo TO con2bar; -- ok
\d constraint_rename_test
***************
*** 500,513 **** drop table atacc1;
create table atacc1 (test int);
create table atacc2 (test2 int) inherits (atacc1);
-- ok:
! alter table atacc1 add constraint foo check no inherit (test>0);
-- check constraint is not there on child
insert into atacc2 (test) values (-3);
-- check constraint is there on parent
insert into atacc1 (test) values (-3);
insert into atacc1 (test) values (3);
-- fail, violating row:
! alter table atacc2 add constraint foo check no inherit (test>0);
drop table atacc2;
drop table atacc1;
--- 500,513 ----
create table atacc1 (test int);
create table atacc2 (test2 int) inherits (atacc1);
-- ok:
! alter table atacc1 add constraint foo check (test>0) no inherit;
-- check constraint is not there on child
insert into atacc2 (test) values (-3);
-- check constraint is there on parent
insert into atacc1 (test) values (-3);
insert into atacc1 (test) values (3);
-- fail, violating row:
! alter table atacc2 add constraint foo check (test>0) no inherit;
drop table atacc2;
drop table atacc1;
*** a/src/test/regress/sql/inherit.sql
--- b/src/test/regress/sql/inherit.sql
***************
*** 140,146 **** select * from d;
-- Test non-inheritable parent constraints
create table p1(ff1 int);
! alter table p1 add constraint p1chk check no inherit (ff1 > 0);
alter table p1 add constraint p2chk check (ff1 > 10);
-- connoinherit should be true for NO INHERIT constraint
select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.connoinherit from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname = 'p1' order by 1,2;
--- 140,146 ----
-- Test non-inheritable parent constraints
create table p1(ff1 int);
! alter table p1 add constraint p1chk check (ff1 > 0) no inherit;
alter table p1 add constraint p2chk check (ff1 > 10);
-- connoinherit should be true for NO INHERIT constraint
select pc.relname, pgc.conname, pgc.contype, pgc.conislocal, pgc.coninhcount, pgc.connoinherit from pg_class as pc inner join pg_constraint as pgc on (pgc.conrelid = pc.oid) where pc.relname = 'p1' order by 1,2;
Excerpts from Alvaro Herrera's message of sáb jul 21 00:20:57 -0400 2012:
Here's a (hopefully) complete patch.
Pushed. I hope people like this one better (third time's the charm, and
all that).
--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support