Re: Catalog domain not-null constraints

2024-04-15 Thread Peter Eisentraut

On 09.04.24 10:44, jian he wrote:

After studying this a bit more, I think moving forward in this direction
is the best way.  Attached is a new patch version, mainly with a more
elaborate commit message.  This patch makes the not-null constraint
syntax consistent between CREATE DOMAIN and ALTER DOMAIN, and also makes
the respective documentation correct.

(Note that, as I show in the commit message, commit e5da0fe3c22 had in
passing fixed a couple of bugs in CREATE and ALTER DOMAIN, so just
reverting that commit wouldn't be a complete solution.)



in ruleutils.c
/* conkey is null for domain not-null constraints */
appendStringInfoString(, "NOT NULL VALUE");

should be

/* conkey is null for domain not-null constraints */
appendStringInfoString(, "NOT NULL ");


Good catch, fixed.


currently
src6=# \dD connotnull
/ QUERY */
SELECT n.nspname as "Schema",
t.typname as "Name",
pg_catalog.format_type(t.typbasetype, t.typtypmod) as "Type",
(SELECT c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type bt
 WHERE c.oid = t.typcollation AND bt.oid = t.typbasetype AND
t.typcollation <> bt.typcollation) as "Collation",
CASE WHEN t.typnotnull THEN 'not null' END as "Nullable",
t.typdefault as "Default",
pg_catalog.array_to_string(ARRAY(
  SELECT pg_catalog.pg_get_constraintdef(r.oid, true) FROM
pg_catalog.pg_constraint r WHERE t.oid = r.contypid
), ' ') as "Check"
FROM pg_catalog.pg_type t
  LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
WHERE t.typtype = 'd'
   AND t.typname OPERATOR(pg_catalog.~) '^(connotnull)$' COLLATE
pg_catalog.default
   AND pg_catalog.pg_type_is_visible(t.oid)
ORDER BY 1, 2;
//

---
Since the last column is already named as "Check", maybe we need to
change the query to
pg_catalog.array_to_string(ARRAY(
  SELECT pg_catalog.pg_get_constraintdef(r.oid, true) FROM
pg_catalog.pg_constraint r WHERE t.oid = r.contypid
  and r.contype = 'c'
), ' ') as "Check"

That means domain can be associated with check constraint and not-null
constraint.


Yes, I changed it like you wrote.


the url link destination is fine, but the url rendered name is "per
the section called “Notes”" which seems strange,
please see attached.


Hmm, changing that would be an independent project.

I have committed the patch with the two amendments you provided.

I had also added a test of \dD and that caused some test output 
instability, so I added a ORDER BY r.conname to the \dD query.






Re: Catalog domain not-null constraints

2024-04-08 Thread Peter Eisentraut

On 21.03.24 12:23, Peter Eisentraut wrote:

All the examples in the tests append "value" to this, presumably by
analogy with CHECK constraints, but it looks as though anything works,
and is simply ignored:

ALTER DOMAIN d ADD CONSTRAINT nn NOT NULL xxx; -- works

That doesn't seem particularly satisfactory. I think it should not
require (and reject) a column name after "NOT NULL".


Hmm.  CREATE DOMAIN uses column constraint syntax, but ALTER DOMAIN uses 
table constraint syntax.  As long as you are only dealing with CHECK 
constraints, there is no difference, but it shows up when using NOT NULL 
constraint syntax.  I agree that this is unsatisfactory.  Attached is a 
patch to try to sort this out.


After studying this a bit more, I think moving forward in this direction 
is the best way.  Attached is a new patch version, mainly with a more 
elaborate commit message.  This patch makes the not-null constraint 
syntax consistent between CREATE DOMAIN and ALTER DOMAIN, and also makes 
the respective documentation correct.


(Note that, as I show in the commit message, commit e5da0fe3c22 had in 
passing fixed a couple of bugs in CREATE and ALTER DOMAIN, so just 
reverting that commit wouldn't be a complete solution.)
From 4e4de402dda81798bb0286a09d39c6ed2f087228 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut 
Date: Mon, 8 Apr 2024 11:39:48 +0200
Subject: [PATCH v2] Fix ALTER DOMAIN NOT NULL syntax

In CREATE DOMAIN, a NOT NULL constraint looks like

CREATE DOMAIN d1 AS int [ CONSTRAINT conname ] NOT NULL

(Before e5da0fe3c22, the constraint name was accepted but ignored.)

But in ALTER DOMAIN, a NOT NULL constraint looks like

ALTER DOMAIN d1 ADD [ CONSTRAINT conname ] NOT NULL VALUE

where VALUE is where for a table constraint the column name would be.
(This works as of e5da0fe3c22.  Before e5da0fe3c22, this syntax
resulted in an internal error.)

But for domains, this latter syntax is confusing and needlessly
inconsistent between CREATE and ALTER.  So this changes it to just

ALTER DOMAIN d1 ADD [ CONSTRAINT conname ] NOT NULL

(None of these syntaxes are per SQL standard; we are just living with
the bits of inconsistency that have built up over time.)

Discussion: 
https://www.postgresql.org/message-id/flat/9ec24d7b-633d-463a-84c6-7acff769c9e8%40eisentraut.org
---
 doc/src/sgml/ref/create_domain.sgml  | 17 ++--
 src/backend/parser/gram.y| 60 ++--
 src/test/regress/expected/domain.out |  8 ++--
 src/test/regress/sql/domain.sql  |  8 ++--
 4 files changed, 79 insertions(+), 14 deletions(-)

diff --git a/doc/src/sgml/ref/create_domain.sgml 
b/doc/src/sgml/ref/create_domain.sgml
index 73f9f28d6cf..078bea8410d 100644
--- a/doc/src/sgml/ref/create_domain.sgml
+++ b/doc/src/sgml/ref/create_domain.sgml
@@ -24,9 +24,9 @@
 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 @@ Parameters
   
  
 
- 
+ 
   Notes
 
   
@@ -279,6 +279,17 @@ Compatibility
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 a 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 0523f7e891e..e8b619926ef 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
 
-%typeTableElement TypedTableElement ConstraintElem TableFuncElement
+%typeTableElement TypedTableElement ConstraintElem 
DomainConstraintElem TableFuncElement
 %typecolumnDef columnOptions optionalPeriodName
 %type  def_elem reloption_elem old_aggr_elem operator_def_elem
 %typedef_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
 
-%typeTableConstraint TableLikeClause
+%typeDomainConstraint TableConstraint TableLikeClause
 %typeTableLikeOptionList TableLikeOption
 %type column_compression opt_column_compression 
column_storage opt_column_storage
 %typeColQualList
@@ -4334,6 +4334,60 @@ ConstraintElem:
}
;
 
+/*
+ * DomainConstraint is separate from TableConstraint because the syntax for
+ * NOT NULL constraints is 

Re: Catalog domain not-null constraints

2024-03-31 Thread jian he
On Tue, Mar 26, 2024 at 2:28 AM Dean Rasheed  wrote:
>
> On Fri, 22 Mar 2024 at 08:28, jian he  wrote:
> >
> > On Thu, Mar 21, 2024 at 7:23 PM Peter Eisentraut  
> > wrote:
> > >
> > > Hmm.  CREATE DOMAIN uses column constraint syntax, but ALTER DOMAIN uses
> > > table constraint syntax.  Attached is a patch to try to sort this out.
> >
> > also you should also change src/backend/utils/adt/ruleutils.c?
> >
> > src6=# \dD
> >   List of domains
> >  Schema |Name |  Type   | Collation | Nullable | Default |
> >  Check
> > +-+-+---+--+-+--
> >  public | domain_test | integer |   | not null | |
> > CHECK (VALUE > 0) NOT NULL VALUE
> > (1 row)
> >
> > probably change to CHECK (VALUE IS NOT NULL)
>
> I'd say it should just output "NOT NULL", since that's the input
> syntax that created the constraint. But then again, why display NOT
> NULL constraints in that column at all, when there's a separate
> "Nullable" column?
>
create table sss(a int not null);
SELECT pg_get_constraintdef(oid) FROM pg_constraint WHERE conname =
'sss_a_not_null';
returns
" NOT NULL a"

I think just outputting "NOT NULL" is ok for the domain, given the
table constraint is "NOT NULL" + table column, per above example.
yech, we already have a "Nullable" column, so we don't need to display
 NOT NULL constraints.




Re: Catalog domain not-null constraints

2024-03-26 Thread Dean Rasheed
On Tue, 26 Mar 2024 at 07:30, Alvaro Herrera  wrote:
>
> On 2024-Mar-25, Dean Rasheed wrote:
>
> > Also (not this patch's fault), psql doesn't seem to offer a way to
> > display domain constraint names -- something you need to know to drop
> > or alter them. Perhaps \dD+ could be made to do that?
>
> Ooh, I remember we had offered a patch for \d++ to display these
> constraint names for tables, but didn't get around to gather consensus
> for it.  We did gather consensus on *not* wanting \d+ to display them,
> but we need *something*.  I suppose we should do something symmetrical
> for tables and domains.  How about \dD++ and \dt++?
>

Personally, I quite like the fact that \d+ displays NOT NULL
constraints, because it puts them on an equal footing with CHECK
constraints. However, I can appreciate that it will significantly
increase the length of the output in some cases.

With \dD it's not so nice because of the way it puts all the details
on one line. The obvious output might look something like this:

\dD
   List of domains
 Schema | Name |  Type   | Collation | Nullable | Default |  Check
+--+-+---+--+-+---
 public | d1   | integer |   | NOT NULL | | CHECK (VALUE > 0)

\dD+
List of domains
 Schema | Name |  Type   | Collation | Nullable
| Default |Check  | Access privileges
| Description
+--+-+---+-+-+---+---+-
 public | d1   | integer |   | CONSTRAINT d1_not_null NOT NULL
| | CONSTRAINT d1_check CHECK (VALUE > 0) |
|

So you'd need quite a wide window to easily view it (or use \x). I
suppose the width could be reduced by dropping the word "CONSTRAINT"
in the \dD+ case, but it's probably still going to be wider than the
average window.

Regards,
Dean




Re: Catalog domain not-null constraints

2024-03-26 Thread Alvaro Herrera
On 2024-Mar-25, Dean Rasheed wrote:

> Also (not this patch's fault), psql doesn't seem to offer a way to
> display domain constraint names -- something you need to know to drop
> or alter them. Perhaps \dD+ could be made to do that?

Ooh, I remember we had offered a patch for \d++ to display these
constraint names for tables, but didn't get around to gather consensus
for it.  We did gather consensus on *not* wanting \d+ to display them,
but we need *something*.  I suppose we should do something symmetrical
for tables and domains.  How about \dD++ and \dt++?

-- 
Álvaro HerreraBreisgau, Deutschland  —  https://www.EnterpriseDB.com/
Bob [Floyd] used to say that he was planning to get a Ph.D. by the "green
stamp method," namely by saving envelopes addressed to him as 'Dr. Floyd'.
After collecting 500 such letters, he mused, a university somewhere in
Arizona would probably grant him a degree.  (Don Knuth)




Re: Catalog domain not-null constraints

2024-03-25 Thread Dean Rasheed
On Fri, 22 Mar 2024 at 08:28, jian he  wrote:
>
> On Thu, Mar 21, 2024 at 7:23 PM Peter Eisentraut  wrote:
> >
> > Hmm.  CREATE DOMAIN uses column constraint syntax, but ALTER DOMAIN uses
> > table constraint syntax.  Attached is a patch to try to sort this out.
>
> also you should also change src/backend/utils/adt/ruleutils.c?
>
> src6=# \dD
>   List of domains
>  Schema |Name |  Type   | Collation | Nullable | Default |
>  Check
> +-+-+---+--+-+--
>  public | domain_test | integer |   | not null | |
> CHECK (VALUE > 0) NOT NULL VALUE
> (1 row)
>
> probably change to CHECK (VALUE IS NOT NULL)

I'd say it should just output "NOT NULL", since that's the input
syntax that created the constraint. But then again, why display NOT
NULL constraints in that column at all, when there's a separate
"Nullable" column?

Also (not this patch's fault), psql doesn't seem to offer a way to
display domain constraint names -- something you need to know to drop
or alter them. Perhaps \dD+ could be made to do that?

+   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 a best avoided in practice anyway.)  The
+   NULL constraint is a
+   PostgreSQL extension (see also ).

I didn't verify this, but I thought that according to the SQL
standard, only non-NULL values should be passed to CHECK constraints,
so there is no standard-conforming way to write a NOT NULL domain
constraint.

FWIW, I think NOT NULL domain constraints are a useful feature to
have, and I suspect that there are more people out there who use them
and like them, than who care what the SQL standard says. If so, I'm in
favour of allowing them to be named and managed in the same way as NOT
NULL table constraints.

+   processCASbits($5, @5, "CHECK",
+  NULL, NULL, >skip_validation,
+  >is_no_inherit, yyscanner);
+   n->initially_valid = !n->skip_validation;

+   /* no NOT VALID support yet */
+   processCASbits($3, @3, "NOT NULL",
+  NULL, NULL, NULL,
+  >is_no_inherit, yyscanner);
+   n->initially_valid = true;

NO INHERIT is allowed for domain constraints? What does that even mean?

There's something very wonky about this:

CREATE DOMAIN d1 AS int CHECK (value > 0) NO INHERIT; -- Rejected
ERROR:  check constraints for domains cannot be marked NO INHERIT

CREATE DOMAIN d1 AS int;
ALTER DOMAIN d1 ADD CHECK (value > 0) NO INHERIT; -- Allowed

CREATE DOMAIN d2 AS int NOT NULL NO INHERIT; -- Now allowed (used to
syntax error)

CREATE DOMAIN d3 AS int;
ALTER DOMAIN d3 ADD NOT NULL NO INHERIT; -- Allowed

Presumably all of those should be rejected in the grammar.

Regards,
Dean




Re: Catalog domain not-null constraints

2024-03-22 Thread jian he
On Thu, Mar 21, 2024 at 7:23 PM Peter Eisentraut  wrote:
>
> On 20.03.24 12:22, Dean Rasheed wrote:
> > Hmm, for CHECK constraints, the ALTER DOMAIN syntax for adding a
> > constraint is the same as for CREATE DOMAIN, but that's not the case
> > for NOT NULL constraints. So, for example, these both work:
> >
> > CREATE DOMAIN d AS int CONSTRAINT c1 CHECK (value > 0);
> >
> > ALTER DOMAIN d ADD CONSTRAINT c2 CHECK (value < 10);
> >
> > However, for NOT NULL constraints, the ALTER DOMAIN syntax differs
> > from the CREATE DOMAIN syntax, because it expects "NOT NULL" to be
> > followed by a column name. So the following CREATE DOMAIN syntax
> > works:
> >
> > CREATE DOMAIN d AS int CONSTRAINT nn NOT NULL;
> >
> > but the equivalent ALTER DOMAIN syntax doesn't work:
> >
> > ALTER DOMAIN d ADD CONSTRAINT nn NOT NULL;
> >
> > ERROR:  syntax error at or near ";"
> > LINE 1: ALTER DOMAIN d ADD CONSTRAINT nn NOT NULL;
> >   ^
> >
> > All the examples in the tests append "value" to this, presumably by
> > analogy with CHECK constraints, but it looks as though anything works,
> > and is simply ignored:
> >
> > ALTER DOMAIN d ADD CONSTRAINT nn NOT NULL xxx; -- works
> >
> > That doesn't seem particularly satisfactory. I think it should not
> > require (and reject) a column name after "NOT NULL".
>
> Hmm.  CREATE DOMAIN uses column constraint syntax, but ALTER DOMAIN uses
> table constraint syntax.  As long as you are only dealing with CHECK
> constraints, there is no difference, but it shows up when using NOT NULL
> constraint syntax.  I agree that this is unsatisfactory.  Attached is a
> patch to try to sort this out.
>


+ | 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,
+   >is_no_inherit, yyscanner);
+ n->initially_valid = true;
+ $$ = (Node *) n;
+ }

