diff --git a/doc/src/sgml/ref/create_domain.sgml b/doc/src/sgml/ref/create_domain.sgml index 73f9f28d6c..ce55520348 100644 --- a/doc/src/sgml/ref/create_domain.sgml +++ b/doc/src/sgml/ref/create_domain.sgml @@ -24,9 +24,9 @@ PostgreSQL documentation CREATE DOMAIN name [ AS ] data_type [ COLLATE collation ] [ DEFAULT expression ] - [ constraint [ ... ] ] + [ domain_constraint [ ... ] ] -where constraint is: +where domain_constraint is: [ CONSTRAINT constraint_name ] { NOT NULL | NULL | CHECK (expression) } @@ -190,7 +190,7 @@ CREATE DOMAIN name [ AS ] - + Notes @@ -279,6 +279,17 @@ CREATE TABLE us_snail_addy ( The command CREATE DOMAIN conforms to the SQL standard. + + + The syntax NOT NULL in this command is a + PostgreSQL extension. (A standard-conforming + way to write the same would be CHECK (VALUE IS NOT + NULL). However, per , + such constraints are best avoided in practice anyway.) The + NULL constraint is a + PostgreSQL extension (see also ). + diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 0523f7e891..e8b619926e 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -524,7 +524,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type generic_set set_rest set_rest_more generic_reset reset_rest SetResetClause FunctionSetResetClause -%type TableElement TypedTableElement ConstraintElem TableFuncElement +%type TableElement TypedTableElement ConstraintElem DomainConstraintElem TableFuncElement %type columnDef columnOptions optionalPeriodName %type def_elem reloption_elem old_aggr_elem operator_def_elem %type def_arg columnElem where_clause where_or_current_clause @@ -596,7 +596,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type col_name_keyword reserved_keyword %type bare_label_keyword -%type TableConstraint TableLikeClause +%type DomainConstraint TableConstraint TableLikeClause %type TableLikeOptionList TableLikeOption %type column_compression opt_column_compression column_storage opt_column_storage %type ColQualList @@ -4334,6 +4334,60 @@ ConstraintElem: } ; +/* + * DomainConstraint is separate from TableConstraint because the syntax for + * NOT NULL constraints is different. For table constraints, we need to + * accept a column name, but for domain constraints, we don't. (We could + * accept something like NOT NULL VALUE, but that seems weird.) CREATE DOMAIN + * (which uses ColQualList) has for a long time accepted NOT NULL without a + * column name, so it makes sense that ALTER DOMAIN (which uses + * DomainConstraint) does as well. None of these syntaxes are per SQL + * standard; we are just living with the bits of inconsistency that have built + * up over time. + */ +DomainConstraint: + CONSTRAINT name DomainConstraintElem + { + Constraint *n = castNode(Constraint, $3); + + n->conname = $2; + n->location = @1; + $$ = (Node *) n; + } + | DomainConstraintElem { $$ = $1; } + ; + +DomainConstraintElem: + 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; + } + | NOT NULL_P ConstraintAttributeSpec + { + Constraint *n = makeNode(Constraint); + + n->contype = CONSTR_NOTNULL; + n->location = @1; + n->keys = list_make1(makeString("value")); + /* no NOT VALID support yet */ + processCASbits($3, @3, "NOT NULL", + NULL, NULL, NULL, + &n->is_no_inherit, yyscanner); + n->initially_valid = true; + $$ = (Node *) n; + } + ; + opt_no_inherit: NO INHERIT { $$ = true; } | /* EMPTY */ { $$ = false; } ; @@ -11586,7 +11640,7 @@ AlterDomainStmt: $$ = (Node *) n; } /* ALTER DOMAIN ADD CONSTRAINT ... */ - | ALTER DOMAIN_P any_name ADD_P TableConstraint + | ALTER DOMAIN_P any_name ADD_P DomainConstraint { AlterDomainStmt *n = makeNode(AlterDomainStmt); diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 24e3514b00..b3428e2ae4 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -2523,7 +2523,7 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, else if (conForm->contypid) { /* conkey is null for domain not-null constraints */ - appendStringInfoString(&buf, "NOT NULL VALUE"); + appendStringInfoString(&buf, "NOT NULL"); } break; } diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 6433497bcd..1f0a056d90 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -4449,7 +4449,7 @@ listDomains(const char *pattern, bool verbose, bool showSystem) " CASE WHEN t.typnotnull THEN 'not null' END as \"%s\",\n" " t.typdefault as \"%s\",\n" " pg_catalog.array_to_string(ARRAY(\n" - " SELECT pg_catalog.pg_get_constraintdef(r.oid, true) FROM pg_catalog.pg_constraint r WHERE t.oid = r.contypid\n" + " SELECT pg_catalog.pg_get_constraintdef(r.oid, true) FROM pg_catalog.pg_constraint r WHERE t.oid = r.contypid AND r.contype = 'c'\n" " ), ' ') as \"%s\"", gettext_noop("Schema"), gettext_noop("Name"), diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out index fa8459e10f..db0b8a180a 100644 --- a/src/test/regress/expected/domain.out +++ b/src/test/regress/expected/domain.out @@ -785,6 +785,13 @@ alter domain con add constraint t check (VALUE < 1); -- fails ERROR: column "col1" of table "domcontest" contains values that violate the new constraint alter domain con add constraint t check (VALUE < 34); alter domain con add check (VALUE > 0); +\dD con + List of domains + Schema | Name | Type | Collation | Nullable | Default | Check +--------+------+---------+-----------+----------+---------+-------------------------------------- + public | con | integer | | | | CHECK (VALUE < 34) CHECK (VALUE > 0) +(1 row) + insert into domcontest values (-5); -- fails ERROR: value for domain con violates check constraint "con_check" insert into domcontest values (42); -- fails @@ -805,26 +812,33 @@ create table domconnotnulltest , col2 connotnull ); insert into domconnotnulltest default values; -alter domain connotnull add not null value; -- fails +alter domain connotnull add not null; -- fails ERROR: column "col1" of table "domconnotnulltest" contains null values update domconnotnulltest set col1 = 5; -alter domain connotnull add not null value; -- fails +alter domain connotnull add not null; -- fails ERROR: column "col2" of table "domconnotnulltest" contains null values update domconnotnulltest set col2 = 6; -alter domain connotnull add constraint constr1 not null value; +alter domain connotnull add constraint constr1 not null; select count(*) from pg_constraint where contypid = 'connotnull'::regtype and contype = 'n'; count ------- 1 (1 row) -alter domain connotnull add constraint constr1bis not null value; -- redundant +alter domain connotnull add constraint constr1bis not null; -- redundant select count(*) from pg_constraint where contypid = 'connotnull'::regtype and contype = 'n'; count ------- 1 (1 row) +\dD connotnull + List of domains + Schema | Name | Type | Collation | Nullable | Default | Check +--------+------------+---------+-----------+----------+---------+------- + public | connotnull | integer | | not null | | +(1 row) + update domconnotnulltest set col1 = null; -- fails ERROR: domain connotnull does not allow null values alter domain connotnull drop constraint constr1; diff --git a/src/test/regress/sql/domain.sql b/src/test/regress/sql/domain.sql index 763c68f1db..b5a70ee8be 100644 --- a/src/test/regress/sql/domain.sql +++ b/src/test/regress/sql/domain.sql @@ -458,6 +458,8 @@ alter domain con add constraint t check (VALUE < 1); -- fails alter domain con add constraint t check (VALUE < 34); alter domain con add check (VALUE > 0); +\dD con + insert into domcontest values (-5); -- fails insert into domcontest values (42); -- fails insert into domcontest values (5); @@ -477,18 +479,20 @@ create table domconnotnulltest ); insert into domconnotnulltest default values; -alter domain connotnull add not null value; -- fails +alter domain connotnull add not null; -- fails update domconnotnulltest set col1 = 5; -alter domain connotnull add not null value; -- fails +alter domain connotnull add not null; -- fails update domconnotnulltest set col2 = 6; -alter domain connotnull add constraint constr1 not null value; +alter domain connotnull add constraint constr1 not null; select count(*) from pg_constraint where contypid = 'connotnull'::regtype and contype = 'n'; -alter domain connotnull add constraint constr1bis not null value; -- redundant +alter domain connotnull add constraint constr1bis not null; -- redundant select count(*) from pg_constraint where contypid = 'connotnull'::regtype and contype = 'n'; +\dD connotnull + update domconnotnulltest set col1 = null; -- fails alter domain connotnull drop constraint constr1;