i don't understand this part.
+ n->keys = list_make1(makeString("value"));

also you should also change src/backend/utils/adt/ruleutils.c?

src6=# create domain domain_test integer;
alter domain domain_test add constraint pos1 check (value > 0);
alter domain domain_test add constraint constr1 not null ;
CREATE DOMAIN
ALTER DOMAIN
ALTER DOMAIN
src6=# \dD
  List of domains
 Schema |Name |  Type   | Collation | Nullable | Default |
 Check
+-+-+---+--+-+--
 public | domain_test | integer |   | not null | |
CHECK (VALUE > 0) NOT NULL VALUE
(1 row)

probably change to CHECK (VALUE IS NOT NULL)

-   appendStringInfoString(,
"NOT NULL VALUE");
+   appendStringInfoString(,
"CHECK (VALUE IS NOT NULL)");
seems works.




Re: Catalog domain not-null constraints

2024-03-21 Thread Vik Fearing

On 3/22/24 01:46, Tom Lane wrote:

Vik Fearing  writes:

Anyway, I will bring this up with the committee and report back.  My
proposed solution will be for CAST to check domain constraints even if
the input is NULL.


Please do not claim that that is the position of the Postgres project.



Everything that I do on the SQL Committee is in my own name.

I do not speak for either PostgreSQL or for EDB (my employer), even 
though my opinions are of course often influenced by some combination of 
both.

--
Vik Fearing





Re: Catalog domain not-null constraints

2024-03-21 Thread Tom Lane
Vik Fearing  writes:
> On 3/22/24 00:17, Tom Lane wrote:
>> Vik Fearing  writes:
>>> As also said somewhere in that thread, I think that 
>>> short-cutting a NULL input value without considering the constraints of
>>> a domain is a bug that needs to be fixed in the standard.

>> I think it's probably intentional.  It certainly fits with the lack of
>> syntax for DOMAIN NOT NULL.  Also, it's been like that since SQL99;
>> do you think nobody's noticed it for 25 years?

> Haven't we (postgres) had bug reports of similar age?

Well, they've looked it at it since then.  SQL99 has

c) If SV is the null value, then the result is the null value.

SQL:2008 and later have the text I quoted:

c) If SV is the null value, then the result of CS is the null
value and no further General Rules of this Sub-clause are
applied.

I find it *extremely* hard to believe that they would have added
that explicit text without noticing exactly which operations they
were saying to skip.

> Anyway, I will bring this up with the committee and report back.  My 
> proposed solution will be for CAST to check domain constraints even if 
> the input is NULL.

Please do not claim that that is the position of the Postgres project.

regards, tom lane




Re: Catalog domain not-null constraints

2024-03-21 Thread Vik Fearing

On 3/22/24 00:17, Tom Lane wrote:

Vik Fearing  writes:

On 3/21/24 15:30, Tom Lane wrote:

The SQL spec's answer to that conundrum appears to be "NULL is
a valid value of every domain, and if you don't like it, tough".



I don't see how you can infer this from the standard at all.


I believe where we got that from is 6.13 ,
which quoth (general rule 2):

 c) If SV is the null value, then the result of CS is the null
 value and no further General Rules of this Subclause are applied.

In particular, that short-circuits application of the domain
constraints (GR 23), implying that CAST(NULL AS some_domain) is
always successful.  Now you could argue that there's some other
context that would reject nulls, but being inconsistent with
CAST would seem more like a bug than a feature.



I think the main bug is in what you quoted from .

I believe that the POLA for casting to a domain is for all constraints 
of the domain to be verified for ALL values including the null value.




As also said somewhere in that thread, I think that 
short-cutting a NULL input value without considering the constraints of
a domain is a bug that needs to be fixed in the standard.


I think it's probably intentional.  It certainly fits with the lack of
syntax for DOMAIN NOT NULL.  Also, it's been like that since SQL99;
do you think nobody's noticed it for 25 years?



Haven't we (postgres) had bug reports of similar age?

There is also the possibility that no one has noticed because major 
players have not implemented domains.  For example, Oracle only just got 
them last year: 
https://blogs.oracle.com/coretec/post/less-coding-with-sql-domains-in-23c


Anyway, I will bring this up with the committee and report back.  My 
proposed solution will be for CAST to check domain constraints even if 
the input is NULL.

--
Vik Fearing





Re: Catalog domain not-null constraints

2024-03-21 Thread Tom Lane
Vik Fearing  writes:
> On 3/21/24 15:30, Tom Lane wrote:
>> The SQL spec's answer to that conundrum appears to be "NULL is
>> a valid value of every domain, and if you don't like it, tough".

> I don't see how you can infer this from the standard at all.

I believe where we got that from is 6.13 ,
which quoth (general rule 2):

c) If SV is the null value, then the result of CS is the null
value and no further General Rules of this Subclause are applied.

In particular, that short-circuits application of the domain
constraints (GR 23), implying that CAST(NULL AS some_domain) is
always successful.  Now you could argue that there's some other
context that would reject nulls, but being inconsistent with
CAST would seem more like a bug than a feature.

> As also said somewhere in that thread, I think that  
> short-cutting a NULL input value without considering the constraints of 
> a domain is a bug that needs to be fixed in the standard.

I think it's probably intentional.  It certainly fits with the lack of
syntax for DOMAIN NOT NULL.  Also, it's been like that since SQL99;
do you think nobody's noticed it for 25 years?

regards, tom lane




Re: Catalog domain not-null constraints

2024-03-21 Thread Vik Fearing

On 3/21/24 15:30, Tom Lane wrote:

Peter Eisentraut  writes:


A quick reading of the SQL standard suggests to me that the way we are
doing null handling in domain constraints is all wrong.  The standard
says that domain constraints are only checked on values that are not
null.  So both the handling of constraints using the CHECK syntax is
nonstandard and the existence of explicit NOT NULL constraints is an
extension.  The CREATE DOMAIN reference page already explains why all of
this is a bad idea.  Do we want to document all of that further, or
maybe we just want to rip out domain not-null constraints, or at least
not add further syntax for it?



Yeah.  The real problem with domain not null is: how can a column
that's propagated up through the nullable side of an outer join
still be considered to belong to such a domain?



Per spec, it is not considered to be so.  The domain only applies to 
table storage and CASTs and gets "forgotten" in a query.




The SQL spec's answer to that conundrum appears to be "NULL is
a valid value of every domain, and if you don't like it, tough".



I don't see how you can infer this from the standard at all.



I'm too lazy to search the archives, but we have had at least one
previous discussion about how we should adopt the spec's semantics.
It'd be an absolutely trivial fix in CoerceToDomain (succeed
immediately if input is NULL), but the question is what to do
with existing "DOMAIN NOT NULL" DDL.



Here is a semi-random link into a conversation you and I have recently 
had about this: 
https://www.postgresql.org/message-id/a13db59c-c68f-4a30-87a5-177fe135665e%40postgresfriends.org


As also said somewhere in that thread, I think that  
short-cutting a NULL input value without considering the constraints of 
a domain is a bug that needs to be fixed in the standard.

--
Vik Fearing





Re: Catalog domain not-null constraints

2024-03-21 Thread Isaac Morland
On Thu, 21 Mar 2024 at 10:30, Tom Lane  wrote:


> The SQL spec's answer to that conundrum appears to be "NULL is
> a valid value of every domain, and if you don't like it, tough".
>

To be fair, NULL is a valid value of every type. Even VOID has NULL.

In this context, it’s a bit weird to be able to decree up front when
defining a type that no table column of that type, anywhere, may ever
contain a NULL. It would be nice if there was a way to reverse the default
so that if you (almost or) never want NULLs anywhere that’s what you get
without saying "NOT NULL" all over the place, and instead just specify
"NULLABLE" (or something) where you want. But that effectively means
optionally changing the behaviour of CREATE TABLE and ALTER TABLE.


Re: Catalog domain not-null constraints

2024-03-21 Thread Tom Lane
Peter Eisentraut  writes:
> 
> A quick reading of the SQL standard suggests to me that the way we are 
> doing null handling in domain constraints is all wrong.  The standard 
> says that domain constraints are only checked on values that are not 
> null.  So both the handling of constraints using the CHECK syntax is 
> nonstandard and the existence of explicit NOT NULL constraints is an 
> extension.  The CREATE DOMAIN reference page already explains why all of 
> this is a bad idea.  Do we want to document all of that further, or 
> maybe we just want to rip out domain not-null constraints, or at least 
> not add further syntax for it?
> 

Yeah.  The real problem with domain not null is: how can a column
that's propagated up through the nullable side of an outer join
still be considered to belong to such a domain?

The SQL spec's answer to that conundrum appears to be "NULL is
a valid value of every domain, and if you don't like it, tough".
I'm too lazy to search the archives, but we have had at least one
previous discussion about how we should adopt the spec's semantics.
It'd be an absolutely trivial fix in CoerceToDomain (succeed
immediately if input is NULL), but the question is what to do
with existing "DOMAIN NOT NULL" DDL.

Anyway, now that I recall all that, e5da0fe3c is throwing good work
after bad, and I wonder if we shouldn't revert it.

regards, tom lane




Re: Catalog domain not-null constraints

2024-03-21 Thread Peter Eisentraut

On 20.03.24 12:22, Dean Rasheed wrote:

Hmm, for CHECK constraints, the ALTER DOMAIN syntax for adding a
constraint is the same as for CREATE DOMAIN, but that's not the case
for NOT NULL constraints. So, for example, these both work:

CREATE DOMAIN d AS int CONSTRAINT c1 CHECK (value > 0);

ALTER DOMAIN d ADD CONSTRAINT c2 CHECK (value < 10);

However, for NOT NULL constraints, the ALTER DOMAIN syntax differs
from the CREATE DOMAIN syntax, because it expects "NOT NULL" to be
followed by a column name. So the following CREATE DOMAIN syntax
works:

CREATE DOMAIN d AS int CONSTRAINT nn NOT NULL;

but the equivalent ALTER DOMAIN syntax doesn't work:

ALTER DOMAIN d ADD CONSTRAINT nn NOT NULL;

ERROR:  syntax error at or near ";"
LINE 1: ALTER DOMAIN d ADD CONSTRAINT nn NOT NULL;
  ^

All the examples in the tests append "value" to this, presumably by
analogy with CHECK constraints, but it looks as though anything works,
and is simply ignored:

ALTER DOMAIN d ADD CONSTRAINT nn NOT NULL xxx; -- works

That doesn't seem particularly satisfactory. I think it should not
require (and reject) a column name after "NOT NULL".


Hmm.  CREATE DOMAIN uses column constraint syntax, but ALTER DOMAIN uses 
table constraint syntax.  As long as you are only dealing with CHECK 
constraints, there is no difference, but it shows up when using NOT NULL 
constraint syntax.  I agree that this is unsatisfactory.  Attached is a 
patch to try to sort this out.



Looking in the SQL spec, it seems to only mention adding CHECK
constraints to domains, so the option to add NOT NULL constraints
should probably be listed in the "Compatibility" section.




A quick reading of the SQL standard suggests to me that the way we are 
doing null handling in domain constraints is all wrong.  The standard 
says that domain constraints are only checked on values that are not 
null.  So both the handling of constraints using the CHECK syntax is 
nonstandard and the existence of explicit NOT NULL constraints is an 
extension.  The CREATE DOMAIN reference page already explains why all of 
this is a bad idea.  Do we want to document all of that further, or 
maybe we just want to rip out domain not-null constraints, or at least 
not add further syntax for it?



From 0bdc4944879670b7b53447833fa9f9b385eb0309 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut 
Date: Thu, 21 Mar 2024 12:15:11 +0100
Subject: [PATCH] WIP: Fix ALTER DOMAIN NOT NULL syntax

---
 doc/src/sgml/ref/create_domain.sgml  | 17 ++--
 src/backend/parser/gram.y| 60 ++--
 src/test/regress/expected/domain.out |  8 ++--
 src/test/regress/sql/domain.sql  |  8 ++--
 4 files changed, 79 insertions(+), 14 deletions(-)

diff --git a/doc/src/sgml/ref/create_domain.sgml 
b/doc/src/sgml/ref/create_domain.sgml
index 73f9f28d6cf..078bea8410d 100644
--- a/doc/src/sgml/ref/create_domain.sgml
+++ b/doc/src/sgml/ref/create_domain.sgml
@@ -24,9 +24,9 @@
 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 @@ Parameters
   
  
 
- 
+ 
   Notes
 
   
@@ -279,6 +279,17 @@ Compatibility
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 a 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 39a801a1c38..391a708467e 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -522,7 +522,7 @@ static Node *makeRecursiveViewSelect(char *relname, List 
*aliases, Node *query);
 %type  generic_set set_rest set_rest_more generic_reset reset_rest
 SetResetClause FunctionSetResetClause
 
-%typeTableElement TypedTableElement ConstraintElem TableFuncElement
+%typeTableElement TypedTableElement ConstraintElem 
DomainConstraintElem TableFuncElement
 %typecolumnDef columnOptions
 %type  def_elem reloption_elem old_aggr_elem operator_def_elem
 %typedef_arg columnElem where_clause where_or_current_clause
@@ -593,7 +593,7 @@ static Node *makeRecursiveViewSelect(char *relname, List 
*aliases, Node *query);
 %type  col_name_keyword reserved_keyword
 %type  bare_label_keyword
 
-%typeTableConstraint TableLikeClause
+%typeDomainConstraint TableConstraint TableLikeClause
 %typeTableLikeOptionList TableLikeOption
 %type column_compression opt_column_compression 
column_storage opt_column_storage
 %typeColQualList
@@ -4251,6 +4251,60 @@ ConstraintElem:
 

Re: Catalog domain not-null constraints

2024-03-20 Thread Dean Rasheed
On Wed, 20 Mar 2024 at 09:43, Peter Eisentraut  wrote:
>
> On 19.03.24 10:57, jian he wrote:
> > this new syntax need to be added into the alter_domain.sgml's synopsis and 
> > also
> > need an explanation varlistentry?
>
> The ALTER DOMAIN reference page refers to CREATE DOMAIN about the
> details of the constraint syntax.  I believe this is still accurate.  We
> could add more detail locally on the ALTER DOMAIN page, but that is not
> this patch's job.  For example, the details of CHECK constraints are
> also not shown on the ALTER DOMAIN page right now.
>

Hmm, for CHECK constraints, the ALTER DOMAIN syntax for adding a
constraint is the same as for CREATE DOMAIN, but that's not the case
for NOT NULL constraints. So, for example, these both work:

CREATE DOMAIN d AS int CONSTRAINT c1 CHECK (value > 0);

ALTER DOMAIN d ADD CONSTRAINT c2 CHECK (value < 10);

However, for NOT NULL constraints, the ALTER DOMAIN syntax differs
from the CREATE DOMAIN syntax, because it expects "NOT NULL" to be
followed by a column name. So the following CREATE DOMAIN syntax
works:

CREATE DOMAIN d AS int CONSTRAINT nn NOT NULL;

but the equivalent ALTER DOMAIN syntax doesn't work:

ALTER DOMAIN d ADD CONSTRAINT nn NOT NULL;

ERROR:  syntax error at or near ";"
LINE 1: ALTER DOMAIN d ADD CONSTRAINT nn NOT NULL;
 ^

All the examples in the tests append "value" to this, presumably by
analogy with CHECK constraints, but it looks as though anything works,
and is simply ignored:

ALTER DOMAIN d ADD CONSTRAINT nn NOT NULL xxx; -- works

That doesn't seem particularly satisfactory. I think it should not
require (and reject) a column name after "NOT NULL".

Looking in the SQL spec, it seems to only mention adding CHECK
constraints to domains, so the option to add NOT NULL constraints
should probably be listed in the "Compatibility" section.

Regards,
Dean




Re: Catalog domain not-null constraints

2024-03-20 Thread Peter Eisentraut

On 19.03.24 10:57, jian he wrote:

this new syntax need to be added into the alter_domain.sgml's synopsis and also
need an explanation varlistentry?


The ALTER DOMAIN reference page refers to CREATE DOMAIN about the 
details of the constraint syntax.  I believe this is still accurate.  We 
could add more detail locally on the ALTER DOMAIN page, but that is not 
this patch's job.  For example, the details of CHECK constraints are 
also not shown on the ALTER DOMAIN page right now.



+  false, /* connoinherit */
+  false, /* conwithoutoverlaps */
+  false); /* is_internal */

/* conwithoutoverlaps */
should be
/* conperiod */


Good catch, thanks.




Re: Catalog domain not-null constraints

2024-03-20 Thread Peter Eisentraut

On 18.03.24 11:02, Aleksander Alekseev wrote:

Hi,


Anyway, in order to move this forward, here is an updated patch where
the ADD CONSTRAINT ... NOT NULL behavior for domains matches the
idempotent behavior of tables.  This uses the patch that Jian He posted.


I tested the patch on Raspberry Pi 5 and Intel MacBook and also
experimented with it. Everything seems to work properly.

Personally I believe new functions such as
validateDomainNotNullConstraint() and findDomainNotNullConstraint()
could use a few lines of comments (accepts..., returns..., etc).


Done.


Also
I think that the commit message should explicitly say that supporting
NOT VALID constraints is out of scope of this patch.


Not done.  I don't know what NOT VALID has to do with this.  This patch 
just changes the internal catalog representation, it doesn't claim to 
add or change any features.  The documentation already accurately states 
that NOT VALID is not supported for NOT NULL constraints.



Except for named nitpicks v4 LGTM.


Committed, thanks.





Re: Catalog domain not-null constraints

2024-03-19 Thread jian he
create domain connotnull integer;
create table domconnotnulltest
( col1 connotnull
, col2 connotnull
);
alter domain connotnull add not null value;
---
the above query does not work in pg16.
ERROR: syntax error at or near "not".

after applying the patch, now this works.
this new syntax need to be added into the alter_domain.sgml's synopsis and also
need an explanation varlistentry?

+ /*
+ * Store the constraint in pg_constraint
+ */
+ ccoid =
+ CreateConstraintEntry(constr->conname, /* Constraint Name */
+  domainNamespace, /* namespace */
+  CONSTRAINT_NOTNULL, /* Constraint Type */
+  false, /* Is Deferrable */
+  false, /* Is Deferred */
+  !constr->skip_validation, /* Is Validated */
+  InvalidOid, /* no parent constraint */
+  InvalidOid, /* not a relation constraint */
+  NULL,
+  0,
+  0,
+  domainOid, /* domain constraint */
+  InvalidOid, /* no associated index */
+  InvalidOid, /* Foreign key fields */
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  0,
+  ' ',
+  ' ',
+  NULL,
+  0,
+  ' ',
+  NULL, /* not an exclusion constraint */
+  NULL,
+  NULL,
+  true, /* is local */
+  0, /* inhcount */
+  false, /* connoinherit */
+  false, /* conwithoutoverlaps */
+  false); /* is_internal */

/* conwithoutoverlaps */
should be
/* conperiod */




Re: Catalog domain not-null constraints

2024-03-18 Thread Aleksander Alekseev
Hi,

> Anyway, in order to move this forward, here is an updated patch where
> the ADD CONSTRAINT ... NOT NULL behavior for domains matches the
> idempotent behavior of tables.  This uses the patch that Jian He posted.

I tested the patch on Raspberry Pi 5 and Intel MacBook and also
experimented with it. Everything seems to work properly.

Personally I believe new functions such as
validateDomainNotNullConstraint() and findDomainNotNullConstraint()
could use a few lines of comments (accepts..., returns..., etc). Also
I think that the commit message should explicitly say that supporting
NOT VALID constraints is out of scope of this patch.

Except for named nitpicks v4 LGTM.

-- 
Best regards,
Aleksander Alekseev




Re: Catalog domain not-null constraints

2024-03-18 Thread Peter Eisentraut
Anyway, in order to move this forward, here is an updated patch where 
the ADD CONSTRAINT ... NOT NULL behavior for domains matches the 
idempotent behavior of tables.  This uses the patch that Jian He posted.
From a0075e4bcd5f2db292f92fc2b70576ccebd07210 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut 
Date: Thu, 23 Nov 2023 07:35:32 +0100
Subject: [PATCH v4 1/2] Add tests for domain-related information schema views

Discussion: 
https://www.postgresql.org/message-id/flat/9ec24d7b-633d-463a-84c6-7acff769c9e8%40eisentraut.org
---
 src/test/regress/expected/domain.out | 47 
 src/test/regress/sql/domain.sql  | 24 ++
 2 files changed, 71 insertions(+)

diff --git a/src/test/regress/expected/domain.out 
b/src/test/regress/expected/domain.out
index 6d94e84414a..e70aebd70c0 100644
--- a/src/test/regress/expected/domain.out
+++ b/src/test/regress/expected/domain.out
@@ -1207,3 +1207,50 @@ create domain testdomain1 as int constraint unsigned 
check (value > 0);
 alter domain testdomain1 rename constraint unsigned to unsigned_foo;
 alter domain testdomain1 drop constraint unsigned_foo;
 drop domain testdomain1;
+--
+-- Information schema
+--
+SELECT * FROM information_schema.column_domain_usage
+  WHERE domain_name IN ('con', 'dom', 'pos_int', 'things')
+  ORDER BY domain_name;
+ domain_catalog | domain_schema | domain_name | table_catalog | table_schema | 
table_name | column_name 
++---+-+---+--++-
+ regression | public| con | regression| public   | 
domcontest | col1
+ regression | public| dom | regression| public   | 
domview| col1
+ regression | public| things  | regression| public   | 
thethings  | stuff
+(3 rows)
+
+SELECT * FROM information_schema.domain_constraints
+  WHERE domain_name IN ('con', 'dom', 'pos_int', 'things')
+  ORDER BY constraint_name;
+ constraint_catalog | constraint_schema | constraint_name | domain_catalog | 
domain_schema | domain_name | is_deferrable | initially_deferred 
++---+-++---+-+---+
+ regression | public| con_check   | regression | 
public| con | NO| NO
+ regression | public| meow| regression | 
public| things  | NO| NO
+ regression | public| pos_int_check   | regression | 
public| pos_int | NO| NO
+(3 rows)
+
+SELECT * FROM information_schema.domains
+  WHERE domain_name IN ('con', 'dom', 'pos_int', 'things')
+  ORDER BY domain_name;
+ domain_catalog | domain_schema | domain_name | data_type | 
character_maximum_length | character_octet_length | character_set_catalog | 
character_set_schema | character_set_name | collation_catalog | 
collation_schema | collation_name | numeric_precision | numeric_precision_radix 
| numeric_scale | datetime_precision | interval_type | interval_precision | 
domain_default | udt_catalog | udt_schema | udt_name | scope_catalog | 
scope_schema | scope_name | maximum_cardinality | dtd_identifier 
++---+-+---+--++---+--++---+--++---+-+---++---+++-++--+---+--++-+
+ regression | public| con | integer   |
  ||   |  | 
   |   |  ||
32 |   2 | 0 || 
  ||| regression  | pg_catalog 
| int4 |   |  || | 1
+ regression | public| dom | integer   |
  ||   |  | 
   |   |  ||
32 |   2 | 0 || 
  ||| regression  | pg_catalog 
| int4 |   |  || | 1
+ regression | public| pos_int | integer   |
  ||   |  | 
   |   |  |   

Re: Catalog domain not-null constraints

2024-03-14 Thread Peter Eisentraut

On 14.03.24 15:03, Alvaro Herrera wrote:

On 2024-Mar-14, Peter Eisentraut wrote:


Perhaps it would make sense if we change the ALTER TABLE command to be like

 ALTER TABLE t1 ADD IF NOT EXISTS NOT NULL c1

Then the behavior is like one would expect.

For ALTER TABLE, we would reject this command if IF NOT EXISTS is not
specified.  (Since this is mainly for pg_dump, it doesn't really matter for
usability.)  For ALTER DOMAIN, we could accept both variants.


I don't understand why you want to change this behavior, though.


Because in the abstract, the behavior of

ALTER TABLE t1 ADD 

should be to add a constraint.

In the current implementation, the behavior is different for different 
constraint types.






Re: Catalog domain not-null constraints

2024-03-14 Thread Alvaro Herrera
On 2024-Mar-14, Peter Eisentraut wrote:

> Perhaps it would make sense if we change the ALTER TABLE command to be like
> 
> ALTER TABLE t1 ADD IF NOT EXISTS NOT NULL c1
> 
> Then the behavior is like one would expect.
> 
> For ALTER TABLE, we would reject this command if IF NOT EXISTS is not
> specified.  (Since this is mainly for pg_dump, it doesn't really matter for
> usability.)  For ALTER DOMAIN, we could accept both variants.

I don't understand why you want to change this behavior, though.

-- 
Álvaro Herrera PostgreSQL Developer  —  https://www.EnterpriseDB.com/
"La victoria es para quien se atreve a estar solo"




Re: Catalog domain not-null constraints

2024-03-14 Thread Peter Eisentraut

On 12.02.24 11:24, Alvaro Herrera wrote:

On 2024-Feb-11, Peter Eisentraut wrote:

But I see that table constraints do not work that way.  A command like ALTER
TABLE t1 ADD NOT NULL c1 does nothing if the column already has a NOT NULL
constraint.  I'm not sure this is correct.  At least it's not documented.
We should probably make the domains feature work the same way, but I would
like to understand why it works that way first.



The main source of nastiness, when we allow multiple constraints, is
constraint inheritance.  If we allow just one constraint per column,
then it's always easy to know what to do on inheritance attach and
detach: just coninhcount+1 or coninhcount-1 of the one relevant
constraint (which can be matched by column name).  If we have multiple
ones, we have to know which one(s) to match and how (by constraint
name?); if the parent has two and the child has one, we need to create
another in the child, with its own coninhcount adjustments; if the
parent has one named parent_col_not_null and the child also has
child_col_not_null, then at ADD INHERIT do we match these ignoring the
differing name, or do we rename the one on child so that we now have
two?  Also, the clutter in psql/pg_dump becomes worse.

I would suggest that domain not-null constraints should also allow just
one per column.


Perhaps it would make sense if we change the ALTER TABLE command to be like

ALTER TABLE t1 ADD IF NOT EXISTS NOT NULL c1

Then the behavior is like one would expect.

For ALTER TABLE, we would reject this command if IF NOT EXISTS is not 
specified.  (Since this is mainly for pg_dump, it doesn't really matter 
for usability.)  For ALTER DOMAIN, we could accept both variants.






Re: Catalog domain not-null constraints

2024-02-21 Thread jian he
wandering around the function AlterDomainNotNull,
the following code can fix the previous undesired behavior.
seems pretty simple, am I missing something?
based on v3-0001-Add-tests-for-domain-related-information-schema-v.patch
and v3-0002-Catalog-domain-not-null-constraints.patch

diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 2f94e375..9069465a 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -2904,7 +2904,7 @@ AlterDomainAddConstraint(List *names, Node *newConstraint,
Form_pg_type typTup;
Constraint *constr;
char   *ccbin;
-   ObjectAddress address;
+   ObjectAddress address = InvalidObjectAddress;

/* Make a TypeName so we can use standard type lookup machinery */
typename = makeTypeNameFromNameList(names);
@@ -3003,6 +3003,12 @@ AlterDomainAddConstraint(List *names, Node
*newConstraint,
}
else if (constr->contype == CONSTR_NOTNULL)
{
+   /* Is the domain already set NOT NULL */
+   if (typTup->typnotnull)
+   {
+   table_close(typrel, RowExclusiveLock);
+   return address;
+   }
domainAddNotNullConstraint(domainoid, typTup->typnamespace,
typTup->typbasetype, typTup->typtypmod,
constr, NameStr(typTup->typname), constrAddr);




Re: Catalog domain not-null constraints

2024-02-12 Thread Alvaro Herrera
On 2024-Feb-11, Peter Eisentraut wrote:

> But I see that table constraints do not work that way.  A command like ALTER
> TABLE t1 ADD NOT NULL c1 does nothing if the column already has a NOT NULL
> constraint.  I'm not sure this is correct.  At least it's not documented.
> We should probably make the domains feature work the same way, but I would
> like to understand why it works that way first.

It's an intentional design decision actually; I had it creating multiple
constraints at first, but it had some ugly problems, so I made it behave
this way (which was no small amount of changes).  I think the first time
I posted an implementation that worked this way was here
https://postgr.es/m/20230315224440.cz3brr6323fcrxs6@alvherre.pgsql

and then we debated it again later, starting at the bottom of
https://www.postgresql.org/message-id/flat/CAEZATCUA_iPo5kqUun4myghoZtgqbY3jx62%3DGwcYKRMmxFUq_g%40mail.gmail.com#482db1d21bcf8a4c3ef4fbee609810f4
A few messages later, I quoted the SQL standard for DROP NOT NULL, which
is pretty clear that if you run that command, then the column becomes
possibly nullable, which means that we'd have to drop all matching
constraints, or something.

The main source of nastiness, when we allow multiple constraints, is
constraint inheritance.  If we allow just one constraint per column,
then it's always easy to know what to do on inheritance attach and
detach: just coninhcount+1 or coninhcount-1 of the one relevant
constraint (which can be matched by column name).  If we have multiple
ones, we have to know which one(s) to match and how (by constraint
name?); if the parent has two and the child has one, we need to create
another in the child, with its own coninhcount adjustments; if the
parent has one named parent_col_not_null and the child also has
child_col_not_null, then at ADD INHERIT do we match these ignoring the
differing name, or do we rename the one on child so that we now have
two?  Also, the clutter in psql/pg_dump becomes worse.

I would suggest that domain not-null constraints should also allow just
one per column.

-- 
Álvaro HerreraBreisgau, Deutschland  —  https://www.EnterpriseDB.com/
"People get annoyed when you try to debug them."  (Larry Wall)




Re: Catalog domain not-null constraints

2024-02-11 Thread Tom Lane
Peter Eisentraut  writes:
> But I see that table constraints do not work that way.  A command like 
> ALTER TABLE t1 ADD NOT NULL c1 does nothing if the column already has a 
> NOT NULL constraint.  I'm not sure this is correct.  At least it's not 
> documented.  We should probably make the domains feature work the same 
> way, but I would like to understand why it works that way first.

That's probably a hangover from when the underlying state was just
a boolean (attnotnull).  Still, I'm a little hesitant to change the
behavior.  I do agree that named constraints need to "stack", so
that you'd have to remove each one before not-nullness stops being
enforced.  Less sure about unnamed properties.

regards, tom lane




Re: Catalog domain not-null constraints

2024-02-11 Thread Peter Eisentraut

On 08.02.24 13:17, jian he wrote:

I think I found a bug.
connotnull already set to not null.
every execution of  `alter domain connotnull add not null value ;`
would concatenate 'NOT NULL VALUE' for the "Check" column,


I would have expected that.  Each invocation adds a new constraint.

But I see that table constraints do not work that way.  A command like 
ALTER TABLE t1 ADD NOT NULL c1 does nothing if the column already has a 
NOT NULL constraint.  I'm not sure this is correct.  At least it's not 
documented.  We should probably make the domains feature work the same 
way, but I would like to understand why it works that way first.







Re: Catalog domain not-null constraints

2024-02-08 Thread jian he
On Wed, Feb 7, 2024 at 4:11 PM Peter Eisentraut  wrote:
>
> >
> > Interesting.  I couldn't reproduce this locally, even across different
> > operating systems.  The cfbot failures appear to be sporadic, but also
> > happening across multiple systems, so it's clearly not just a local
> > environment failure.  Can anyone else perhaps reproduce this locally?
>
> This patch set needed a rebase, so here it is.
>
do you think
add following
ALTER DOMAIN name ADD NOT
NULL VALUE

to doc/src/sgml/ref/alter_domain.sgml synopsis makes sense?
otherwise it would be hard to find out this command, i think.


I think I found a bug.
connotnull already set to not null.
every execution of  `alter domain connotnull add not null value ;`
would concatenate 'NOT NULL VALUE' for the "Check" column,
That means changes in the function pg_get_constraintdef_worker are not
100% correct.
see below demo:


src8=# \dD+
  List of domains
 Schema |Name|  Type   | Collation | Nullable | Default |
Check  | Access privileges | Description
++-+---+--+-++---+-
 public | connotnull | integer |   |  | | NOT
NULL VALUE |   |
 public | nnint  | integer |   | not null | | NOT
NULL VALUE |   |
(2 rows)

src8=# alter domain connotnull add not null value ;
ALTER DOMAIN
src8=# \dD+
 List of domains
 Schema |Name|  Type   | Collation | Nullable | Default |
   Check | Access privileges | Descript
ion
++-+---+--+-+---+---+-

 public | connotnull | integer |   | not null | | NOT
NULL VALUE NOT NULL VALUE |   |
 public | nnint  | integer |   | not null | | NOT
NULL VALUE|   |
(2 rows)

src8=# alter domain connotnull add not null value ;
ALTER DOMAIN
src8=# \dD+
 List of domains
 Schema |Name|  Type   | Collation | Nullable | Default |
  Check | Access privil
eges | Description
++-+---+--+-+--+--
-+-
 public | connotnull | integer |   | not null | | NOT
NULL VALUE NOT NULL VALUE NOT NULL VALUE |
 |
 public | nnint  | integer |   | not null | | NOT
NULL VALUE   |
 |
(2 rows)




Re: Catalog domain not-null constraints

2024-02-07 Thread Peter Eisentraut

On 18.01.24 07:53, Peter Eisentraut wrote:

On 17.01.24 13:15, vignesh C wrote:

One of the test has failed in CFBot at [1] with:
diff -U3 /tmp/cirrus-ci-build/src/test/regress/expected/domain.out
/tmp/cirrus-ci-build/src/test/regress/results/domain.out
--- /tmp/cirrus-ci-build/src/test/regress/expected/domain.out
2024-01-14 15:40:01.793434601 +
+++ /tmp/cirrus-ci-build/src/test/regress/results/domain.out
2024-01-14 15:42:23.013332625 +
@@ -1271,11 +1271,4 @@
  FROM information_schema.domain_constraints
  WHERE domain_name IN ('con', 'dom', 'pos_int', 'things'))
    ORDER BY constraint_name;
- constraint_catalog | constraint_schema | constraint_name  |   
check_clause

-+---+--+---
- regression | public    | con_check    | (VALUE > 0)
- regression | public    | meow | (VALUE < 
11)

- regression | public    | pos_int_check    | (VALUE > 0)
- regression | public    | pos_int_not_null | VALUE IS 
NOT NULL

-(4 rows)
-
+ERROR:  could not open relation with OID 36379

[1] - https://cirrus-ci.com/task/4536440638406656
[2] - 
https://api.cirrus-ci.com/v1/artifact/task/4536440638406656/log/src/test/regress/regression.diffs


Interesting.  I couldn't reproduce this locally, even across different 
operating systems.  The cfbot failures appear to be sporadic, but also 
happening across multiple systems, so it's clearly not just a local 
environment failure.  Can anyone else perhaps reproduce this locally?


This patch set needed a rebase, so here it is.

About the sporadic test failure above, I think that is an existing issue 
that is just now exposed through some test timing changes.  The 
pg_get_expr() function used in information_schema.check_constraints has 
no locking against concurrent drops of tables.  I think in this 
particular case, the tests "domain" and "alter_table" are prone to this 
conflict.  If I move "domain" to a separate test group, the issue goes 
away.  I'll start a separate discussion about this issue.
From cd48c8eb8aa5958020e899b8406cbd7866805db5 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut 
Date: Thu, 23 Nov 2023 07:35:32 +0100
Subject: [PATCH v3 1/2] Add tests for domain-related information schema views

Discussion: 
https://www.postgresql.org/message-id/flat/9ec24d7b-633d-463a-84c6-7acff769c9e8%40eisentraut.org
---
 src/test/regress/expected/domain.out | 47 
 src/test/regress/sql/domain.sql  | 24 ++
 2 files changed, 71 insertions(+)

diff --git a/src/test/regress/expected/domain.out 
b/src/test/regress/expected/domain.out
index 6d94e84414a..e70aebd70c0 100644
--- a/src/test/regress/expected/domain.out
+++ b/src/test/regress/expected/domain.out
@@ -1207,3 +1207,50 @@ create domain testdomain1 as int constraint unsigned 
check (value > 0);
 alter domain testdomain1 rename constraint unsigned to unsigned_foo;
 alter domain testdomain1 drop constraint unsigned_foo;
 drop domain testdomain1;
+--
+-- Information schema
+--
+SELECT * FROM information_schema.column_domain_usage
+  WHERE domain_name IN ('con', 'dom', 'pos_int', 'things')
+  ORDER BY domain_name;
+ domain_catalog | domain_schema | domain_name | table_catalog | table_schema | 
table_name | column_name 
++---+-+---+--++-
+ regression | public| con | regression| public   | 
domcontest | col1
+ regression | public| dom | regression| public   | 
domview| col1
+ regression | public| things  | regression| public   | 
thethings  | stuff
+(3 rows)
+
+SELECT * FROM information_schema.domain_constraints
+  WHERE domain_name IN ('con', 'dom', 'pos_int', 'things')
+  ORDER BY constraint_name;
+ constraint_catalog | constraint_schema | constraint_name | domain_catalog | 
domain_schema | domain_name | is_deferrable | initially_deferred 
++---+-++---+-+---+
+ regression | public| con_check   | regression | 
public| con | NO| NO
+ regression | public| meow| regression | 
public| things  | NO| NO
+ regression | public| pos_int_check   | regression | 
public| pos_int | NO| NO
+(3 rows)
+
+SELECT * FROM information_schema.domains
+  WHERE domain_name IN ('con', 'dom', 'pos_int', 'things')
+  ORDER BY domain_name;
+ domain_catalog | domain_schema | domain_name | data_type | 
character_maximum_length | character_octet_length | character_set_catalog | 
character_set_schema | character_set_name | collation_catalog | 
collation_schema | collation_name | numeric_precision | 

Re: Catalog domain not-null constraints

2024-01-17 Thread Peter Eisentraut

On 17.01.24 13:15, vignesh C wrote:

One of the test has failed in CFBot at [1] with:
diff -U3 /tmp/cirrus-ci-build/src/test/regress/expected/domain.out
/tmp/cirrus-ci-build/src/test/regress/results/domain.out
--- /tmp/cirrus-ci-build/src/test/regress/expected/domain.out
2024-01-14 15:40:01.793434601 +
+++ /tmp/cirrus-ci-build/src/test/regress/results/domain.out
2024-01-14 15:42:23.013332625 +
@@ -1271,11 +1271,4 @@
  FROM information_schema.domain_constraints
  WHERE domain_name IN ('con', 'dom', 'pos_int', 'things'))
ORDER BY constraint_name;
- constraint_catalog | constraint_schema | constraint_name  |   check_clause
-+---+--+---
- regression | public| con_check| (VALUE > 0)
- regression | public| meow | (VALUE < 11)
- regression | public| pos_int_check| (VALUE > 0)
- regression | public| pos_int_not_null | VALUE IS NOT NULL
-(4 rows)
-
+ERROR:  could not open relation with OID 36379

[1] - https://cirrus-ci.com/task/4536440638406656
[2] - 
https://api.cirrus-ci.com/v1/artifact/task/4536440638406656/log/src/test/regress/regression.diffs


Interesting.  I couldn't reproduce this locally, even across different 
operating systems.  The cfbot failures appear to be sporadic, but also 
happening across multiple systems, so it's clearly not just a local 
environment failure.  Can anyone else perhaps reproduce this locally?






Re: Catalog domain not-null constraints

2024-01-17 Thread vignesh C
On Wed, 29 Nov 2023 at 01:14, Peter Eisentraut  wrote:
>
> On 23.11.23 14:13, Aleksander Alekseev wrote:
> > =# create domain connotnull1 integer;
> > =# create domain connotnull2 integer;
> > =# alter domain connotnull1 add not null value;
> > =# alter domain connotnull2 set not null;
> > =# \dD
> > ERROR:  unexpected null value in cached tuple for catalog
> > pg_constraint column conkey
>
> Yeah, for domain not-null constraints pg_constraint.conkey is indeed
> null.  Should we put something in there?
>
> Attached is an updated patch that avoids the error by taking a separate
> code path for domain constraints in ruleutils.c.  But maybe there is
> another way to arrange this.

One of the test has failed in CFBot at [1] with:
diff -U3 /tmp/cirrus-ci-build/src/test/regress/expected/domain.out
/tmp/cirrus-ci-build/src/test/regress/results/domain.out
--- /tmp/cirrus-ci-build/src/test/regress/expected/domain.out
2024-01-14 15:40:01.793434601 +
+++ /tmp/cirrus-ci-build/src/test/regress/results/domain.out
2024-01-14 15:42:23.013332625 +
@@ -1271,11 +1271,4 @@
 FROM information_schema.domain_constraints
 WHERE domain_name IN ('con', 'dom', 'pos_int', 'things'))
   ORDER BY constraint_name;
- constraint_catalog | constraint_schema | constraint_name  |   check_clause
-+---+--+---
- regression | public| con_check| (VALUE > 0)
- regression | public| meow | (VALUE < 11)
- regression | public| pos_int_check| (VALUE > 0)
- regression | public| pos_int_not_null | VALUE IS NOT NULL
-(4 rows)
-
+ERROR:  could not open relation with OID 36379

[1] - https://cirrus-ci.com/task/4536440638406656
[2] - 
https://api.cirrus-ci.com/v1/artifact/task/4536440638406656/log/src/test/regress/regression.diffs

Regards,
Vignesh




Re: Catalog domain not-null constraints

2023-11-28 Thread Peter Eisentraut

On 23.11.23 14:13, Aleksander Alekseev wrote:

=# create domain connotnull1 integer;
=# create domain connotnull2 integer;
=# alter domain connotnull1 add not null value;
=# alter domain connotnull2 set not null;
=# \dD
ERROR:  unexpected null value in cached tuple for catalog
pg_constraint column conkey


Yeah, for domain not-null constraints pg_constraint.conkey is indeed 
null.  Should we put something in there?


Attached is an updated patch that avoids the error by taking a separate 
code path for domain constraints in ruleutils.c.  But maybe there is 
another way to arrange this.From c297c458766a0e1ee65408d3ced469f32cf5e7d8 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut 
Date: Tue, 28 Nov 2023 20:38:16 +0100
Subject: [PATCH v2 2/2] Catalog domain not-null constraints

This applies the explicit catalog representation of not-null
constraints introduced by b0e96f3119 for table constraints also to
domain not-null constraints.

TODO: catversion

Discussion: 
https://www.postgresql.org/message-id/flat/9ec24d7b-633d-463a-84c6-7acff769c9e8%40eisentraut.org
---
 src/backend/catalog/information_schema.sql |   2 +-
 src/backend/catalog/pg_constraint.c|  40 +++
 src/backend/commands/typecmds.c| 313 +++--
 src/backend/utils/adt/ruleutils.c  |   8 +
 src/backend/utils/cache/typcache.c |   2 +-
 src/bin/pg_dump/pg_dump.c  |   2 +-
 src/include/catalog/pg_constraint.h|   1 +
 src/test/regress/expected/domain.out   |  49 +++-
 src/test/regress/sql/domain.sql|  26 ++
 9 files changed, 339 insertions(+), 104 deletions(-)

diff --git a/src/backend/catalog/information_schema.sql 
b/src/backend/catalog/information_schema.sql
index 10b34c3c5b..95a1b34560 100644
--- a/src/backend/catalog/information_schema.sql
+++ b/src/backend/catalog/information_schema.sql
@@ -448,7 +448,7 @@ CREATE VIEW check_constraints AS
 SELECT current_database()::information_schema.sql_identifier AS 
constraint_catalog,
rs.nspname::information_schema.sql_identifier AS constraint_schema,
con.conname::information_schema.sql_identifier AS constraint_name,
-   pg_catalog.format('%s IS NOT NULL', 
at.attname)::information_schema.character_data AS check_clause
+   pg_catalog.format('%s IS NOT NULL', coalesce(at.attname, 
'VALUE'))::information_schema.character_data AS check_clause
  FROM pg_constraint con
 LEFT JOIN pg_namespace rs ON rs.oid = con.connamespace
 LEFT JOIN pg_class c ON c.oid = con.conrelid
diff --git a/src/backend/catalog/pg_constraint.c 
b/src/backend/catalog/pg_constraint.c
index e9d4d6006e..4f1a5e1c84 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -629,6 +629,46 @@ findNotNullConstraint(Oid relid, const char *colname)
return findNotNullConstraintAttnum(relid, attnum);
 }
 
+HeapTuple
+findDomainNotNullConstraint(Oid typid)
+{
+   Relationpg_constraint;
+   HeapTuple   conTup,
+   retval = NULL;
+   SysScanDesc scan;
+   ScanKeyData key;
+
+   pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
+   ScanKeyInit(,
+   Anum_pg_constraint_contypid,
+   BTEqualStrategyNumber, F_OIDEQ,
+   ObjectIdGetDatum(typid));
+   scan = systable_beginscan(pg_constraint, 
ConstraintRelidTypidNameIndexId,
+ true, NULL, 1, );
+
+   while (HeapTupleIsValid(conTup = systable_getnext(scan)))
+   {
+   Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(conTup);
+
+   /*
+* We're looking for a NOTNULL constraint that's marked 
validated.
+*/
+   if (con->contype != CONSTRAINT_NOTNULL)
+   continue;
+   if (!con->convalidated)
+   continue;
+
+   /* Found it */
+   retval = heap_copytuple(conTup);
+   break;
+   }
+
+   systable_endscan(scan);
+   table_close(pg_constraint, AccessShareLock);
+
+   return retval;
+}
+
 /*
  * Given a pg_constraint tuple for a not-null constraint, return the column
  * number it is for.
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index aaf9728697..57389be9ed 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -126,15 +126,19 @@ static OidfindTypeSubscriptingFunction(List 
*procname, Oid typeOid);
 static Oid findRangeSubOpclass(List *opcname, Oid subtype);
 static Oid findRangeCanonicalFunction(List *procname, Oid typeOid);
 static Oid findRangeSubtypeDiffFunction(List *procname, Oid subtype);
-static void validateDomainConstraint(Oid domainoid, char *ccbin);
+static void validateDomainCheckConstraint(Oid domainoid, char 

Re: Catalog domain not-null constraints

2023-11-26 Thread Peter Eisentraut

On 23.11.23 17:38, Alvaro Herrera wrote:

If you create a table with column of domain that has a NOT NULL
constraint, what happens?  I mean, is the table column marked
attnotnull, and how does it behave?


No, the domain does not affect the catalog entry for the column.  This 
is the same way it behaves now.



Is there a separate pg_constraint
row for the constraint in the table?  What happens if you do
ALTER TABLE ... DROP NOT NULL for that column?


Those are separate.  After dropping the NOT NULL for a column, null 
values for the column could still be rejected by a domain.  (This is the 
same way CHECK constraints work.)






Re: Catalog domain not-null constraints

2023-11-23 Thread Alvaro Herrera
On 2023-Nov-23, Peter Eisentraut wrote:

> This patch set applies the explicit catalog representation of not-null
> constraints introduced by b0e96f3119 for table constraints also to domain
> not-null constraints.

I like the idea of having domain not-null constraints appear in
pg_constraint.

> Since there is no inheritance or primary keys etc., this is much simpler and
> just applies the existing infrastructure to domains as well.

If you create a table with column of domain that has a NOT NULL
constraint, what happens?  I mean, is the table column marked
attnotnull, and how does it behave?  Is there a separate pg_constraint
row for the constraint in the table?  What happens if you do
ALTER TABLE ... DROP NOT NULL for that column?

-- 
Álvaro Herrera   48°01'N 7°57'E  —  https://www.EnterpriseDB.com/
"Por suerte hoy explotó el califont porque si no me habría muerto
 de aburrido"  (Papelucho)




Re: Catalog domain not-null constraints

2023-11-23 Thread Alvaro Herrera
On 2023-Nov-23, Aleksander Alekseev wrote:

> Interestingly enough according to the documentation this syntax is
> already supported [1][2], but the actual query will fail on `master`:
> 
> ```
> =# create domain connotnull integer;
> CREATE DOMAIN
> =# alter domain connotnull add not null value;
> ERROR:  unrecognized constraint subtype: 1
> ```

Hah, nice ... this only fails in this way on master, though, as a
side-effect of previous NOT NULL work during this cycle.  So if we take
Peter's patch, we don't need to worry about it.  In 16 it behaves
properly, with a normal syntax error.

> ```
> =# create domain connotnull1 integer;
> =# create domain connotnull2 integer;
> =# alter domain connotnull1 add not null value;
> =# alter domain connotnull2 set not null;
> =# \dD
> ERROR:  unexpected null value in cached tuple for catalog
> pg_constraint column conkey
> ```

This is also a master-only problem, as "add not null" is rejected in 16
with a syntax error (and obviously \dD doesn't fail).

> NOT VALID is not supported:
> 
> ```
> =# alter domain connotnull add not null value not valid;
> ERROR:  NOT NULL constraints cannot be marked NOT VALID
> ```

Yeah, it'll take more work to let NOT NULL constraints be marked NOT
VALID, both on domains and on tables.  It'll be a good feature for sure.

-- 
Álvaro Herrera   48°01'N 7°57'E  —  https://www.EnterpriseDB.com/
"La victoria es para quien se atreve a estar solo"




Re: Catalog domain not-null constraints

2023-11-23 Thread Aleksander Alekseev
Hi,

> This patch set applies the explicit catalog representation of not-null
> constraints introduced by b0e96f3119 for table constraints also to
> domain not-null constraints.

Interestingly enough according to the documentation this syntax is
already supported [1][2], but the actual query will fail on `master`:

```
=# create domain connotnull integer;
CREATE DOMAIN
=# alter domain connotnull add not null value;
ERROR:  unrecognized constraint subtype: 1
```

I wonder if we should reflect this limitation in the documentation
and/or show better error messages. This could be quite surprising to
the user. However if we change the documentation on the `master`
branch this patch will have to change it back.

I was curious about the semantic difference between `SET NOT NULL` and
`ADD NOT NULL value`. When I wanted to figure this out I discovered
something that seems to be a bug:

```
=# create domain connotnull1 integer;
=# create domain connotnull2 integer;
=# alter domain connotnull1 add not null value;
=# alter domain connotnull2 set not null;
=# \dD
ERROR:  unexpected null value in cached tuple for catalog
pg_constraint column conkey
```

Also it turned out that I can do both: `SET NOT NULL` and `ADD NOT
NULL value` for the same domain. Is it an intended behavior? We should
either forbid it or cover this case with a test.

NOT VALID is not supported:

```
=# alter domain connotnull add not null value not valid;
ERROR:  NOT NULL constraints cannot be marked NOT VALID
```

... and this is correct: "NOT VALID is only accepted for CHECK
constraints" [1]. This code path however doesn't seem to be
test-covered even on `master`. While on it, I suggest fixing this.

[1]: https://www.postgresql.org/docs/current/sql-alterdomain.html
[2]: https://www.postgresql.org/docs/current/sql-createdomain.html

-- 
Best regards,
Aleksander Alekseev