Re: cataloguing NOT NULL constraints

2024-05-15 Thread Bruce Momjian
On Wed, May 15, 2024 at 09:50:36AM +0200, Álvaro Herrera wrote:
> On 2024-May-14, Bruce Momjian wrote:
> 
> > Turns out these commits generated a single release note item, which I
> > have now removed with the attached committed patch.
> 
> Hmm, but the commits about not-null constraints for domains were not
> reverted, only the ones for constraints on relations.  I think the
> release notes don't properly address the ones on domains.  I think it's
> at least these two commits:
> 
> > -Author: Peter Eisentraut 
> > -2024-03-20 [e5da0fe3c] Catalog domain not-null constraints
> > -Author: Peter Eisentraut 
> > -2024-04-15 [9895b35cb] Fix ALTER DOMAIN NOT NULL syntax
> 
> It may still be a good idea to make a note about those, at least to
> point out that information_schema now lists them.  For example, pg11
> release notes had this item

Let me explain what I did to adjust the release notes.  I took your
commit hashes, which were longer than mine, and got the commit subject
text from them.  I then searched the release notes to see which commit
subjects existed in the document.  Only the first three did, and the
release note item has five commits.

The then tested if the last two patches could be reverted, and 'patch'
thought they could be, so that confirmed they were not reverted.

However, there was no text in the release note item that corresponded to
the commits, so I just removed the entire item.

What I now think happened is that the last two commits were considered
part of the larger NOT NULL change, and not worth mentioning separately,
but now that the NOT NULL part is reverted, we might need to mention
them.

I rarely handle such complex cases so I don't think I was totally
correct in my handling.  Let's get a reply to Peter Eisentraut's
question and we can figure out what to do.

-- 
  Bruce Momjian  https://momjian.us
  EDB  https://enterprisedb.com

  Only you can decide what is important to you.




Re: cataloguing NOT NULL constraints

2024-05-15 Thread Peter Eisentraut

On 15.05.24 09:50, Alvaro Herrera wrote:

On 2024-May-14, Bruce Momjian wrote:


Turns out these commits generated a single release note item, which I
have now removed with the attached committed patch.


Hmm, but the commits about not-null constraints for domains were not
reverted, only the ones for constraints on relations.  I think the
release notes don't properly address the ones on domains.  I think it's
at least these two commits:


-Author: Peter Eisentraut 
-2024-03-20 [e5da0fe3c] Catalog domain not-null constraints
-Author: Peter Eisentraut 
-2024-04-15 [9895b35cb] Fix ALTER DOMAIN NOT NULL syntax


I'm confused that these were kept.  The first one was specifically to 
make the catalog representation of domain not-null constraints 
consistent with table not-null constraints.  But the table part was 
reverted, so now the domain constraints are inconsistent again.


The second one refers to the first one, but it might also fix some 
additional older issue, so it would need more investigation.






Re: cataloguing NOT NULL constraints

2024-05-15 Thread Alvaro Herrera
On 2024-May-14, Bruce Momjian wrote:

> Turns out these commits generated a single release note item, which I
> have now removed with the attached committed patch.

Hmm, but the commits about not-null constraints for domains were not
reverted, only the ones for constraints on relations.  I think the
release notes don't properly address the ones on domains.  I think it's
at least these two commits:

> -Author: Peter Eisentraut 
> -2024-03-20 [e5da0fe3c] Catalog domain not-null constraints
> -Author: Peter Eisentraut 
> -2024-04-15 [9895b35cb] Fix ALTER DOMAIN NOT NULL syntax

It may still be a good idea to make a note about those, at least to
point out that information_schema now lists them.  For example, pg11
release notes had this item



   
Add information_schema columns related to table
constraints and triggers (Peter Eisentraut)
   

   
Specifically,

triggers.action_order,

triggers.action_reference_old_table,
and

triggers.action_reference_new_table
are now populated, where before they were always null.  Also,

table_constraints.enforced
now exists but is not yet usefully populated.
   


-- 
Álvaro HerreraBreisgau, Deutschland  —  https://www.EnterpriseDB.com/




Re: cataloguing NOT NULL constraints

2024-05-14 Thread Bruce Momjian
On Mon, May 13, 2024 at 09:00:28AM -0400, Robert Haas wrote:
> > Specifically, the problem is that I mentioned that we could restrict the
> > NOT NULL NO INHERIT addition in pg_dump for primary keys to occur only
> > in pg_upgrade; but it turns this is not correct.  In normal
> > dump/restore, there's an additional table scan to check for nulls when
> > the constraints is not there, so the PK creation would become measurably
> > slower.  (In a table with a million single-int rows, PK creation goes
> > from 2000ms to 2300ms due to the second scan to check for nulls).
> 
> I have a feeling that any theory of the form "X only needs to happen
> during pg_upgrade" is likely to be wrong. pg_upgrade isn't really
> doing anything especially unusual: just creating some objects and
> loading data. Those things can also be done at other times, so
> whatever is needed during pg_upgrade is also likely to be needed at
> other times. Maybe that's not sound reasoning for some reason or
> other, but that's my intuition.

I assume Alvaro is saying that pg_upgrade has only a single session,
which is unique and might make things easier for him.

-- 
  Bruce Momjian  https://momjian.us
  EDB  https://enterprisedb.com

  Only you can decide what is important to you.




Re: cataloguing NOT NULL constraints

2024-05-14 Thread Bruce Momjian
On Sun, May 12, 2024 at 04:56:09PM +0200, Álvaro Herrera wrote:
> On 2024-May-11, Alvaro Herrera wrote:
> 
> > I have found two more problems that [] require some more work to fix,
> > so I've decided to cut my losses now and revert the whole.
> 
> Here's the revert patch, which I intend to push early tomorrow.
> 
> Commits reverted are:
> 21ac38f498b33f0231842238b83847ec63dfe07b
> d45597f72fe53a53f6271d5ba4e7acf8fc9308a1
> 13daa33fa5a6d340f9be280db14e7b07ed11f92e
> 0cd711271d42b0888d36f8eda50e1092c2fed4b3
> d72d32f52d26c9588256de90b9bc54fe312cee60
> d9f686a72ee91f6773e5d2bc52994db8d7157a8e
> c3709100be73ad5af7ff536476d4d713bca41b1a
> 3af7217942722369a6eb7629e0fb1cbbef889a9b
> b0f7dd915bca6243f3daf52a81b8d0682a38ee3b
> ac22a9545ca906e70a819b54e76de38817c93aaf
> d0ec2ddbe088f6da35444fad688a62eae4fbd840
> 9b581c53418666205938311ef86047aa3c6b741f
> b0e96f311985bceba79825214f8e43f65afa653a
> 
> with some significant conflict fixes (mostly in the last one).

Turns out these commits generated a single release note item, which I
have now removed with the attached committed patch.

-- 
  Bruce Momjian  https://momjian.us
  EDB  https://enterprisedb.com

  Only you can decide what is important to you.
diff --git a/doc/src/sgml/release-17.sgml b/doc/src/sgml/release-17.sgml
index 9c7c0a0337f..143ee17716d 100644
--- a/doc/src/sgml/release-17.sgml
+++ b/doc/src/sgml/release-17.sgml
@@ -1486,29 +1486,6 @@ Add event trigger support for REINDEX (Garrett Thornburg, Jian He)
 
 
 
-
-
-
-
-Allow NOT NULL columns to be specified as column constraints and domains (Alvaro Herrera, Bernd Helmle, Kyotaro Horiguchi, Peter Eisentraut)
-
-
-
-Previously NOT NULL could only be specified as a column attribute.
-
-
-
 

Re: cataloguing NOT NULL constraints

2024-05-14 Thread Alvaro Herrera
On 2024-May-13, Robert Haas wrote:

> It seems to me that the practical thing to do about this problem is
> just decide not to solve it. I mean, it's currently the case that if
> you establish a PRIMARY KEY when you create a table, the columns of
> that key are marked NOT NULL and remain NOT NULL even if the primary
> key is later dropped. So, if that didn't change, we would be no less
> compliant with the SQL standard (or your reading of it) than we are
> now.
[...]
> So I don't really think it's a great idea to change this behavior, but
> even if it is, is it such a good idea that we want to sink the whole
> patch set repeatedly over it, as has already happened twice now? I
> feel that if we did what Tom suggested a year ago in
> https://www.postgresql.org/message-id/3801207.1681057...@sss.pgh.pa.us
> -- "I'm inclined to think that this idea of suppressing the implied
> NOT NULL from PRIMARY KEY is a nonstarter and we should just go ahead
> and make such a constraint" [...]

Hmm, I hadn't interpreted Tom's message the way you suggest, and you may
be right that it might be a good way forward.  I'll keep this in mind
for next time.

-- 
Álvaro Herrera PostgreSQL Developer  —  https://www.EnterpriseDB.com/
"No es bueno caminar con un hombre muerto"




Re: cataloguing NOT NULL constraints

2024-05-13 Thread Robert Haas
On Mon, May 13, 2024 at 12:45 PM Alvaro Herrera  wrote:
> The point is that a column can be in a primary key and not have an
> explicit not-null constraint.  This is different from having a column be
> NOT NULL and having a primary key on top.  In both cases the attnotnull
> flag is set; the difference between these two scenarios is what happens
> if you drop the primary key.  If you do not have an explicit not-null
> constraint, then the attnotnull flag is lost as soon as you drop the
> primary key.  You don't have to do DROP NOT NULL for that to happen
>
> This means that if you have a column that's in the primary key but does
> not have an explicit not-null constraint, then we shouldn't make one up.
> (Which we would, if we were to keep an unadorned NOT NULL that we can't
> drop at the end of the dump.)

It seems to me that the practical thing to do about this problem is
just decide not to solve it. I mean, it's currently the case that if
you establish a PRIMARY KEY when you create a table, the columns of
that key are marked NOT NULL and remain NOT NULL even if the primary
key is later dropped. So, if that didn't change, we would be no less
compliant with the SQL standard (or your reading of it) than we are
now. And if you do really want to make that change, why not split it
out into its own patch, so that the patch that does $SUBJECT is
changing the minimal number of other things at the same time? That
way, reverting something might not involve reverting everything, plus
you could have a separate design discussion about what that fix ought
to look like, separate from the issues that are truly inherent to
cataloging NOT NULL constraints per se.

What I meant about changing the order of operations is that,
currently, the database knows that the column is NOT NULL before the
COPY happens, and I don't think we can change that. I think you agree
-- that's why you invented the throwaway constraints. As far as I can
see, the problems all have to do with getting the "throwaway" part to
happen correctly. It can't be a problem to just mark the relevant
columns NOT NULL in the relevant tables -- we already do that. But if
you want to discard some of those NOT NULL markings once the PRIMARY
KEY is added, you have to know which ones to discard. If we just
consider the most straightforward scenario where somebody does a full
dump-and-restore, getting that right may be annoying, but it seems
like it surely has to be possible. The dump will just have to
understand which child tables (or, more generally, descendent tables)
got a NOT NULL marking on a column because of the PK and which ones
had an explicit marking in the old database and do the right thing in
each case.

But what if somebody does a selective restore of one table from a
partitioning hierarchy? Currently, the columns that would have been
part of the primary key end up NOT NULL, but the primary key itself is
not restored because it can't be. What will happen in this new system?
If you don't apply any NOT NULL constraints to those columns, then a
user who restores one partition from an old dump and tries to reattach
it to the correct partitioned table has to recheck the NOT NULL
constraint, unlike now. If you apply a normal-looking garden-variety
NOT NULL constraint to that column, you've invented a constraint that
didn't exist in the source database. And if you apply a throwaway NOT
NULL constraint but the user never attaches that table anywhere, then
the throwaway constraint survives. None of those options sound very
good to me.

Another scenario: Say that you have a table with a PRIMARY KEY. For
some reason, you want to drop the primary key and then add it back.
Well, with this definitional change, as soon as you drop it, you
forget that the underlying columns don't contain any nulls, so when
you add it back, you have to check them again. I don't know who would
find that behavior an improvement over what we have today.

So I don't really think it's a great idea to change this behavior, but
even if it is, is it such a good idea that we want to sink the whole
patch set repeatedly over it, as has already happened twice now? I
feel that if we did what Tom suggested a year ago in
https://www.postgresql.org/message-id/3801207.1681057...@sss.pgh.pa.us
-- "I'm inclined to think that this idea of suppressing the implied
NOT NULL from PRIMARY KEY is a nonstarter and we should just go ahead
and make such a constraint" -- there's a very good chance that a
revert would have been avoided here and it would still be just as
valid to think of revisiting this particular question in a future
release as it is now.

-- 
Robert Haas
EDB: http://www.enterprisedb.com




Re: cataloguing NOT NULL constraints

2024-05-13 Thread Alvaro Herrera
On 2024-May-13, Robert Haas wrote:

> On Mon, May 13, 2024 at 9:44 AM Alvaro Herrera  
> wrote:
> > The problematic point is the need to add NOT NULL constraints during
> > table creation that don't exist in the table being dumped, for
> > performance of primary key creation -- I called this a throwaway
> > constraint.  We needed to be able to drop those constraints after the PK
> > was created.  These were marked NO INHERIT to allow them to be dropped,
> > which is easier if the children don't have them.  This all worked fine.
> 
> This seems really weird to me. Why is it necessary? I mean, in
> existing releases, if you declare a column as PRIMARY KEY, the columns
> included in the key are forced to be NOT NULL, and you can't change
> that for so long as they are included in the PRIMARY KEY.

The point is that a column can be in a primary key and not have an
explicit not-null constraint.  This is different from having a column be
NOT NULL and having a primary key on top.  In both cases the attnotnull
flag is set; the difference between these two scenarios is what happens
if you drop the primary key.  If you do not have an explicit not-null
constraint, then the attnotnull flag is lost as soon as you drop the
primary key.  You don't have to do DROP NOT NULL for that to happen.

This means that if you have a column that's in the primary key but does
not have an explicit not-null constraint, then we shouldn't make one up.
(Which we would, if we were to keep an unadorned NOT NULL that we can't
drop at the end of the dump.)

> So I would have thought that after this patch, you'd end up with the
> same thing.

At least as I interpret the standard, you wouldn't.

> One way of doing that would be to make the PRIMARY KEY depend on the
> now-catalogued NOT NULL constraints, and the other way would be to
> keep it as an ad-hoc prohibition, same as now.

That would be against what [I think] the standard says.

> But I don't see why I need to end up with what the patch generates,
> which seems to be something like CONSTRAINT pgdump_throwaway_notnull_0
> NOT NULL NO INHERIT. That kind of thing suggests that we're changing
> around the order of operations in pg_dump, probably by adding the NOT
> NULL constraints at a later stage than currently, and I think the
> proper solution is most likely to be to avoid doing that in the first
> place.

The point of the throwaway constraints is that they don't remain after
the dump has restored completely.  They are there only so that we don't
have to scan the data looking for possible nulls when we create the
primary key.  We have a DROP CONSTRAINT for the throwaway not-nulls as
soon as the PK is created.

We're not changing any order of operations as such.

> That's not to say that we shouldn't try to make improvements, just
> that it may be hard to get right.

Sure, that's why this patch has now been reverted twice :-) and has been
in the works for ... how many years now?

-- 
Álvaro Herrera   48°01'N 7°57'E  —  https://www.EnterpriseDB.com/




Re: cataloguing NOT NULL constraints

2024-05-13 Thread Robert Haas
On Mon, May 13, 2024 at 9:44 AM Alvaro Herrera  wrote:
> The problematic point is the need to add NOT NULL constraints during
> table creation that don't exist in the table being dumped, for
> performance of primary key creation -- I called this a throwaway
> constraint.  We needed to be able to drop those constraints after the PK
> was created.  These were marked NO INHERIT to allow them to be dropped,
> which is easier if the children don't have them.  This all worked fine.

This seems really weird to me. Why is it necessary? I mean, in
existing releases, if you declare a column as PRIMARY KEY, the columns
included in the key are forced to be NOT NULL, and you can't change
that for so long as they are included in the PRIMARY KEY. So I would
have thought that after this patch, you'd end up with the same thing.
One way of doing that would be to make the PRIMARY KEY depend on the
now-catalogued NOT NULL constraints, and the other way would be to
keep it as an ad-hoc prohibition, same as now. In PostgreSQL 16, I get
a dump like this:

CREATE TABLE public.foo (
a integer NOT NULL,
b text
);

COPY public.foo (a, b) FROM stdin;
\.

ALTER TABLE ONLY public.foo
ADD CONSTRAINT foo_pkey PRIMARY KEY (a);

If I'm dumping from an existing release, I don't see why any of that
needs to change. The NOT NULL decoration should lead to a
system-generated constraint name. If I'm dumping from a new release,
the NOT NULL decoration needs to be replaced with CONSTRAINT
existing_constraint_name NOT NULL. But I don't see why I need to end
up with what the patch generates, which seems to be something like
CONSTRAINT pgdump_throwaway_notnull_0 NOT NULL NO INHERIT. That kind
of thing suggests that we're changing around the order of operations
in pg_dump, probably by adding the NOT NULL constraints at a later
stage than currently, and I think the proper solution is most likely
to be to avoid doing that in the first place.

> However, at some point we realized that we needed to add NOT NULL
> constraints in child tables for the columns in which the parent had a
> primary key.  Then things become messy because we had the throwaway
> constraints on one hand and the not-nulls that descend from the PK on
> the other hand, where one was NO INHERIT and the other wasn't; worse if
> the child also has a primary key.

This seems like another problem that is created by changing the order
of operations in pg_dump.

> > The other possibility that occurs to me is that I think the motivation
> > for cataloging NOT NULL constraints was that we wanted to be able to
> > track dependencies on them, or something like that, which seems like
> > it might be able to create issues of the type that you're facing, but
> > the details aren't clear to me.
>
> NOT VALID constraints would be extremely useful, for one thing (because
> then you don't need to exclusively-lock the table during a long scan in
> order to add a constraint), and it's just one step away from having
> these constraints be catalogued.  It was also fixing some inconsistent
> handling of inheritance cases.

I agree that NOT VALID constraints would be very useful. I'm a little
scared by the idea of fixing inconsistent handling of inheritance
cases, just for fear that there may be more things relying on the
inconsistent behavior than we realize. I feel like this is an area
where it's easy for changes to be scarier than they at first seem. I
still have memories of discovering some of the current behavior back
in the mid-2000s when I was learning PostgreSQL (and databases
generally). It struck me as fiddly back then, and it still does. I
feel like there are probably some behaviors that look like arbitrary
decisions but are actually very important for some undocumented
reason. That's not to say that we shouldn't try to make improvements,
just that it may be hard to get right.

-- 
Robert Haas
EDB: http://www.enterprisedb.com




Re: cataloguing NOT NULL constraints

2024-05-13 Thread Alvaro Herrera
On 2024-May-13, Robert Haas wrote:

> On Sat, May 11, 2024 at 5:40 AM Alvaro Herrera  
> wrote:

> > Specifically, the problem is that I mentioned that we could restrict the
> > NOT NULL NO INHERIT addition in pg_dump for primary keys to occur only
> > in pg_upgrade; but it turns this is not correct.  In normal
> > dump/restore, there's an additional table scan to check for nulls when
> > the constraints is not there, so the PK creation would become measurably
> > slower.  (In a table with a million single-int rows, PK creation goes
> > from 2000ms to 2300ms due to the second scan to check for nulls).
> 
> I have a feeling that any theory of the form "X only needs to happen
> during pg_upgrade" is likely to be wrong. pg_upgrade isn't really
> doing anything especially unusual: just creating some objects and
> loading data. Those things can also be done at other times, so
> whatever is needed during pg_upgrade is also likely to be needed at
> other times. Maybe that's not sound reasoning for some reason or
> other, but that's my intuition.

True.  It may be that by setting up the upgrade SQL script differently,
we don't need to make the distinction at all.  I hope to be able to do
that.

> I'm sorry that I haven't been following this thread closely, but I'm
> confused about how we ended up here. What exactly are the user-visible
> behavior changes wrought by this patch, and how do they give rise to
> these issues?

The problematic point is the need to add NOT NULL constraints during
table creation that don't exist in the table being dumped, for
performance of primary key creation -- I called this a throwaway
constraint.  We needed to be able to drop those constraints after the PK
was created.  These were marked NO INHERIT to allow them to be dropped,
which is easier if the children don't have them.  This all worked fine.

However, at some point we realized that we needed to add NOT NULL
constraints in child tables for the columns in which the parent had a
primary key.  Then things become messy because we had the throwaway
constraints on one hand and the not-nulls that descend from the PK on
the other hand, where one was NO INHERIT and the other wasn't; worse if
the child also has a primary key.

It turned out that we didn't have any mechanism to transform a NO
INHERIT constraint into a regular one that would be inherited.  I added
one, didn't like the way it worked, tried to restrict it but that caused
other problems; this is the mess that led to the revert (pg_dump in
normal mode would emit scripts that fail for some legitimate cases).

One possible way forward might be to make pg_dump smarter by adding one
more query to know the relationship between constraints that must be
dropped and those that don't.  Another might be to allow multiple
not-null constraints on the same column (one inherits, the other
doesn't, and you can drop them independently).  There may be others.

> The other possibility that occurs to me is that I think the motivation
> for cataloging NOT NULL constraints was that we wanted to be able to
> track dependencies on them, or something like that, which seems like
> it might be able to create issues of the type that you're facing, but
> the details aren't clear to me.

NOT VALID constraints would be extremely useful, for one thing (because
then you don't need to exclusively-lock the table during a long scan in
order to add a constraint), and it's just one step away from having
these constraints be catalogued.  It was also fixing some inconsistent
handling of inheritance cases.

-- 
Álvaro Herrera   48°01'N 7°57'E  —  https://www.EnterpriseDB.com/




Re: cataloguing NOT NULL constraints

2024-05-13 Thread Robert Haas
On Sat, May 11, 2024 at 5:40 AM Alvaro Herrera  wrote:
> I have found two more problems that I think are going to require some
> more work to fix, so I've decided to cut my losses now and revert the
> whole.  I'll come back again in 18 with these problems fixed.

Bummer, but makes sense.

> Specifically, the problem is that I mentioned that we could restrict the
> NOT NULL NO INHERIT addition in pg_dump for primary keys to occur only
> in pg_upgrade; but it turns this is not correct.  In normal
> dump/restore, there's an additional table scan to check for nulls when
> the constraints is not there, so the PK creation would become measurably
> slower.  (In a table with a million single-int rows, PK creation goes
> from 2000ms to 2300ms due to the second scan to check for nulls).

I have a feeling that any theory of the form "X only needs to happen
during pg_upgrade" is likely to be wrong. pg_upgrade isn't really
doing anything especially unusual: just creating some objects and
loading data. Those things can also be done at other times, so
whatever is needed during pg_upgrade is also likely to be needed at
other times. Maybe that's not sound reasoning for some reason or
other, but that's my intuition.

> The addition of NOT NULL NO INHERIT constraints for this purpose
> collides with addition of constraints for other reasons, and it forces
> us to do unpleasant things such as altering an existing constraint to go
> from NO INHERIT to INHERIT.  If this happens only during pg_upgrade,
> that would be okay IMV; but if we're forced to allow in normal operation
> (and in some cases we are), it could cause inconsistencies, so I don't
> want to do that.  I see a way to fix this (adding another query in
> pg_dump that detects which columns descend from ones used in PKs in
> ancestor tables), but that's definitely too much additional mechanism to
> be adding this late in the cycle.

I'm sorry that I haven't been following this thread closely, but I'm
confused about how we ended up here. What exactly are the user-visible
behavior changes wrought by this patch, and how do they give rise to
these issues? One change I know about is that a constraint that is
explicitly catalogued (vs. just existing implicitly) has a name. But
it isn't obvious to me that such a difference, by itself, is enough to
cause all of these problems: if a NOT NULL constraint is created
without a name, then I suppose we just have to generate one. Maybe the
fact that the constraints have names somehow causes ugliness later,
but I can't quite understand why it would.

The other possibility that occurs to me is that I think the motivation
for cataloging NOT NULL constraints was that we wanted to be able to
track dependencies on them, or something like that, which seems like
it might be able to create issues of the type that you're facing, but
the details aren't clear to me. Changing any behavior in this area
seems like it could be quite tricky, because of things like the
interaction between PRIMARY KEY and NOT NULL, which is rather
idiosyncratic but upon which a lot of existing SQL (including SQL not
controlled by us) likely depends. If there's not a clear plan for how
we keep all the stuff that works today working, I fear we'll end up in
an endless game of whack-a-mole. If you've already written the design
ideas down someplace, I'd appreciate a pointer in the right direction.

Or maybe there's some other issue entirely. In any case, sorry about
the revert, and sorry that I haven't paid more attention to this.

-- 
Robert Haas
EDB: http://www.enterprisedb.com




Re: cataloguing NOT NULL constraints

2024-05-11 Thread Alvaro Herrera
On 2024-May-09, Robert Haas wrote:

> Yeah, I have to admit that the ongoing bug fixing here has started to
> make me a bit nervous, but I also can't totally follow everything
> that's under discussion, so I don't want to rush to judgement.

I have found two more problems that I think are going to require some
more work to fix, so I've decided to cut my losses now and revert the
whole.  I'll come back again in 18 with these problems fixed.

Specifically, the problem is that I mentioned that we could restrict the
NOT NULL NO INHERIT addition in pg_dump for primary keys to occur only
in pg_upgrade; but it turns this is not correct.  In normal
dump/restore, there's an additional table scan to check for nulls when
the constraints is not there, so the PK creation would become measurably
slower.  (In a table with a million single-int rows, PK creation goes
from 2000ms to 2300ms due to the second scan to check for nulls).

The addition of NOT NULL NO INHERIT constraints for this purpose
collides with addition of constraints for other reasons, and it forces
us to do unpleasant things such as altering an existing constraint to go
from NO INHERIT to INHERIT.  If this happens only during pg_upgrade,
that would be okay IMV; but if we're forced to allow in normal operation
(and in some cases we are), it could cause inconsistencies, so I don't
want to do that.  I see a way to fix this (adding another query in
pg_dump that detects which columns descend from ones used in PKs in
ancestor tables), but that's definitely too much additional mechanism to
be adding this late in the cycle.

-- 
Álvaro Herrera   48°01'N 7°57'E  —  https://www.EnterpriseDB.com/




Re: cataloguing NOT NULL constraints

2024-05-09 Thread Robert Haas
On Wed, May 8, 2024 at 4:42 PM Alvaro Herrera  wrote:
> I spent a long time trying to think how to fix this, and I had despaired
> wanting to write that I would need to revert the whole NOT NULL business
> for pg17 -- but that was until I realized that we don't actually need
> this NOT NULL NO INHERIT business except during pg_upgrade, and that
> simplifies things enough to give me confidence that the whole feature
> can be kept.

Yeah, I have to admit that the ongoing bug fixing here has started to
make me a bit nervous, but I also can't totally follow everything
that's under discussion, so I don't want to rush to judgement. I feel
like we might need some documentation or a README or something that
explains the takeaway from the recent commits dealing with no-inherit
constraints. None of those commits updated the documentation, which
may be fine, but neither the resulting behavior nor the reasoning
behind it is obvious. It's not enough for it to be correct -- it has
to be understandable enough to the hive mind that we can maintain it
going forward.

-- 
Robert Haas
EDB: http://www.enterprisedb.com




Re: cataloguing NOT NULL constraints

2024-05-08 Thread Alvaro Herrera
On 2024-May-07, Kyotaro Horiguchi wrote:

> Hello,
> 
> At Wed, 1 May 2024 19:49:35 +0200, Alvaro Herrera  
> wrote in 
> > Here are two patches that I intend to push soon (hopefully tomorrow).
> 
> This commit added and edited two error messages, resulting in using
> slightly different wordings "in" and "on" for relation constraints.
> 
> +   errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on 
> relation \"%s\"",
> ===
> +   errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" in 
> relation \"%s\"",

Thank you, I hadn't noticed the inconsistency -- I fix this in the
attached series.

While trying to convince myself that I could mark the remaining open
item for this work closed, I discovered that pg_dump fails to produce
working output for some combinations.  Notably, if I create Andrew
Bille's example in 16:

create table test_0 (id serial primary key);
create table test_1 (id integer primary key) inherits (test_0);

then current master's pg_dump produces output that the current server
fails to restore, failing the PK creation in test_0:

ALTER TABLE ONLY public.test_0
ADD CONSTRAINT test_0_pkey PRIMARY KEY (id);
ERROR:  cannot change NO INHERIT status of NOT NULL constraint 
"pgdump_throwaway_notnull_0" in relation "test_1"

because we have already created the NOT NULL NO INHERIT constraint in
test_1 when we created it, and because of d45597f72fe5, we refuse to
change it into a regular inheritable constraint, which the PK in its
parent table needs.

I spent a long time trying to think how to fix this, and I had despaired
wanting to write that I would need to revert the whole NOT NULL business
for pg17 -- but that was until I realized that we don't actually need
this NOT NULL NO INHERIT business except during pg_upgrade, and that
simplifies things enough to give me confidence that the whole feature
can be kept.

Because, remember: the idea of those NO INHERIT "throwaway" constraints
is that we can skip reading the data when we create the PRIMARY KEY
during binary upgrade.  We don't actually need the NO INHERIT
constraints for anything during regular pg_dump.  So what we can do, is
restrict the usage of NOT NULL NO INHERIT so that they occur only during
pg_upgrade.  I think this will make Justin P. happier, because we no
longer have these unsightly NOT NULL NO INHERIT nonstandard syntax in
dumps.

The attached patch series does that.  Actually, it does a little more,
but it's not really much:

0001: fix the typos pointed out by Kyotaro.

0002: A mechanical code movement that takes some ugly ballast out of
getTableAttrs into its own routine.  I realized that this new code was
far too ugly and messy to be in the middle of filling the tbinfo struct
of attributes.  If you use "git show --color-moved
--color-moved-ws=ignore-all-space" with this commit you can see that
nothing happens apart from the code move.

0003: pgindent, fixes the comments just moved to account for different
indentation depth.

0004: moves again the moved PQfnumber() calls back to getTableAttrs(),
for efficiency (we don't want to search the result for those resnums for
every single attribute of all tables being dumped).

0005: This is the actual code change I describe above.  We restrict
use_throwaway_nulls so that it's only set during binary upgrade mode.
This changes pg_dump output; in the normal case, we no longer have NOT
NULL NO INHERIT.  I added one test stanza to verify that pg_upgrade
retains these clauses, where they are critical.

0006: Tighten up what d45597f72fe5 did, in that outside of binary
upgrade mode, we no longer accept changes to NOT NULL NO INHERIT
constraints so that they become INHERIT.  Previously we accepted that
during recursion, but this isn't really very principled.  (I had
accepted this because pg_dump required it for some other cases).  This
changes some test output, and I also simplify some test cases that were
testing stuff that's no longer interesting.

(To push, I'll squash 0002+0003+0004 as a single one, and perhaps 0005
with them; I produced them like this only to make them easy to see
what's changing.)


I also have a pending patch for 16 that adds tables like the problematic
ones so that they remain for future pg_upgrade testing.  With the
changes in this series, the whole thing finally works AFAICT.

I did notice one more small bit of weirdness, which is that at the end
of the process you may end up with constraints that retain the throwaway
name.  This doesn't seem at all critical, considering that you can't
drop them anyway and such names do not survive a further dump (because
they are marked as inherited constraint without a "local" definition, so
they're not dumped separately).  I would still like to fix it, but it
seems to require unduly contortions so I may end up not doing anything
about it.

-- 
Álvaro Herrera PostgreSQL Developer  —  https://www.EnterpriseDB.com/
>From 5cdec1b6ce61f75d886109d7daafd57b8064a4a6 Mon Sep 17 00:00:00 2001
From: 

Re: cataloguing NOT NULL constraints

2024-05-07 Thread Kyotaro Horiguchi
Hello,

At Wed, 1 May 2024 19:49:35 +0200, Alvaro Herrera  
wrote in 
> Here are two patches that I intend to push soon (hopefully tomorrow).

This commit added and edited two error messages, resulting in using
slightly different wordings "in" and "on" for relation constraints.

+   errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on 
relation \"%s\"",
===
+   errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" in 
relation \"%s\"",

I think we usually use on in this case.

regards.

-- 
Kyotaro Horiguchi
NTT Open Source Software Center




Re: cataloguing NOT NULL constraints

2024-05-02 Thread Alexander Lakhin

02.05.2024 19:21, Alvaro Herrera wrote:


Now, you could claim that the standard doesn't mention
INCLUDING/EXCLUDING CONSTRAINTS, therefore since we have come up with
its definition then we should make it affect not-null constraints.
However, there's also this note:

   NOTE 520 — s, except for NOT NULL, are not included in
   CDi; s are effectively transformed to s and are thereby also excluded.

which is explicitly saying that not-null constraints are treated
differently; in essence, with INCLUDING CONSTRAINTS we choose to affect
the constraints that the standard says to ignore.


Thank you for very detailed and convincing explanation!

Now I see what the last sentence here (from [1]) means:
INCLUDING CONSTRAINTS

    CHECK constraints will be copied. No distinction is made between
    column constraints and table constraints. _Not-null constraints are
    always copied to the new table._

(I hadn't paid enough attention to it, because this exact paragraph is
also presented in previous versions...)

[1] https://www.postgresql.org/docs/devel/sql-createtable.html

Best regards,
Alexander




Re: cataloguing NOT NULL constraints

2024-05-02 Thread Alvaro Herrera
Hello Alexander

On 2024-May-02, Alexander Lakhin wrote:

> Could you also clarify, please, how CREATE TABLE ... LIKE is expected to
> work with NOT NULL constraints?

It should behave identically to 16.  If in 16 you end up with a
not-nullable column, then in 17 you should get a not-null constraint.

> I wonder whether EXCLUDING CONSTRAINTS (ALL) should cover not-null
> constraints too. What I'm seeing now, is that:
> CREATE TABLE t1 (i int, CONSTRAINT nn NOT NULL i);
> CREATE TABLE t2 (LIKE t1 EXCLUDING ALL);
> \d+ t2
> -- ends with:
> Not-null constraints:
>     "nn" NOT NULL "i"

In 16, this results in
   Table "public.t2"
 Column │  Type   │ Collation │ Nullable │ Default │ Storage │ Compression │ 
Stats target │ Description 
┼─┼───┼──┼─┼─┼─┼──┼─
 i  │ integer │   │ not null │ │ plain   │ │
  │ 
Access method: heap

so the fact that we have a not-null constraint in pg17 is correct.


> Or a similar case with PRIMARY KEY:
> CREATE TABLE t1 (i int PRIMARY KEY);
> CREATE TABLE t2 (LIKE t1 EXCLUDING CONSTRAINTS EXCLUDING INDEXES);
> \d+ t2
> -- leaves:
> Not-null constraints:
>     "t2_i_not_null" NOT NULL "i"

Here you also end up with a not-nullable column in 16, so I made it do
that.

Now you could argue that EXCLUDING CONSTRAINTS is explicit in saying
that we don't want the constraints; but in that case why did 16 mark the
columns as not-null?  The answer seems to be that the standard requires
this.  Look at 11.3  syntax rule 9) b) iii) 4):

  4) If the nullability characteristic included in LCDi is known not
  nullable, then let LNCi be NOT NULL; otherwise, let LNCi be the
  zero-length character string.

where LCDi is "1) Let LCDi be the column descriptor of the i-th column
of LT." and then

  5) Let CDi be the 
 LCNi LDTi LNCi


Now, you could claim that the standard doesn't mention
INCLUDING/EXCLUDING CONSTRAINTS, therefore since we have come up with
its definition then we should make it affect not-null constraints.
However, there's also this note:

  NOTE 520 — s, except for NOT NULL, are not included in
  CDi; s are effectively transformed to s and are thereby also excluded.

which is explicitly saying that not-null constraints are treated
differently; in essence, with INCLUDING CONSTRAINTS we choose to affect
the constraints that the standard says to ignore.


Thanks for looking!

-- 
Álvaro Herrera   48°01'N 7°57'E  —  https://www.EnterpriseDB.com/
"Learn about compilers. Then everything looks like either a compiler or
a database, and now you have two problems but one of them is fun."
https://twitter.com/thingskatedid/status/1456027786158776329




Re: cataloguing NOT NULL constraints

2024-05-02 Thread Alexander Lakhin

Hello Alvaro,

01.05.2024 20:49, Alvaro Herrera wrote:

Here are two patches that I intend to push soon (hopefully tomorrow).



Thank you for fixing those issues!

Could you also clarify, please, how CREATE TABLE ... LIKE is expected to
work with NOT NULL constraints?

I wonder whether EXCLUDING CONSTRAINTS (ALL) should cover not-null
constraints too. What I'm seeing now, is that:
CREATE TABLE t1 (i int, CONSTRAINT nn NOT NULL i);
CREATE TABLE t2 (LIKE t1 EXCLUDING ALL);
\d+ t2
-- ends with:
Not-null constraints:
    "nn" NOT NULL "i"

Or a similar case with PRIMARY KEY:
CREATE TABLE t1 (i int PRIMARY KEY);
CREATE TABLE t2 (LIKE t1 EXCLUDING CONSTRAINTS EXCLUDING INDEXES);
\d+ t2
-- leaves:
Not-null constraints:
    "t2_i_not_null" NOT NULL "i"

Best regards,
Alexander




Re: cataloguing NOT NULL constraints

2024-05-01 Thread Alvaro Herrera
On 2024-Apr-25, Alvaro Herrera wrote:

> > Also, I've found a weird behaviour with a non-inherited NOT NULL
> > constraint for a partitioned table:
> > CREATE TABLE pt(a int NOT NULL NO INHERIT) PARTITION BY LIST (a);

> Ugh.  Maybe a way to handle this is to disallow NO INHERIT in
> constraints on partitioned tables altogether.  I mean, they are a
> completely useless gimmick, aren't they?

Here are two patches that I intend to push soon (hopefully tomorrow).

-- 
Álvaro Herrera   48°01'N 7°57'E  —  https://www.EnterpriseDB.com/
"No me acuerdo, pero no es cierto.  No es cierto, y si fuera cierto,
 no me acuerdo." (Augusto Pinochet a una corte de justicia)
>From 97318ac81cfe82cf52629f141b26a5a497b0913c Mon Sep 17 00:00:00 2001
From: Alvaro Herrera 
Date: Wed, 1 May 2024 17:32:50 +0200
Subject: [PATCH 1/2] Disallow direct change of NO INHERIT of not-null
 constraints

We support changing NO INHERIT constraint to INHERIT for constraints in
child relations when adding a constraint to some ancestor relation, and
also during pg_upgrade's schema restore; but other than those special
cases, command ALTER TABLE ADD CONSTRAINT should not be allowed to
change an existing constraint from NO INHERIT to INHERIT, as that would
require to process child relations so that they also acquire an
appropriate constraint, which we may not be in a position to do.  (It'd
also be surprising behavior.)

It is conceivable that we want to allow ALTER TABLE SET NOT NULL to make
such a change; but in that case some more code is needed to implement it
correctly, so for now I've made that throw the same error message.

Also, during the prep phase of ALTER TABLE ADD CONSTRAINT, acquire locks
on all descendant tables; otherwise we might operate on child tables on
which no locks are held, particularly in the mode where a primary key
causes not-null constraints to be created on children.

Reported-by: Alexander Lakhin 
Discussion: https://postgr.es/m/7d923a66-55f0-3395-cd40-81c142b54...@gmail.com
---
 doc/src/sgml/ref/alter_table.sgml |  2 +-
 src/backend/catalog/heap.c| 20 +++-
 src/backend/catalog/pg_constraint.c   | 15 +++
 src/backend/commands/tablecmds.c  | 17 +++--
 src/include/catalog/pg_constraint.h   |  2 +-
 src/test/regress/expected/inherit.out | 24 +++-
 src/test/regress/sql/inherit.sql  | 15 +++
 7 files changed, 81 insertions(+), 14 deletions(-)

diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml
index ebd8c62038..0bf11f6cb6 100644
--- a/doc/src/sgml/ref/alter_table.sgml
+++ b/doc/src/sgml/ref/alter_table.sgml
@@ -105,7 +105,7 @@ WITH ( MODULUS numeric_literal, REM
 and column_constraint is:
 
 [ CONSTRAINT constraint_name ]
-{ NOT NULL |
+{ NOT NULL [ NO INHERIT ] |
   NULL |
   CHECK ( expression ) [ NO INHERIT ] |
   DEFAULT default_expr |
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index f0278b9c01..136cc42a92 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -2549,13 +2549,23 @@ AddRelationNewConstraints(Relation rel,
 
 			/*
 			 * If the column already has an inheritable not-null constraint,
-			 * we need only adjust its inheritance status and we're done.  If
-			 * the constraint is there but marked NO INHERIT, then it is
-			 * updated in the same way, but we also recurse to the children
-			 * (if any) to add the constraint there as well.
+			 * we need only adjust its coninhcount and we're done.  In certain
+			 * cases (see below), if the constraint is there but marked NO
+			 * INHERIT, then we mark it as no longer such and coninhcount
+			 * updated, plus we must also recurse to the children (if any) to
+			 * add the constraint there.
+			 *
+			 * We only allow the inheritability status to change during binary
+			 * upgrade (where it's used to add the not-null constraints for
+			 * children of tables with primary keys), or when we're recursing
+			 * processing a table down an inheritance hierarchy; directly
+			 * allowing a constraint to change from NO INHERIT to INHERIT
+			 * during ALTER TABLE ADD CONSTRAINT would be far too surprising
+			 * behavior.
 			 */
 			existing = AdjustNotNullInheritance1(RelationGetRelid(rel), colnum,
- cdef->inhcount, cdef->is_no_inherit);
+ cdef->inhcount, cdef->is_no_inherit,
+ IsBinaryUpgrade || allow_merge);
 			if (existing == 1)
 continue;		/* all done! */
 			else if (existing == -1)
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index aaf3537d3f..6b8496e085 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -721,7 +721,7 @@ extractNotNullColumn(HeapTuple constrTup)
  */
 int
 AdjustNotNullInheritance1(Oid relid, AttrNumber attnum, int count,
-		  bool is_no_inherit)
+		  bool is_no_inherit, bool allow_noinherit_change)
 {
 	HeapTuple	

Re: cataloguing NOT NULL constraints

2024-04-25 Thread Alvaro Herrera
On 2024-Apr-25, Alexander Lakhin wrote:

> While studying the NO INHERIT option, I've noticed that the documentation
> probably misses it's specification for NOT NULL:
> https://www.postgresql.org/docs/devel/sql-createtable.html
> 
> where column_constraint is:
> ...
> [ CONSTRAINT constraint_name ]
> { NOT NULL |
>   NULL |
>   CHECK ( expression ) [ NO INHERIT ] |

Hmm, okay, will fix.

> Also, I've found a weird behaviour with a non-inherited NOT NULL
> constraint for a partitioned table:
> CREATE TABLE pt(a int NOT NULL NO INHERIT) PARTITION BY LIST (a);
> CREATE TABLE dp(a int NOT NULL);
> ALTER TABLE pt ATTACH PARTITION dp DEFAULT;
> ALTER TABLE pt DETACH PARTITION dp;
> fails with:
> ERROR:  relation 16389 has non-inherited constraint "dp_a_not_null"

Ugh.  Maybe a way to handle this is to disallow NO INHERIT in
constraints on partitioned tables altogether.  I mean, they are a
completely useless gimmick, aren't they?

-- 
Álvaro Herrera PostgreSQL Developer  —  https://www.EnterpriseDB.com/




Re: cataloguing NOT NULL constraints

2024-04-24 Thread Alexander Lakhin

24.04.2024 20:36, Alvaro Herrera wrote:

So I added a restriction that we only accept such a change when
recursively adding a constraint, or during binary upgrade.  This should
limit the damage: you're no longer able to change an existing constraint
from NO INHERIT to YES INHERIT merely by doing another ALTER TABLE ADD
CONSTRAINT.

One thing that has me a little nervous about this whole business is
whether we're set up to error out where some child table down the
hierarchy has nulls, and we add a not-null constraint to it but fail to
do a verification scan.  I tried a couple of cases and AFAICS it works
correctly, but maybe there are other cases I haven't thought about where
it doesn't.



Thank you for the fix!

While studying the NO INHERIT option, I've noticed that the documentation
probably misses it's specification for NOT NULL:
https://www.postgresql.org/docs/devel/sql-createtable.html

where column_constraint is:
...
[ CONSTRAINT constraint_name ]
{ NOT NULL |
  NULL |
  CHECK ( expression ) [ NO INHERIT ] |

Also, I've found a weird behaviour with a non-inherited NOT NULL
constraint for a partitioned table:
CREATE TABLE pt(a int NOT NULL NO INHERIT) PARTITION BY LIST (a);
CREATE TABLE dp(a int NOT NULL);
ALTER TABLE pt ATTACH PARTITION dp DEFAULT;
ALTER TABLE pt DETACH PARTITION dp;
fails with:
ERROR:  relation 16389 has non-inherited constraint "dp_a_not_null"

Though with an analogous check constraint, I get:
CREATE TABLE pt(a int, CONSTRAINT nna CHECK (a IS NOT NULL) NO INHERIT) 
PARTITION BY LIST (a);
ERROR:  cannot add NO INHERIT constraint to partitioned table "pt"

Best regards,
Alexander




Re: cataloguing NOT NULL constraints

2024-04-24 Thread Alvaro Herrera
On 2024-Apr-22, Alvaro Herrera wrote:

> > On d9f686a72~1 this script results in:
> > ERROR:  cannot change NO INHERIT status of inherited NOT NULL constraint 
> > "t_a_not_null" on relation "t"
> 
> Right.  Now I'm beginning to wonder if allowing ADD CONSTRAINT to mutate
> a pre-existing NO INHERIT constraint into a inheritable constraint
> (while accepting a constraint name in the command that we don't heed) is
> really what we want.  Maybe we should throw some error when the affected
> constraint is the topmost one, and only accept the inheritance status
> change when we're recursing.

So I added a restriction that we only accept such a change when
recursively adding a constraint, or during binary upgrade.  This should
limit the damage: you're no longer able to change an existing constraint
from NO INHERIT to YES INHERIT merely by doing another ALTER TABLE ADD
CONSTRAINT.

One thing that has me a little nervous about this whole business is
whether we're set up to error out where some child table down the
hierarchy has nulls, and we add a not-null constraint to it but fail to
do a verification scan.  I tried a couple of cases and AFAICS it works
correctly, but maybe there are other cases I haven't thought about where
it doesn't.

-- 
Álvaro Herrera   48°01'N 7°57'E  —  https://www.EnterpriseDB.com/
"You're _really_ hosed if the person doing the hiring doesn't understand
relational systems: you end up with a whole raft of programmers, none of
whom has had a Date with the clue stick."  (Andrew Sullivan)
https://postgr.es/m/20050809113420.gd2...@phlogiston.dyndns.org
>From 238bc09bed57dcd0e4887615f3c57a580eb26d9e Mon Sep 17 00:00:00 2001
From: Alvaro Herrera 
Date: Mon, 22 Apr 2024 11:32:04 +0200
Subject: [PATCH v2 1/2] Acquire locks on children before recursing

ALTER TABLE ADD CONSTRAINT was missing this, as evidenced by assertion
failures.  While at it, create a small routine to encapsulate the weird
find_all_inheritors() call.

Reported-by: Alexander Lakhin 
Discussion: https://postgr.es/m/5b4cd32f-1d5b-c080-c688-31736bbcd...@gmail.com
---
 src/backend/commands/tablecmds.c | 40 +---
 1 file changed, 27 insertions(+), 13 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 3556240c8e..9058a0de7a 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -427,6 +427,7 @@ static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowe
 static void ATSimpleRecursion(List **wqueue, Relation rel,
 			  AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
 			  AlterTableUtilityContext *context);
+static void ATLockAllDescendants(Oid relid, LOCKMODE lockmode);
 static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode);
 static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
   LOCKMODE lockmode,
@@ -1621,9 +1622,7 @@ RemoveRelations(DropStmt *drop)
 		 * will lock those objects in the other order.
 		 */
 		if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
-			(void) find_all_inheritors(state.heapOid,
-	   state.heap_lockmode,
-	   NULL);
+			ATLockAllDescendants(state.heapOid, state.heap_lockmode);
 
 		/* OK, we're ready to delete this one */
 		obj.classId = RelationRelationId;
@@ -4979,10 +4978,12 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 			break;
 		case AT_AddConstraint:	/* ADD CONSTRAINT */
 			ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
-			/* Recursion occurs during execution phase */
-			/* No command-specific prep needed except saving recurse flag */
 			if (recurse)
+			{
+/* if recursing, set flag and lock all descendants */
 cmd->recurse = true;
+ATLockAllDescendants(RelationGetRelid(rel), lockmode);
+			}
 			pass = AT_PASS_ADD_CONSTR;
 			break;
 		case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
@@ -6721,6 +6722,21 @@ ATSimpleRecursion(List **wqueue, Relation rel,
 	}
 }
 
+/*
+ * ATLockAllDescendants
+ *
+ * Acquire lock on all descendant relations of the given relation.
+ */
+static void
+ATLockAllDescendants(Oid relid, LOCKMODE lockmode)
+{
+	/*
+	 * This is only used in DDL code, so it doesn't matter that we leak the
+	 * list storage; it'll be gone soon enough.
+	 */
+	(void) find_all_inheritors(relid, lockmode, NULL);
+}
+
 /*
  * Obtain list of partitions of the given table, locking them all at the given
  * lockmode and ensuring that they all pass CheckTableNotInUse.
@@ -9370,10 +9386,9 @@ ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
 
 	/*
 	 * Acquire locks all the way down the hierarchy.  The recursion to lower
-	 * levels occurs at execution time as necessary, so we don't need to do it
-	 * here, and we don't need the returned list either.
+	 * levels occurs at execution time as necessary.
 	 */
-	(void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
+	

Re: cataloguing NOT NULL constraints

2024-04-22 Thread Alvaro Herrera
Hi Alexander,

On 2024-Apr-18, Alexander Lakhin wrote:

> 18.04.2024 16:39, Alvaro Herrera wrote:
> > I have pushed a fix which should hopefully fix this problem
> > (d9f686a72e).  Please give this a look.  Thanks for reporting the issue.
> 
> Please look at an assertion failure, introduced with d9f686a72:
> CREATE TABLE t(a int, NOT NULL a NO INHERIT);
> CREATE TABLE t2() INHERITS (t);
> 
> ALTER TABLE t ADD CONSTRAINT nna NOT NULL a;
> TRAP: failed Assert("lockmode != NoLock || IsBootstrapProcessingMode() ||
> CheckRelationLockedByMe(r, AccessShareLock, true)"), File: "relation.c",
> Line: 67, PID: 2980258

Ah, of course -- we're missing acquiring locks during the prep phase for
the recursive case of ADD CONSTRAINT.  So we just need to add
find_all_inheritors() to do so in the AT_AddConstraint case in
ATPrepCmd().  However these naked find_all_inheritors() call look a bit
ugly to me, so I couldn't resist the temptation of adding a static
function ATLockAllDescendants to clean it up a bit.  I'll also add your
script to the tests and push shortly.

> On d9f686a72~1 this script results in:
> ERROR:  cannot change NO INHERIT status of inherited NOT NULL constraint 
> "t_a_not_null" on relation "t"

Right.  Now I'm beginning to wonder if allowing ADD CONSTRAINT to mutate
a pre-existing NO INHERIT constraint into a inheritable constraint
(while accepting a constraint name in the command that we don't heed) is
really what we want.  Maybe we should throw some error when the affected
constraint is the topmost one, and only accept the inheritance status
change when we're recursing.

Also I just noticed that in 9b581c534186 (which introduced this error
message) I used ERRCODE_DATATYPE_MISMATCH ... Is that really appropriate
here?

-- 
Álvaro Herrera   48°01'N 7°57'E  —  https://www.EnterpriseDB.com/
"Once again, thank you and all of the developers for your hard work on
PostgreSQL.  This is by far the most pleasant management experience of
any database I've worked on." (Dan Harris)
http://archives.postgresql.org/pgsql-performance/2006-04/msg00247.php
>From 35b485b72d0675d631cde9f95e65d9c0db9254b8 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera 
Date: Mon, 22 Apr 2024 11:32:04 +0200
Subject: [PATCH] Acquire locks on children before recursing

ALTER TABLE ADD CONSTRAINT was missing this, as evidenced by assertion
failures.

Reported-by: Alexander Lakhin 
Discussion: https://postgr.es/m/5b4cd32f-1d5b-c080-c688-31736bbcd...@gmail.com
---
 src/backend/commands/tablecmds.c | 33 ++--
 1 file changed, 23 insertions(+), 10 deletions(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 3556240c8e..8941181912 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -427,6 +427,7 @@ static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowe
 static void ATSimpleRecursion(List **wqueue, Relation rel,
 			  AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
 			  AlterTableUtilityContext *context);
+static void ATLockAllDescendants(Oid relid, LOCKMODE lockmode);
 static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode);
 static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
   LOCKMODE lockmode,
@@ -1621,9 +1622,7 @@ RemoveRelations(DropStmt *drop)
 		 * will lock those objects in the other order.
 		 */
 		if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
-			(void) find_all_inheritors(state.heapOid,
-	   state.heap_lockmode,
-	   NULL);
+			ATLockAllDescendants(state.heapOid, state.heap_lockmode);
 
 		/* OK, we're ready to delete this one */
 		obj.classId = RelationRelationId;
@@ -4979,10 +4978,15 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 			break;
 		case AT_AddConstraint:	/* ADD CONSTRAINT */
 			ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
-			/* Recursion occurs during execution phase */
-			/* No command-specific prep needed except saving recurse flag */
 			if (recurse)
+			{
+/*
+ * Make note for execution phase about need for recursion;
+ * also acquire lock on all descendants.
+ */
+ATLockAllDescendants(RelationGetRelid(rel), lockmode);
 cmd->recurse = true;
+			}
 			pass = AT_PASS_ADD_CONSTR;
 			break;
 		case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
@@ -6721,6 +6725,17 @@ ATSimpleRecursion(List **wqueue, Relation rel,
 	}
 }
 
+/*
+ * ATLockAllDescendants
+ *
+ * Acquire lock on all descendants of the given relation.
+ */
+static void
+ATLockAllDescendants(Oid relid, LOCKMODE lockmode)
+{
+	(void) find_all_inheritors(relid, lockmode, NULL);
+}
+
 /*
  * Obtain list of partitions of the given table, locking them all at the given
  * lockmode and ensuring that they all pass CheckTableNotInUse.
@@ -9370,10 +9385,9 @@ ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
 
 	/*
 	 * 

Re: cataloguing NOT NULL constraints

2024-04-18 Thread Alexander Lakhin

Hello Alvaro,

18.04.2024 16:39, Alvaro Herrera wrote:

I have pushed a fix which should hopefully fix this problem
(d9f686a72e).  Please give this a look.  Thanks for reporting the issue.


Please look at an assertion failure, introduced with d9f686a72:
CREATE TABLE t(a int, NOT NULL a NO INHERIT);
CREATE TABLE t2() INHERITS (t);

ALTER TABLE t ADD CONSTRAINT nna NOT NULL a;
TRAP: failed Assert("lockmode != NoLock || IsBootstrapProcessingMode() || CheckRelationLockedByMe(r, AccessShareLock, 
true)"), File: "relation.c", Line: 67, PID: 2980258


On d9f686a72~1 this script results in:
ERROR:  cannot change NO INHERIT status of inherited NOT NULL constraint "t_a_not_null" 
on relation "t"

Best regards,
Alexander




Re: cataloguing NOT NULL constraints

2024-04-18 Thread Alvaro Herrera
On 2024-Jan-25, Andrew Bille wrote:

> Starting from b0e96f31, pg_upgrade fails with inherited NOT NULL constraint:
> For example upgrade from 9c13b6814a (or REL_12_STABLE .. REL_16_STABLE) to
> b0e96f31 (or master) with following two tables (excerpt from
> src/test/regress/sql/rules.sql)
> 
> create table test_0 (id serial primary key);
> create table test_1 (id integer primary key) inherits (test_0);

I have pushed a fix which should hopefully fix this problem
(d9f686a72e).  Please give this a look.  Thanks for reporting the issue.

-- 
Álvaro HerreraBreisgau, Deutschland  —  https://www.EnterpriseDB.com/
"I apologize for the confusion in my previous responses.
 There appears to be an error."   (ChatGPT)




Re: cataloguing NOT NULL constraints

2024-04-15 Thread Alvaro Herrera
(I think I had already argued this point, but I don't see it in the
archives, so here it is again).

On 2024-Feb-07, jian he wrote:

> if you place CommandCounterIncrement inside the `if (recurse)` branch,
> then the regression test will be ok.

Yeah, but don't you think this is too magical?  I mean, randomly added
CCIs in the execution path for other reasons would break this.  Worse --
how can we _ensure_ that no CCIs occur at all?  I mean, it's possible
that an especially crafted multi-subcommand ALTER TABLE could contain
just the right CCI to break things in the opposite way.  The difference
in behavior would be difficult to justify.  (For good or ill, ALTER
TABLE ATTACH PARTITION cannot run in a multi-subcommand ALTER TABLE, so
this concern might be misplaced.  Still, more certainty seems better
than less.)

I've pushed both these patches now, adding what seemed a reasonable set
of test cases.  If there still are cases behaving in unexpected ways,
please let me know.

-- 
Álvaro HerreraBreisgau, Deutschland  —  https://www.EnterpriseDB.com/
"La espina, desde que nace, ya pincha" (Proverbio africano)




Re: cataloguing NOT NULL constraints

2024-02-07 Thread jian he
On Mon, Feb 5, 2024 at 5:51 PM Alvaro Herrera  wrote:
>
> On 2024-Feb-05, Alvaro Herrera wrote:
>
> > Hmm, let me have a look, I can probably get this one fixed today before
> > embarking on a larger fix elsewhere in the same feature.
>
> You know what -- this missing CCI has a much more visible impact, which
> is that the attnotnull marker that a primary key imposes on a partition
> is propagated early.  So this regression test no longer fails:
>
> create table cnn2_parted(a int primary key) partition by list (a);
> create table cnn2_part1(a int);
> alter table cnn2_parted attach partition cnn2_part1 for values in (1);
>
> Here, in the existing code the ALTER TABLE ATTACH fails with the error
> message that
>   ERROR:  primary key column "a" is not marked NOT NULL
> but with the patch, this no longer occurs.
>
> I'm not sure that this behavior change is desirable ... I have vague
> memories of people complaining that this sort of error was not very
> welcome ... but on the other hand it seems now pretty clear that if it
> *is* desirable, then its implementation is no good, because a single
> added CCI breaks it.
>
> I'm leaning towards accepting the behavior change, but I'd like to
> investigate a little bit more first, but what do others think?
>

if you place CommandCounterIncrement inside the `if (recurse)` branch,
then the regression test will be ok.

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 9f516967..25e225c2 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -7719,6 +7719,9 @@ set_attnotnull(List **wqueue, Relation rel,
AttrNumber attnum, bool recurse,

 false));
retval |= set_attnotnull(wqueue, childrel, childattno,

  recurse, lockmode);
+
+   CommandCounterIncrement();




Re: cataloguing NOT NULL constraints

2024-02-05 Thread Alvaro Herrera
On 2024-Feb-05, Alvaro Herrera wrote:

> While playing with it I noticed this other behavior change from 16,
> 
> create table pa (a int primary key) partition by list (a);
> create table pe (a int unique);
> alter table pa attach partition pe for values in (1, null);
> 
> In 16, we get the error:
> ERROR:  column "a" in child table must be marked NOT NULL
> which is correct (because the PK requires not-null).  In master we just
> let that through, but that seems to be a separate bug.

Hmm, so my initial reaction was to make the constraint-matching code
ignore the constraint in the partition-to-be if it's not the same type
(this is what patch 0002 here does) ... but what ends up happening is
that we create a separate, identical constraint+index for the primary
key.  I don't like that behavior too much myself, as it seems too
magical and surprising, since it could cause the ALTER TABLE ATTACH
operation of a large partition become costly and slower, since it needs
to create an index instead of merely scanning the whole data.

I'll look again at the idea of raising an error if the not-null
constraint is not already present.  That seems safer (and also, it's
what we've been doing all along).

-- 
Álvaro Herrera PostgreSQL Developer  —  https://www.EnterpriseDB.com/
>From 6a9feb7800675983198fbb3928c3f34360ac5a49 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera 
Date: Mon, 5 Feb 2024 10:18:43 +0100
Subject: [PATCH v2 1/2] Fix failure to merge NOT NULL constraints in
 inheritance

set_attnotnull() was not careful to CommandCounterIncrement() in cases
of multiple recursion.  Omission in b0e96f311985.

Reported-by: Alexander Lakhin 
Discussion: https://postgr.es/m/045dec3f-9b3d-aa44-0c99-85f699230...@gmail.com
---
 src/backend/commands/tablecmds.c  | 4 
 src/test/regress/expected/constraints.out | 1 -
 src/test/regress/expected/inherit.out | 8 
 src/test/regress/sql/inherit.sql  | 8 
 4 files changed, 20 insertions(+), 1 deletion(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 9f51696740..02724d5f04 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -7703,6 +7703,10 @@ set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum, bool recurse,
 		List	   *children;
 		ListCell   *lc;
 
+		/* Make above catalog changes visible */
+		if (retval)
+			CommandCounterIncrement();
+
 		children = find_inheritance_children(RelationGetRelid(rel), lockmode);
 		foreach(lc, children)
 		{
diff --git a/src/test/regress/expected/constraints.out b/src/test/regress/expected/constraints.out
index 5b068477bf..bef3d6577c 100644
--- a/src/test/regress/expected/constraints.out
+++ b/src/test/regress/expected/constraints.out
@@ -1010,7 +1010,6 @@ ERROR:  constraint "cnn_parent_pkey" of relation "cnn_parent" does not exist
 create table cnn2_parted(a int primary key) partition by list (a);
 create table cnn2_part1(a int);
 alter table cnn2_parted attach partition cnn2_part1 for values in (1);
-ERROR:  primary key column "a" is not marked NOT NULL
 drop table cnn2_parted, cnn2_part1;
 create table cnn2_parted(a int not null) partition by list (a);
 create table cnn2_part1(a int primary key);
diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out
index 130a924228..fe33bc4c2f 100644
--- a/src/test/regress/expected/inherit.out
+++ b/src/test/regress/expected/inherit.out
@@ -2496,6 +2496,14 @@ drop table inh_p1, inh_p2, inh_p3, inh_p4 cascade;
 NOTICE:  drop cascades to 2 other objects
 DETAIL:  drop cascades to table inh_multiparent
 drop cascades to table inh_multiparent2
+-- recursively add column with constraint while merging existing column
+create table inh_p1 ();
+create table inh_p2 (f1 int) inherits (inh_p1);
+create table inh_p3 () inherits (inh_p1, inh_p2);
+alter table inh_p1 add column f1 int not null;
+NOTICE:  merging definition of column "f1" for child "inh_p2"
+NOTICE:  merging definition of column "f1" for child "inh_p3"
+drop table inh_p1, inh_p2, inh_p3;
 --
 -- Mixed ownership inheritance tree
 --
diff --git a/src/test/regress/sql/inherit.sql b/src/test/regress/sql/inherit.sql
index 0ef9a61bc1..41cdde1d90 100644
--- a/src/test/regress/sql/inherit.sql
+++ b/src/test/regress/sql/inherit.sql
@@ -961,6 +961,14 @@ select conrelid::regclass, contype, conname,
 
 drop table inh_p1, inh_p2, inh_p3, inh_p4 cascade;
 
+-- recursively add column with constraint while merging existing column
+create table inh_p1 ();
+create table inh_p2 (f1 int) inherits (inh_p1);
+create table inh_p3 () inherits (inh_p1, inh_p2);
+alter table inh_p1 add column f1 int not null;
+
+drop table inh_p1, inh_p2, inh_p3;
+
 --
 -- Mixed ownership inheritance tree
 --
-- 
2.39.2

>From e871a4a991762ec5312239aa1a1e0ff918d6ce90 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera 
Date: Mon, 5 Feb 2024 19:05:34 +0100
Subject: [PATCH v2 2/2] ATTACH PARTITION: Don't let a UNIQUE constraint match
 a PRIMARY 

Re: cataloguing NOT NULL constraints

2024-02-05 Thread Alvaro Herrera
On 2024-Feb-05, Alvaro Herrera wrote:

> So this regression test no longer fails:
> 
> create table cnn2_parted(a int primary key) partition by list (a);
> create table cnn2_part1(a int);
> alter table cnn2_parted attach partition cnn2_part1 for values in (1);

> Here, in the existing code the ALTER TABLE ATTACH fails with the error
> message that
>   ERROR:  primary key column "a" is not marked NOT NULL
> but with the patch, this no longer occurs.

I think this change is OK.  In the partition, the primary key is created
in the partition anyway (as expected) which marks the column as
attnotnull[*], and the table is scanned for presence of NULLs if there's
no not-null constraint, and not scanned if there's one.  (The actual
scan is inevitable anyway because we must check the partition
constraint).  This seems the behavior we want.

[*] This attnotnull constraint is lost if you DETACH the partition and
drop the primary key, which is also the behavior we want.


While playing with it I noticed this other behavior change from 16,

create table pa (a int primary key) partition by list (a);
create table pe (a int unique);
alter table pa attach partition pe for values in (1, null);

In 16, we get the error:
ERROR:  column "a" in child table must be marked NOT NULL
which is correct (because the PK requires not-null).  In master we just
let that through, but that seems to be a separate bug.

-- 
Álvaro HerreraBreisgau, Deutschland  —  https://www.EnterpriseDB.com/
"Saca el libro que tu religión considere como el indicado para encontrar la
oración que traiga paz a tu alma. Luego rebootea el computador
y ve si funciona" (Carlos Duclós)




Re: cataloguing NOT NULL constraints

2024-02-05 Thread Alvaro Herrera
On 2024-Feb-05, Alvaro Herrera wrote:

> Subject: [PATCH v1] Fix failure to merge NOT NULL constraints in inheritance
> 
> set_attnotnull() was not careful to CommandCounterIncrement() in cases
> of multiple recursion.  Omission in b0e96f311985.

Eh, this needs to read "multiple inheritance" rather than "multiple
recursion".  (I'd also need to describe the change for the partitioning
cases in the commit message.)

-- 
Álvaro HerreraBreisgau, Deutschland  —  https://www.EnterpriseDB.com/
"Someone said that it is at least an order of magnitude more work to do
production software than a prototype. I think he is wrong by at least
an order of magnitude."  (Brian Kernighan)




Re: cataloguing NOT NULL constraints

2024-02-05 Thread Alvaro Herrera
On 2024-Feb-05, Alvaro Herrera wrote:

> Hmm, let me have a look, I can probably get this one fixed today before
> embarking on a larger fix elsewhere in the same feature.

You know what -- this missing CCI has a much more visible impact, which
is that the attnotnull marker that a primary key imposes on a partition
is propagated early.  So this regression test no longer fails:

create table cnn2_parted(a int primary key) partition by list (a);
create table cnn2_part1(a int);
alter table cnn2_parted attach partition cnn2_part1 for values in (1);

Here, in the existing code the ALTER TABLE ATTACH fails with the error
message that
  ERROR:  primary key column "a" is not marked NOT NULL
but with the patch, this no longer occurs.

I'm not sure that this behavior change is desirable ... I have vague
memories of people complaining that this sort of error was not very
welcome ... but on the other hand it seems now pretty clear that if it
*is* desirable, then its implementation is no good, because a single
added CCI breaks it.

I'm leaning towards accepting the behavior change, but I'd like to
investigate a little bit more first, but what do others think?

-- 
Álvaro Herrera PostgreSQL Developer  —  https://www.EnterpriseDB.com/
>From 486f6bfa8faa6162257c5f12b52180b5a3d89704 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera 
Date: Mon, 5 Feb 2024 10:18:43 +0100
Subject: [PATCH v1] Fix failure to merge NOT NULL constraints in inheritance

set_attnotnull() was not careful to CommandCounterIncrement() in cases
of multiple recursion.  Omission in b0e96f311985.

Reported-by: Alexander Lakhin 
Discussion: https://postgr.es/m/045dec3f-9b3d-aa44-0c99-85f699230...@gmail.com
---
 src/backend/commands/tablecmds.c  | 4 
 src/test/regress/expected/constraints.out | 1 -
 src/test/regress/expected/inherit.out | 8 
 src/test/regress/sql/inherit.sql  | 8 
 4 files changed, 20 insertions(+), 1 deletion(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 9f51696740..02724d5f04 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -7703,6 +7703,10 @@ set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum, bool recurse,
 		List	   *children;
 		ListCell   *lc;
 
+		/* Make above catalog changes visible */
+		if (retval)
+			CommandCounterIncrement();
+
 		children = find_inheritance_children(RelationGetRelid(rel), lockmode);
 		foreach(lc, children)
 		{
diff --git a/src/test/regress/expected/constraints.out b/src/test/regress/expected/constraints.out
index 5b068477bf..bef3d6577c 100644
--- a/src/test/regress/expected/constraints.out
+++ b/src/test/regress/expected/constraints.out
@@ -1010,7 +1010,6 @@ ERROR:  constraint "cnn_parent_pkey" of relation "cnn_parent" does not exist
 create table cnn2_parted(a int primary key) partition by list (a);
 create table cnn2_part1(a int);
 alter table cnn2_parted attach partition cnn2_part1 for values in (1);
-ERROR:  primary key column "a" is not marked NOT NULL
 drop table cnn2_parted, cnn2_part1;
 create table cnn2_parted(a int not null) partition by list (a);
 create table cnn2_part1(a int primary key);
diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out
index 130a924228..fe33bc4c2f 100644
--- a/src/test/regress/expected/inherit.out
+++ b/src/test/regress/expected/inherit.out
@@ -2496,6 +2496,14 @@ drop table inh_p1, inh_p2, inh_p3, inh_p4 cascade;
 NOTICE:  drop cascades to 2 other objects
 DETAIL:  drop cascades to table inh_multiparent
 drop cascades to table inh_multiparent2
+-- recursively add column with constraint while merging existing column
+create table inh_p1 ();
+create table inh_p2 (f1 int) inherits (inh_p1);
+create table inh_p3 () inherits (inh_p1, inh_p2);
+alter table inh_p1 add column f1 int not null;
+NOTICE:  merging definition of column "f1" for child "inh_p2"
+NOTICE:  merging definition of column "f1" for child "inh_p3"
+drop table inh_p1, inh_p2, inh_p3;
 --
 -- Mixed ownership inheritance tree
 --
diff --git a/src/test/regress/sql/inherit.sql b/src/test/regress/sql/inherit.sql
index 0ef9a61bc1..41cdde1d90 100644
--- a/src/test/regress/sql/inherit.sql
+++ b/src/test/regress/sql/inherit.sql
@@ -961,6 +961,14 @@ select conrelid::regclass, contype, conname,
 
 drop table inh_p1, inh_p2, inh_p3, inh_p4 cascade;
 
+-- recursively add column with constraint while merging existing column
+create table inh_p1 ();
+create table inh_p2 (f1 int) inherits (inh_p1);
+create table inh_p3 () inherits (inh_p1, inh_p2);
+alter table inh_p1 add column f1 int not null;
+
+drop table inh_p1, inh_p2, inh_p3;
+
 --
 -- Mixed ownership inheritance tree
 --
-- 
2.39.2



Re: cataloguing NOT NULL constraints

2024-02-05 Thread Alvaro Herrera
On 2024-Feb-05, Michael Paquier wrote:

> On Fri, Feb 02, 2024 at 07:00:01PM +0300, Alexander Lakhin wrote:
> > results in:
> > NOTICE:  merging definition of column "i" for child "b"
> > NOTICE:  merging definition of column "i" for child "c"
> > ERROR:  tuple already updated by self
> > 
> > (This is similar to bug #18297, but ATExecAddColumn() isn't guilty in this
> > case.)
> 
> Still I suspect that the fix should be similar, so I'll go put a coin
> on a missing CCI().

Hmm, let me have a look, I can probably get this one fixed today before
embarking on a larger fix elsewhere in the same feature.

-- 
Álvaro HerreraBreisgau, Deutschland  —  https://www.EnterpriseDB.com/
"After a quick R of TFM, all I can say is HOLY CR** THAT IS COOL! PostgreSQL was
amazing when I first started using it at 7.2, and I'm continually astounded by
learning new features and techniques made available by the continuing work of
the development team."
Berend Tober, http://archives.postgresql.org/pgsql-hackers/2007-08/msg01009.php




Re: cataloguing NOT NULL constraints

2024-02-04 Thread Michael Paquier
On Fri, Feb 02, 2024 at 07:00:01PM +0300, Alexander Lakhin wrote:
> results in:
> NOTICE:  merging definition of column "i" for child "b"
> NOTICE:  merging definition of column "i" for child "c"
> ERROR:  tuple already updated by self
> 
> (This is similar to bug #18297, but ATExecAddColumn() isn't guilty in this
> case.)

Still I suspect that the fix should be similar, soI'll go put a coin
on a missing CCI().
--
Michael


signature.asc
Description: PGP signature


Re: cataloguing NOT NULL constraints

2024-02-02 Thread Alexander Lakhin

Hello Alvaro,

Please look at an anomaly introduced with b0e96f311.
The following script:
CREATE TABLE a ();
CREATE TABLE b (i int) INHERITS (a);
CREATE TABLE c () INHERITS (a, b);

ALTER TABLE a ADD COLUMN i int NOT NULL;

results in:
NOTICE:  merging definition of column "i" for child "b"
NOTICE:  merging definition of column "i" for child "c"
ERROR:  tuple already updated by self

(This is similar to bug #18297, but ATExecAddColumn() isn't guilty in this
case.)

Best regards,
Alexander




Re: cataloguing NOT NULL constraints

2024-01-25 Thread Andrew Bille
Hi Alvaro,
25.08.2023 14:38, Alvaro Herrera wrote:
> I have now pushed this again. Hopefully it'll stick this time.

Starting from b0e96f31, pg_upgrade fails with inherited NOT NULL constraint:
For example upgrade from 9c13b6814a (or REL_12_STABLE .. REL_16_STABLE) to
b0e96f31 (or master) with following two tables (excerpt from
src/test/regress/sql/rules.sql)

create table test_0 (id serial primary key);
create table test_1 (id integer primary key) inherits (test_0);

I get the failure:

Setting frozenxid and minmxid counters in new cluster ok
Restoring global objects in the new cluster   ok
Restoring database schemas in the new cluster
 test
*failure*

Consult the last few lines of
"new/pg_upgrade_output.d/20240125T151231.112/log/pg_upgrade_dump_16384.log"
for
the probable cause of the failure.
Failure, exiting

In log:

pg_restore: connecting to database for restore
pg_restore: creating DATABASE "test"
pg_restore: connecting to new database "test"
pg_restore: creating DATABASE PROPERTIES "test"
pg_restore: connecting to new database "test"
pg_restore: creating pg_largeobject "pg_largeobject"
pg_restore: creating COMMENT "SCHEMA "public""
pg_restore: creating TABLE "public.test_0"
pg_restore: creating SEQUENCE "public.test_0_id_seq"
pg_restore: creating SEQUENCE OWNED BY "public.test_0_id_seq"
pg_restore: creating TABLE "public.test_1"
pg_restore: creating DEFAULT "public.test_0 id"
pg_restore: executing SEQUENCE SET test_0_id_seq
pg_restore: creating CONSTRAINT "public.test_0 test_0_pkey"
pg_restore: creating CONSTRAINT "public.test_1 test_1_pkey"
pg_restore: while PROCESSING TOC:
pg_restore: from TOC entry 3200; 2606 16397 CONSTRAINT test_1 test_1_pkey
andrew
pg_restore: error: could not execute query: ERROR:  cannot drop inherited
constraint "pgdump_throwaway_notnull_0" of relation "test_1"
Command was:
-- For binary upgrade, must preserve pg_class oids and relfilenodes
SELECT
pg_catalog.binary_upgrade_set_next_index_pg_class_oid('16396'::pg_catalog.oid);

SELECT
pg_catalog.binary_upgrade_set_next_index_relfilenode('16396'::pg_catalog.oid);


ALTER TABLE ONLY "public"."test_1"
   ADD CONSTRAINT "test_1_pkey" PRIMARY KEY ("id");

ALTER TABLE ONLY "public"."test_1" DROP CONSTRAINT
pgdump_throwaway_notnull_0;

Thanks!




On Thu, Jan 25, 2024 at 3:06 PM Alvaro Herrera 
wrote:

> I have now pushed this again.  Hopefully it'll stick this time.
>
> We may want to make some further tweaks to the behavior in some cases --
> for example, don't disallow ALTER TABLE DROP NOT NULL when the
> constraint is both inherited and has a local definition; the other
> option is to mark the constraint as no longer having a local definition.
> I left it the other way because that's what CHECK does; maybe we would
> like to change both at once.
>
> I ran it through CI, and the pg_upgrade test with a dump from 14's
> regression test database and everything worked well, but it's been a
> while since I tested the sepgsql part of it, so that might the first
> thing to explode.
>
> --
> Álvaro Herrera   48°01'N 7°57'E  —
> https://www.EnterpriseDB.com/
>
>
>
>
>


Re: cataloguing NOT NULL constraints

2023-11-08 Thread Alvaro Herrera
On 2023-Oct-12, Alexander Lakhin wrote:

Hello,

> I've discovered that that commit added several recursive functions, and
> some of them are not protected from stack overflow.

True.  I reproduced the first two, but didn't attempt to reproduce the
third one -- patching all these to check for stack depth is cheap
protection.  I also patched ATAddCheckNNConstraint:

> (ATAddCheckNNConstraint() is protected because it calls
> AddRelationNewConstraints(), which in turn calls StoreRelCheck() ->
> CreateConstraintEntry() ->  recordDependencyOnSingleRelExpr() ->
> find_expr_references_walker() ->  expression_tree_walker() ->
> expression_tree_walker() -> check_stack_depth().)

because it seems uselessly risky to rely on depth checks that exist on
completely unrelated pieces of code, when the function visibly recurses
on itself.  Especially so since the test cases that demonstrate crashes
are so expensive to run, which means we're not going to detect it if at
some point that other stack depth check stops being called for whatever
reason.

BTW probably the tests could be made much cheaper by running the server
with a lower "ulimit -s" setting.  I didn't try.

I noticed one more crash while trying to "drop table" one of the
hierarchies your scripts create.  But it's a preexisting issue which
needs a backpatched fix, and I think Egor already reported it in the
other thread.

Thank you

-- 
Álvaro HerreraBreisgau, Deutschland  —  https://www.EnterpriseDB.com/
"Industry suffers from the managerial dogma that for the sake of stability
and continuity, the company should be independent of the competence of
individual employees."  (E. Dijkstra)




Re: cataloguing NOT NULL constraints

2023-10-12 Thread Alexander Lakhin

Hi Alvaro,

25.08.2023 14:38, Alvaro Herrera wrote:

I have now pushed this again.  Hopefully it'll stick this time.


I've discovered that that commit added several recursive functions, and
some of them are not protected from stack overflow.

Namely, with "max_locks_per_transaction = 600" and default ulimit -s (8192),
I observe server crashes with the following scripts:
# ATExecSetNotNull()
(n=4; printf "create table t0 (a int, b int);";
for ((i=1;i<=$n;i++)); do printf "create table t$i() inherits(t$(( $i - 1 ))); 
"; done;
printf "alter table t0 alter b set not null;" ) | psql >psql.log

# dropconstraint_internal()
(n=2; printf "create table t0 (a int, b int not null);";
for ((i=1;i<=$n;i++)); do printf "create table t$i() inherits(t$(( $i - 1 ))); 
"; done;
printf "alter table t0 alter b drop not null;" ) | psql >psql.log

# set_attnotnull()
(n=11; printf "create table tp (a int, b int, primary key(a, b)) partition by range (a); create table tp0 (a int 
primary key, b int) partition by range (a);";
for ((i=1;i<=$n;i++)); do printf "create table tp$i partition of tp$(( $i - 1 )) for values from ($i) to (100) 
partition by range (a);"; done;
printf "alter table tp attach partition tp0 for values from (0) to (100);") | psql >psql.log # this takes half an 
hour on my machine


May be you would find appropriate to add check_stack_depth() to these
functions.

(ATAddCheckNNConstraint() is protected because it calls
AddRelationNewConstraints(), which in turn calls StoreRelCheck() ->
CreateConstraintEntry() ->  recordDependencyOnSingleRelExpr() ->
find_expr_references_walker() ->  expression_tree_walker() ->
expression_tree_walker() -> check_stack_depth().)

(There were patches prepared for similar cases [1], but they don't cover new
functions, of course, and I'm not sure how to handle all such instances.)

[1] https://commitfest.postgresql.org/45/4239/

Best regards,
Alexander




Re: cataloguing NOT NULL constraints

2023-09-05 Thread Alvaro Herrera
On 2023-Sep-05, Peter Eisentraut wrote:

> The following information schema views are affected by the not-null
> constraint catalog entries:
> 
> 1. CHECK_CONSTRAINTS
> 2. CONSTRAINT_COLUMN_USAGE
> 3. DOMAIN_CONSTRAINTS
> 4. TABLE_CONSTRAINTS
> 
> Note that 1 and 3 also contain domain constraints.  So as long as the domain
> not-null constraints are not similarly catalogued, we can't delete the
> separate not-null union branch.  (3 never had one, so arguably a bit buggy.)
> 
> I think we can fix up 4 by just deleting the not-null union branch.
> 
> For 2, the simple fix is also easy, but there are some other options, as you
> discuss above.
> 
> How do you want to proceed?

I posted as a patch in a separate thread[1].  Let me fix up the
definitions for views 1 and 3 for domains per your comments, and I'll
post in that thread again.

[1] https://postgr.es/m/202309041710.psytrxlsiqex@alvherre.pgsql

-- 
Álvaro Herrera   48°01'N 7°57'E  —  https://www.EnterpriseDB.com/
"Ninguna manada de bestias tiene una voz tan horrible como la humana" (Orual)




Re: cataloguing NOT NULL constraints

2023-09-05 Thread Peter Eisentraut

On 31.08.23 12:02, Alvaro Herrera wrote:

In constraint_column_usage, you're adding a relkind to the catalog scan
that goes through pg_depend for CHECK constraints.  Here we can rely on
a simple conkey[1] check and a separate UNION ALL arm[q5]; this is also
faster when there are many tables.

The third view definition looks ok.  It's certainly very nice to be able
to delete XXX comments there.


The following information schema views are affected by the not-null 
constraint catalog entries:


1. CHECK_CONSTRAINTS
2. CONSTRAINT_COLUMN_USAGE
3. DOMAIN_CONSTRAINTS
4. TABLE_CONSTRAINTS

Note that 1 and 3 also contain domain constraints.  So as long as the 
domain not-null constraints are not similarly catalogued, we can't 
delete the separate not-null union branch.  (3 never had one, so 
arguably a bit buggy.)


I think we can fix up 4 by just deleting the not-null union branch.

For 2, the simple fix is also easy, but there are some other options, as 
you discuss above.


How do you want to proceed?





Re: cataloguing NOT NULL constraints

2023-09-04 Thread Alvaro Herrera
Looking at your 0001 patch, which adds tests for some of the
information_schema views, I think it's a bad idea to put them in
whatever other regression .sql files; they would be subject to
concurrent changes depending on what other tests are being executed in
the same parallel test.  I suggest to put them all in a separate .sql
file, and schedule that to run in the last concurrent group, together
with the tablespace test.  This way, it would capture all the objects
left over by other test files.

-- 
Álvaro HerreraBreisgau, Deutschland  —  https://www.EnterpriseDB.com/




Re: cataloguing NOT NULL constraints

2023-09-01 Thread Alvaro Herrera
On 2023-Aug-31, Alvaro Herrera wrote:

> Hmm, that's some weird code I left there all right.  Can you please try
> this patch?  (Not final; I'll review it more completely later,
> particularly to add this test case.)

The change in MergeAttributesIntoExisting turned out to be close but not
quite there, so I pushed another version of the fix.

In case you're wondering, the change in MergeConstraintsIntoExisting is
a related but different case, for which I added the other test case you
see there.

I also noticed, while looking at this, that there's another problem when
a child has a NO INHERIT not-null constraint and the parent has a
primary key in the same column.  It should refuse, or take over by
marking it no longer NO INHERIT.  But it just accepts silently and all
appears to be good.  The problems appear when you add a child to that
child.  I'll look into this later; it's not exactly the same code.  At
least it's not a crasher.

-- 
Álvaro HerreraBreisgau, Deutschland  —  https://www.EnterpriseDB.com/




Re: cataloguing NOT NULL constraints

2023-08-31 Thread Alexander Lakhin

31.08.2023 13:26, Alvaro Herrera wrote:

Hmm, that's some weird code I left there all right.  Can you please try
this patch?  (Not final; I'll review it more completely later,
particularly to add this test case.)


Yes, your patch fixes the issue. I get the same error now:
ERROR:  column "a" in child table must be marked NOT NULL

Thank you!

Best regards,
Alexander




Re: cataloguing NOT NULL constraints

2023-08-31 Thread Alvaro Herrera
Hello Alexander,

Thanks for testing.

On 2023-Aug-31, Alexander Lakhin wrote:

> 25.08.2023 14:38, Alvaro Herrera wrote:
> > I have now pushed this again.  Hopefully it'll stick this time.
> 
> I've found that after that commit the following query:
> CREATE TABLE t(a int PRIMARY KEY) PARTITION BY RANGE (a);
> CREATE TABLE tp1(a int);
> ALTER TABLE t ATTACH PARTITION tp1 FOR VALUES FROM (0) to (1);
> 
> triggers a server crash:

Hmm, that's some weird code I left there all right.  Can you please try
this patch?  (Not final; I'll review it more completely later,
particularly to add this test case.)

-- 
Álvaro Herrera   48°01'N 7°57'E  —  https://www.EnterpriseDB.com/
 It does it in a really, really complicated way
 why does it need to be complicated?
 Because it's MakeMaker.
>From ab241913dec84265ca64d3cb76d1509bb7ce1808 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera 
Date: Thu, 31 Aug 2023 12:24:18 +0200
Subject: [PATCH] Fix not-null constraint test

Per report from Alexander Lakhin
---
 src/backend/commands/tablecmds.c | 13 -
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index d097da3c78..5941d0a4be 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -15750,7 +15750,8 @@ MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel)
 
 contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel),
 	 attribute->attnum);
-if (!((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
+
+if (!HeapTupleIsValid(contup))
 	ereport(ERROR,
 			(errcode(ERRCODE_DATATYPE_MISMATCH),
 			 errmsg("column \"%s\" in child table must be marked NOT NULL",
@@ -15975,10 +15976,20 @@ MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
 		systable_endscan(child_scan);
 
 		if (!found)
+		{
+			if (parent_con->contype == CONSTRAINT_NOTNULL)
+ereport(ERROR,
+		errcode(ERRCODE_DATATYPE_MISMATCH),
+		errmsg("column \"%s\" in child table must be marked NOT NULL",
+			   get_attname(parent_relid,
+		   extractNotNullColumn(parent_tuple),
+		   false)));
+
 			ereport(ERROR,
 	(errcode(ERRCODE_DATATYPE_MISMATCH),
 	 errmsg("child table is missing constraint \"%s\"",
 			NameStr(parent_con->conname;
+		}
 	}
 
 	systable_endscan(parent_scan);
-- 
2.39.2



Re: cataloguing NOT NULL constraints

2023-08-31 Thread Alvaro Herrera
On 2023-Mar-29, Peter Eisentraut wrote:

> On 27.03.23 15:55, Peter Eisentraut wrote:
> > The information schema should be updated.  I think the following views:
> > 
> > - CHECK_CONSTRAINTS
> > - CONSTRAINT_COLUMN_USAGE
> > - DOMAIN_CONSTRAINTS
> > - TABLE_CONSTRAINTS
> > 
> > It looks like these have no test coverage; maybe that could be addressed
> > at the same time.
> 
> Here are patches for this.  I haven't included the expected files for the
> tests; this should be checked again that output is correct or the changes
> introduced by this patch set are as expected.
> 
> The reason we didn't have tests for this before was probably in part because
> the information schema made up names for not-null constraints involving
> OIDs, so the test wouldn't have been stable.
> 
> Feel free to integrate this, or we can add it on afterwards.

I'm eyeing patch 0002 here.  I noticed that in view check_constraints it
defines the not-null constraint definition as substrings over the
pg_get_constraintdef() function[q1], so I wondered whether it might be
better to join to pg_attribute instead.  I see two options:

1. add a scalar subselect in the select list for each constraint [q2]
2. add a LEFT JOIN to pg_attribute to the main FROM list [q3]
   ON con.conrelid=att.attrelid AND con.conkey[1] = con.attrelid

With just the regression test tables in place, these forms are all
pretty much the same in execution time.  I then created 20k tables with
6 columns each and NOT NULL constraint on each column[4].  That's not a
huge amount but it's credible for a medium-size database with a bunch of
partitions (it's amazing what passes for a medium-size database these
days).  I was surprised to find out that q3 (~130ms) is three times
faster than q2 (~390ms), which is in turn more than twice faster than
your proposed q1 (~870ms).  So unless you have another reason to prefer
it, I think we should use q3 here.


In constraint_column_usage, you're adding a relkind to the catalog scan
that goes through pg_depend for CHECK constraints.  Here we can rely on
a simple conkey[1] check and a separate UNION ALL arm[q5]; this is also
faster when there are many tables.

The third view definition looks ok.  It's certainly very nice to be able
to delete XXX comments there.


[q1]
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,
CASE con.contype
WHEN 'c'::"char" THEN 
"left"(SUBSTRING(pg_get_constraintdef(con.oid) FROM 8), '-1'::integer)
WHEN 'n'::"char" THEN SUBSTRING(pg_get_constraintdef(con.oid) FROM 
10) || ' IS NOT NULL'::text 
ELSE NULL::text
END::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
 LEFT JOIN pg_type t ON t.oid = con.contypid

  WHERE pg_has_role(COALESCE(c.relowner, t.typowner), 'USAGE'::text) AND 
(con.contype = ANY (ARRAY['c'::"char", 'n'::"char"]));

[q2]
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,
CASE con.contype
WHEN 'c'::"char" THEN 
"left"(SUBSTRING(pg_get_constraintdef(con.oid) FROM 8), '-1'::integer)
WHEN 'n'::"char" THEN FORMAT('CHECK (%s IS NOT NULL)',
 (SELECT attname FROM pg_attribute 
WHERE attrelid = conrelid AND attnum = conkey[1]))
ELSE NULL::text
END::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
 LEFT JOIN pg_type t ON t.oid = con.contypid
  WHERE pg_has_role(COALESCE(c.relowner, t.typowner), 'USAGE'::text) AND 
(con.contype = ANY (ARRAY['c'::"char", 'n'::"char"]));

[q3]
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,
CASE con.contype
WHEN 'c'::"char" THEN 
"left"(SUBSTRING(pg_get_constraintdef(con.oid) FROM 8), '-1'::integer)
WHEN 'n'::"char" THEN FORMAT('CHECK (%s IS NOT NULL)', at.attname)  
   
ELSE NULL::text
END::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
 

Re: cataloguing NOT NULL constraints

2023-08-31 Thread Alexander Lakhin

Hi Alvaro,

25.08.2023 14:38, Alvaro Herrera wrote:

I have now pushed this again.  Hopefully it'll stick this time.


I've found that after that commit the following query:
CREATE TABLE t(a int PRIMARY KEY) PARTITION BY RANGE (a);
CREATE TABLE tp1(a int);
ALTER TABLE t ATTACH PARTITION tp1 FOR VALUES FROM (0) to (1);

triggers a server crash:
Core was generated by `postgres: law regression [local] ALTER TABLE 
 '.
Program terminated with signal SIGSEGV, Segmentation fault.

warning: Section `.reg-xstate/2194811' in core file too small.
#0  0x556007711d77 in MergeAttributesIntoExisting (child_rel=0x7fc30ba309d8,
    parent_rel=0x7fc30ba33f18) at tablecmds.c:15771
15771   if (!((Form_pg_constraint) 
GETSTRUCT(contup))->connoinherit)
(gdb) bt
#0  0x556007711d77 in MergeAttributesIntoExisting (child_rel=0x7fc30ba309d8,
    parent_rel=0x7fc30ba33f18) at tablecmds.c:15771
#1  0x5560077118d4 in CreateInheritance (child_rel=0x7fc30ba309d8, 
parent_rel=0x7fc30ba33f18)
    at tablecmds.c:15631
...

(gdb) print contup
$1 = (HeapTuple) 0x0

On b0e96f311~1 I get:
ERROR:  column "a" in child table must be marked NOT NULL

Best regards,
Alexander

Re: cataloguing NOT NULL constraints

2023-08-28 Thread Alvaro Herrera
On 2023-Aug-28, Peter Eisentraut wrote:

> It looks like we forgot about domain constraints?  For example,
> 
> create domain testdomain as int not null;
> 
> should create a row in pg_constraint?

Well, at some point I purposefully left them out; they were sufficiently
different from the ones in tables that doing both things at the same
time was not saving any effort.  I guess we could try to bake them in
now.

-- 
Álvaro HerreraBreisgau, Deutschland  —  https://www.EnterpriseDB.com/
"Doing what he did amounts to sticking his fingers under the hood of the
implementation; if he gets his fingers burnt, it's his problem."  (Tom Lane)




Re: cataloguing NOT NULL constraints

2023-08-28 Thread Peter Eisentraut

On 25.08.23 13:38, Alvaro Herrera wrote:

I have now pushed this again.  Hopefully it'll stick this time.

We may want to make some further tweaks to the behavior in some cases --
for example, don't disallow ALTER TABLE DROP NOT NULL when the
constraint is both inherited and has a local definition; the other
option is to mark the constraint as no longer having a local definition.
I left it the other way because that's what CHECK does; maybe we would
like to change both at once.

I ran it through CI, and the pg_upgrade test with a dump from 14's
regression test database and everything worked well, but it's been a
while since I tested the sepgsql part of it, so that might the first
thing to explode.


It looks like we forgot about domain constraints?  For example,

create domain testdomain as int not null;

should create a row in pg_constraint?





Re: cataloguing NOT NULL constraints

2023-08-25 Thread Alvaro Herrera
On 2023-Aug-25, Alvaro Herrera wrote:

> I have now pushed this again.  Hopefully it'll stick this time.

Hmm, failed under the Czech locale[1]; apparently "inh_grandchld" sorts
earlier than "inh_child1" there.  I think I'll rename inh_grandchld to
inh_child3 or something like that.

[1] 
https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=hippopotamus=2023-08-25%2011%3A33%3A07

-- 
Álvaro Herrera   48°01'N 7°57'E  —  https://www.EnterpriseDB.com/




Re: cataloguing NOT NULL constraints

2023-08-25 Thread Alvaro Herrera
I have now pushed this again.  Hopefully it'll stick this time.

We may want to make some further tweaks to the behavior in some cases --
for example, don't disallow ALTER TABLE DROP NOT NULL when the
constraint is both inherited and has a local definition; the other
option is to mark the constraint as no longer having a local definition.
I left it the other way because that's what CHECK does; maybe we would
like to change both at once.

I ran it through CI, and the pg_upgrade test with a dump from 14's
regression test database and everything worked well, but it's been a
while since I tested the sepgsql part of it, so that might the first
thing to explode.

-- 
Álvaro Herrera   48°01'N 7°57'E  —  https://www.EnterpriseDB.com/




Re: cataloguing NOT NULL constraints

2023-08-16 Thread Peter Eisentraut

I have two small patches that you can integrate into your patch set:

The first just changes the punctuation of "Not-null constraints" in the 
psql output to match what the documentation mostly uses.


The second has some changes to ddl.sgml to reflect that not-null 
constraints are now named and can be operated on like other constraints. 
 You might want to read that again to make sure it matches your latest 
intentions, but I think it catches all the places that are required to 
change.From 324f0050eee51c47e4c558867e6cc832652b39bb Mon Sep 17 00:00:00 2001
From: Peter Eisentraut 
Date: Wed, 16 Aug 2023 10:26:09 +0200
Subject: [PATCH 1/2] fixup! Have psql print the NOT NULL constraints on \d+

---
 contrib/test_decoding/expected/ddl.out|  8 +-
 src/bin/psql/describe.c   |  2 +-
 src/test/regress/expected/create_table.out|  6 +-
 .../regress/expected/create_table_like.out| 10 +--
 src/test/regress/expected/foreign_data.out| 84 +--
 src/test/regress/expected/generated.out   |  2 +-
 src/test/regress/expected/identity.out|  2 +-
 src/test/regress/expected/publication.out |  6 +-
 .../regress/expected/replica_identity.out |  6 +-
 src/test/regress/expected/rowsecurity.out |  2 +-
 10 files changed, 64 insertions(+), 64 deletions(-)

diff --git a/contrib/test_decoding/expected/ddl.out 
b/contrib/test_decoding/expected/ddl.out
index 95a0722c33..bcd1f74b2b 100644
--- a/contrib/test_decoding/expected/ddl.out
+++ b/contrib/test_decoding/expected/ddl.out
@@ -492,7 +492,7 @@ WITH (user_catalog_table = true)
  options  | text[]  |   |  |   
   | extended |  | 
 Indexes:
 "replication_metadata_pkey" PRIMARY KEY, btree (id)
-Not null constraints:
+Not-null constraints:
 "replication_metadata_id_not_null" NOT NULL "id"
 "replication_metadata_relation_not_null" NOT NULL "relation"
 Options: user_catalog_table=true
@@ -509,7 +509,7 @@ ALTER TABLE replication_metadata RESET (user_catalog_table);
  options  | text[]  |   |  |   
   | extended |  | 
 Indexes:
 "replication_metadata_pkey" PRIMARY KEY, btree (id)
-Not null constraints:
+Not-null constraints:
 "replication_metadata_id_not_null" NOT NULL "id"
 "replication_metadata_relation_not_null" NOT NULL "relation"
 
@@ -525,7 +525,7 @@ ALTER TABLE replication_metadata SET (user_catalog_table = 
true);
  options  | text[]  |   |  |   
   | extended |  | 
 Indexes:
 "replication_metadata_pkey" PRIMARY KEY, btree (id)
-Not null constraints:
+Not-null constraints:
 "replication_metadata_id_not_null" NOT NULL "id"
 "replication_metadata_relation_not_null" NOT NULL "relation"
 Options: user_catalog_table=true
@@ -547,7 +547,7 @@ ALTER TABLE replication_metadata SET (user_catalog_table = 
false);
  rewritemeornot | integer |   |  | 
 | plain|  | 
 Indexes:
 "replication_metadata_pkey" PRIMARY KEY, btree (id)
-Not null constraints:
+Not-null constraints:
 "replication_metadata_id_not_null" NOT NULL "id"
 "replication_metadata_relation_not_null" NOT NULL "relation"
 Options: user_catalog_table=false
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index d1dc8fa066..4d36e0cfd8 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -3072,7 +3072,7 @@ describeOneTableDetails(const char *schemaname,
tuples = PQntuples(result);
 
if (tuples > 0)
-   printTableAddFooter(, _("Not null 
constraints:"));
+   printTableAddFooter(, _("Not-null 
constraints:"));
 
/* Might be an empty set - that's ok */
for (i = 0; i < tuples; i++)
diff --git a/src/test/regress/expected/create_table.out 
b/src/test/regress/expected/create_table.out
index 3f6516c3f8..477e8839e9 100644
--- a/src/test/regress/expected/create_table.out
+++ b/src/test/regress/expected/create_table.out
@@ -854,7 +854,7 @@ drop table test_part_coll_posix;
  b  | integer |   | not null | 1   | plain|  | 
 Partition of: parted FOR VALUES IN ('b')
 Partition constraint: ((a IS NOT NULL) AND (a = 'b'::text))
-Not null constraints:
+Not-null constraints:
 "part_b_b_not_null" NOT NULL "b"
 
 -- Both partition bound and partition key in describe output
@@ -867,7 +867,7 @@ Not null constraints:
 Partition of: parted FOR VALUES IN ('c')
 Partition constraint: ((a IS NOT NULL) AND (a = 'c'::text))
 Partition key: RANGE (b)
-Not null constraints:
+Not-null constraints:
 "part_c_b_not_null" NOT NULL "b"
 Partitions: part_c_1_10 FOR VALUES FROM (1) TO (10)
 
@@ -880,7 +880,7 @@ Partitions: part_c_1_10 FOR 

Re: cataloguing NOT NULL constraints

2023-08-16 Thread Peter Eisentraut

On 15.08.23 11:57, Dean Rasheed wrote:

Something else I noticed when reading the SQL standard is that a
user-defined CHECK (col IS NOT NULL) constraint should be recognised
by the system as also making the column not null (setting its
"nullability characteristic" to "known not nullable"). I think that's
more than just an artefact of how they say NOT NULL constraints should
be implemented, because the effect of such a CHECK constraint should
be exposed in the "columns" view of the information schema -- the
value of "is_nullable" should be "NO" if the column is "known not
nullable".


Nullability determination is different from not-null constraints.  The 
nullability characteristic of a column can be derived from multiple 
sources, including not-null constraints, check constraints, primary key 
constraints, domain constraints, as well as more complex rules in case 
of views, joins, etc.  But this is all distinct and separate from the 
issue of not-null constraints that we are discussing here.





Re: cataloguing NOT NULL constraints

2023-08-15 Thread Alvaro Herrera
On 2023-Aug-15, Dean Rasheed wrote:

> I think perhaps for ALTER TABLE INHERIT, it should check that the
> child has a NOT NULL constraint, and error out if not. That's the
> current behaviour, and also matches other constraints types (e.g.,
> CHECK constraints).

Yeah, I reached the same conclusion yesterday while trying it out, so
that's what I implemented.  I'll post later today.

> More generally though, I'm worried that this is starting to get very
> complicated. I wonder if there might be a different, simpler approach.
> One vague idea is to have a new attribute on the column that counts
> the number of constraints (local and inherited PK and NOT NULL
> constraints) that make the column not null.

Hmm.  I grant that this is different, but I don't see that it is
simpler.

> Something else I noticed when reading the SQL standard is that a
> user-defined CHECK (col IS NOT NULL) constraint should be recognised
> by the system as also making the column not null (setting its
> "nullability characteristic" to "known not nullable").

I agree with this view actually, but I've refrained from implementing
it(*) because our SQL-standards people have advised against it.  Insider
knowledge?  I don't know.  I think this is a comparatively smaller
consideration though, and we can adjust for it afterwards.

(*) Rather: at some point I removed the implementation of that from the
patch.

> I'm also wondering whether creating a pg_constraint entry for *every*
> not-nullable column is actually going too far. If we were to
> distinguish between "defined as NOT NULL" and being not null as a
> result of one or more constraints, in the way that the standard seems
> to suggest, perhaps the former (likely to be much more common) could
> simply be a new attribute stored on the column. I think we actually
> only need to create pg_constraint entries if a constraint name or any
> additional constraint properties such as NOT VALID are specified. That
> would lead to far fewer new constraints, less catalog bloat, and less
> noise in the \d output.

There is a problem if we do this, though, which is that we cannot use
the constraints for the things that we want them for -- for example,
remove_useless_groupby_columns() would like to use unique constraints,
not just primary keys; but it depends on the NOT NULL rows being there
for invalidation reasons (namely: if the NOT NULL constraint is dropped,
we need to be able to replan.  Without catalog rows, we don't have a
mechanism to let that happen).

If we don't add all those redundant catalog rows, then this is all for
naught.

-- 
Á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: cataloguing NOT NULL constraints

2023-08-15 Thread Dean Rasheed
On Fri, 11 Aug 2023 at 14:54, Alvaro Herrera  wrote:
>
> Right, in the end I got around to that point of view.  I abandoned the
> idea of adding these dependency links, and I'm back at relying on the
> coninhcount/conislocal markers.  But there were a couple of bugs in the
> accounting for that, so I've fixed some of those, but it's not yet
> complete:
>
> - ALTER TABLE parent ADD PRIMARY KEY
>   needs to create NOT NULL constraints in children.  I added this, but
>   I'm not yet sure it works correctly (for example, if a child already
>   has a NOT NULL constraint, we need to bump its inhcount, but we
>   don't.)
> - ALTER TABLE parent ADD PRIMARY KEY USING index
>   Not sure if this is just as above or needs separate handling
> - ALTER TABLE DROP PRIMARY KEY
>   needs to decrement inhcount or drop the constraint if there are no
>   other sources for that constraint to exist.  I've adjusted the drop
>   constraint code to do this.
> - ALTER TABLE INHERIT
>   needs to create a constraint on the new child, if parent has PK. Not
>   implemented
> - ALTER TABLE NO INHERIT
>   needs to delink any constraints (decrement inhcount, possibly drop
>   the constraint).
>

I think perhaps for ALTER TABLE INHERIT, it should check that the
child has a NOT NULL constraint, and error out if not. That's the
current behaviour, and also matches other constraints types (e.g.,
CHECK constraints).

More generally though, I'm worried that this is starting to get very
complicated. I wonder if there might be a different, simpler approach.
One vague idea is to have a new attribute on the column that counts
the number of constraints (local and inherited PK and NOT NULL
constraints) that make the column not null.

Something else I noticed when reading the SQL standard is that a
user-defined CHECK (col IS NOT NULL) constraint should be recognised
by the system as also making the column not null (setting its
"nullability characteristic" to "known not nullable"). I think that's
more than just an artefact of how they say NOT NULL constraints should
be implemented, because the effect of such a CHECK constraint should
be exposed in the "columns" view of the information schema -- the
value of "is_nullable" should be "NO" if the column is "known not
nullable".

In this sense, the standard does allow multiple not null constraints
on a column, independently of whether the column is "defined as NOT
NULL". My understanding of the standard is that ALTER COLUMN ...
SET/DROP NOT NULL change whether or not the column is "defined as NOT
NULL", and manage a single system-generated constraint, but there may
be any number of other user-defined constraints that also make the
column "known not nullable", and they need to be tracked in some way.

I'm also wondering whether creating a pg_constraint entry for *every*
not-nullable column is actually going too far. If we were to
distinguish between "defined as NOT NULL" and being not null as a
result of one or more constraints, in the way that the standard seems
to suggest, perhaps the former (likely to be much more common) could
simply be a new attribute stored on the column. I think we actually
only need to create pg_constraint entries if a constraint name or any
additional constraint properties such as NOT VALID are specified. That
would lead to far fewer new constraints, less catalog bloat, and less
noise in the \d output.

Regards,
Dean




Re: cataloguing NOT NULL constraints

2023-08-09 Thread Alvaro Herrera
On 2023-Aug-09, Peter Eisentraut wrote:

> I wonder whether the root of these problems is that we mix together primary
> key constraints and not-null constraints.  I understand that right now, with
> the proposed patch, when a table inherits from a parent table with a primary
> key constraint, we generate not-null constraints on the child, in order to
> enforce the not-nullness.  What if we did something like this instead: In
> the child table, we don't generate a not-null constraint, but instead a
> primary key constraint entry.  But we mark the primary key constraint
> somehow to say, this is just for the purpose of inheritance, don't enforce
> uniqueness, but enforce not-nullness.  Would that work?

Hmm.  One table can have many parents, and many of them can have primary
keys.  If we tried to model it the way you suggest, the child table
would need to have several primary keys.  I don't think this would work.

But I think I just need to stare at the dependency graph a little while
longer.  Maybe I just need to add some extra edges to make it work
correctly.

-- 
Álvaro Herrera   48°01'N 7°57'E  —  https://www.EnterpriseDB.com/




Re: cataloguing NOT NULL constraints

2023-08-09 Thread Peter Eisentraut

On 05.08.23 21:50, Dean Rasheed wrote:

Anyway, I was at the same time fixing the other problem you reported
with inheritance (namely, adding a PK ends up with the child column
being marked NOT NULL but no corresponding constraint).

At some point I wondered if the easy way out wouldn't be to give up on
the idea that creating a PK causes the child columns to be marked
not-nullable.  However, IIRC I decided against that because it breaks
restoring of old dumps, so it wouldn't be acceptable.

To make matters worse: pg_dump creates the PK as

   ALTER TABLE ONLY parent ADD PRIMARY KEY ( ... )

note the ONLY there.  It seems I'm forced to cause the PK to affect
children even though ONLY is given.  This is undesirable but I don't see
a way out of that.

It is all a bit of a rat's nest.



I wonder if that could be made to work in the same way as inherited
CHECK constraints -- dump the child's inherited NOT NULL constraints,
and then manually update conislocal in pg_constraint.


I wonder whether the root of these problems is that we mix together 
primary key constraints and not-null constraints.  I understand that 
right now, with the proposed patch, when a table inherits from a parent 
table with a primary key constraint, we generate not-null constraints on 
the child, in order to enforce the not-nullness.  What if we did 
something like this instead: In the child table, we don't generate a 
not-null constraint, but instead a primary key constraint entry.  But we 
mark the primary key constraint somehow to say, this is just for the 
purpose of inheritance, don't enforce uniqueness, but enforce 
not-nullness.  Would that work?






Re: cataloguing NOT NULL constraints

2023-08-05 Thread Dean Rasheed
On Sat, 5 Aug 2023 at 18:37, Alvaro Herrera  wrote:
>
> Yeah, something like that.  However, if the child had a NOT NULL
> constraint of its own, then it should not be deleted when the
> PK-on-parent is, but merely marked as no longer inherited.  (This is
> also what happens with a straight NOT NULL constraint.)  I think what
> this means is that at some point during the deletion of the PK we must
> remove the dependency link rather than letting it be followed.  I'm not
> yet sure how to do this.
>

I'm not sure that adding that new dependency was the right thing to
do. I think perhaps this could just be made to work using conislocal
and coninhcount to track whether the child constraint needs to be
deleted, or just updated.

> Anyway, I was at the same time fixing the other problem you reported
> with inheritance (namely, adding a PK ends up with the child column
> being marked NOT NULL but no corresponding constraint).
>
> At some point I wondered if the easy way out wouldn't be to give up on
> the idea that creating a PK causes the child columns to be marked
> not-nullable.  However, IIRC I decided against that because it breaks
> restoring of old dumps, so it wouldn't be acceptable.
>
> To make matters worse: pg_dump creates the PK as
>
>   ALTER TABLE ONLY parent ADD PRIMARY KEY ( ... )
>
> note the ONLY there.  It seems I'm forced to cause the PK to affect
> children even though ONLY is given.  This is undesirable but I don't see
> a way out of that.
>
> It is all a bit of a rat's nest.
>

I wonder if that could be made to work in the same way as inherited
CHECK constraints -- dump the child's inherited NOT NULL constraints,
and then manually update conislocal in pg_constraint.

Regards,
Dean




Re: cataloguing NOT NULL constraints

2023-08-05 Thread Alvaro Herrera
On 2023-Aug-05, Dean Rasheed wrote:

> Hmm, thinking about this some more, I think this might be the wrong
> approach to fixing the original problem. I think it was probably OK
> that the NOT NULL constraint on the child was marked as inherited, but
> I think what should have happened is that dropping the PRIMARY KEY
> constraint on the parent should have caused the NOT NULL constraint on
> the child to have been deleted (in the same way as it would have been,
> if it had been a NOT NULL constraint on the parent).

Yeah, something like that.  However, if the child had a NOT NULL
constraint of its own, then it should not be deleted when the
PK-on-parent is, but merely marked as no longer inherited.  (This is
also what happens with a straight NOT NULL constraint.)  I think what
this means is that at some point during the deletion of the PK we must
remove the dependency link rather than letting it be followed.  I'm not
yet sure how to do this.

Anyway, I was at the same time fixing the other problem you reported
with inheritance (namely, adding a PK ends up with the child column
being marked NOT NULL but no corresponding constraint).

At some point I wondered if the easy way out wouldn't be to give up on
the idea that creating a PK causes the child columns to be marked
not-nullable.  However, IIRC I decided against that because it breaks
restoring of old dumps, so it wouldn't be acceptable.

To make matters worse: pg_dump creates the PK as 

  ALTER TABLE ONLY parent ADD PRIMARY KEY ( ... )

note the ONLY there.  It seems I'm forced to cause the PK to affect
children even though ONLY is given.  This is undesirable but I don't see
a way out of that.

It is all a bit of a rat's nest.

-- 
Álvaro HerreraBreisgau, Deutschland  —  https://www.EnterpriseDB.com/
"Nunca se desea ardientemente lo que solo se desea por razón" (F. Alexandre)




Re: cataloguing NOT NULL constraints

2023-08-05 Thread Dean Rasheed
On Fri, 4 Aug 2023 at 19:10, Alvaro Herrera  wrote:
>
> On 2023-Jul-28, Alvaro Herrera wrote:
>
> > To avoid that, one option would be to make this NN constraint
> > undroppable ...  but I don't see how.  One option might be to add a
> > pg_depend row that links the NOT NULL constraint to its PK constraint.
> > But this will be a strange case that occurs nowhere else, since other
> > NOT NULL constraint don't have such pg_depend rows.  Also, I won't know
> > how pg_dump likes this until I implement it.
>
> I've been completing the implementation for this.  It seems to work
> reasonably okay; pg_dump requires somewhat strange contortions, but they
> are similar to what we do in flagInhTables already, so I don't feel too
> bad about that.
>
> What *is* odd and bothersome is that it also causes a problem dropping
> the child table.
>

Hmm, thinking about this some more, I think this might be the wrong
approach to fixing the original problem. I think it was probably OK
that the NOT NULL constraint on the child was marked as inherited, but
I think what should have happened is that dropping the PRIMARY KEY
constraint on the parent should have caused the NOT NULL constraint on
the child to have been deleted (in the same way as it would have been,
if it had been a NOT NULL constraint on the parent).

Regards,
Dean




Re: cataloguing NOT NULL constraints

2023-08-04 Thread Alvaro Herrera
On 2023-Jul-28, Alvaro Herrera wrote:

> To avoid that, one option would be to make this NN constraint
> undroppable ...  but I don't see how.  One option might be to add a
> pg_depend row that links the NOT NULL constraint to its PK constraint.
> But this will be a strange case that occurs nowhere else, since other
> NOT NULL constraint don't have such pg_depend rows.  Also, I won't know
> how pg_dump likes this until I implement it.

I've been completing the implementation for this.  It seems to work
reasonably okay; pg_dump requires somewhat strange contortions, but they
are similar to what we do in flagInhTables already, so I don't feel too
bad about that.

What *is* odd and bothersome is that it also causes a problem dropping
the child table.  For example,

CREATE TABLE parent (a int primary key);
CREATE TABLE child () INHERITS (parent);
\d+ child

 Tabla «public.child»
 Columna │  Tipo   │ Ordenamiento │ Nulable  │ Por omisión │ Almacenamiento │ 
Compresión │ Estadísticas │ Descripción 
─┼─┼──┼──┼─┼┼┼──┼─
 a   │ integer │  │ not null │ │ plain  │   
 │  │ 
Not null constraints:
"child_a_not_null" NOT NULL "a"
Hereda: parent
Método de acceso: heap

This is the behavior that I think we wanted to prevent drop of the child
constraint, and it seems okay to me:

=# alter table child drop constraint child_a_not_null;
ERROR:  cannot drop constraint child_a_not_null on table child because 
constraint parent_pkey on table parent requires it
SUGERENCIA:  You can drop constraint parent_pkey on table parent instead.

But the problem is this:

=# drop table child;
ERROR:  cannot drop table child because other objects depend on it
DETALLE:  constraint parent_pkey on table parent depends on table child
SUGERENCIA:  Use DROP ... CASCADE to drop the dependent objects too.


To be clear, what my patch is doing is add one new dependency:

dep │  ref  
 │ deptype 
┼┼─
 type foo   │ table foo 
 │ i
 table foo  │ schema public 
 │ n
 constraint foo_pkey on table foo   │ column a of table foo 
 │ a
 type bar   │ table bar 
 │ i
 table bar  │ schema public 
 │ n
 table bar  │ table foo 
 │ n
 constraint bar_a_not_null on table bar │ column a of table bar 
 │ a
 constraint child_a_not_null on table child │ column a of table child   
 │ a
 constraint child_a_not_null on table child │ constraint parent_pkey on table 
parent │ i

the last row here is what is new.  I'm not sure what's the right fix.
Maybe I need to invert the direction of that dependency.


Even with that fixed, I'd still need to write more code so that ALTER
TABLE INHERIT adds the link (I already patched the DROP INHERIT part).
Not sure what else might I be missing.

Separately, I also noticed that some code that's currently
dropconstraint_internal needs to be moved to DropConstraintById, because
if the PK is dropped for some other reason than ALTER TABLE DROP
CONSTRAINT, some ancillary actions are not taken.

Sigh.

-- 
Álvaro Herrera PostgreSQL Developer  —  https://www.EnterpriseDB.com/
 Are you not unsure you want to delete Firefox?
   [Not unsure] [Not not unsure][Cancel]
   http://smylers.hates-software.com/2008/01/03/566e45b2.html




Re: cataloguing NOT NULL constraints

2023-08-02 Thread Peter Eisentraut

On 24.07.23 12:32, Alvaro Herrera wrote:

However, 11.16 ( as part of 11.12 ), says that DROP NOT NULL causes the indication of
the column as NOT NULL to be removed.  This, to me, says that if you do
have multiple such constraints, you'd better remove them all with that
command.  All in all, I lean towards allowing just one as best as we
can.


Another clue is in 11.15 , which says

1) Let C be the column identified by the  CN in the
containing . If the column descriptor of C
does not contain an indication that C is defined as NOT NULL, then:

[do things]

Otherwise it does nothing.  So there can only be one such constraint per 
table.






Re: cataloguing NOT NULL constraints

2023-07-28 Thread Alvaro Herrera
> > Given the following sequence:
> > 
> > drop table if exists p,c;
> > create table p(a int primary key);
> > create table c() inherits (p);
> > alter table p drop constraint p_pkey;

> > However, c.a remains non-nullable, with a NOT NULL constraint that
> > claims to be inherited:
> > 
> > \d+ c
> > Table "public.c"
> >  Column |  Type   | Collation | Nullable | Default | Storage |
> > Compression | Stats target | Description
> > +-+---+--+-+-+-+--+-
> >  a  | integer |   | not null | | plain   |
> > |  |
> > Not null constraints:
> > "c_a_not_null" NOT NULL "a" (inherited)
> > Inherits: p
> > Access method: heap
> > 
> > That's a problem, because now the NOT NULL constraint on c cannot be
> > dropped (attempting to drop it on c errors out because it thinks it's
> > inherited, but it can't be dropped via p, because p.a is already
> > nullable).

So I implemented a fix for this (namely: fix the inhcount to be 0
initially), and it works well, but it does cause a definitional problem:
any time we create a child table that inherits from another table that
has a primary key, all the columns in the child table will get normal,
visible, droppable NOT NULL constraints.  Thus, pg_dump for example will
output that constraint exactly as if the user had specified it in the
child's CREATE TABLE command.  By itself this doesn't bother me, though
I admit it seems a little odd.

When you restore such a setup from pg_dump, things work perfectly -- I
mean, you don't get a second constraint.  But if you do drop the
constraint, then it will be reinstated by the next pg_dump as if you
hadn't dropped it, by way of it springing to life from the PK.

To avoid that, one option would be to make this NN constraint
undroppable ...  but I don't see how.  One option might be to add a
pg_depend row that links the NOT NULL constraint to its PK constraint.
But this will be a strange case that occurs nowhere else, since other
NOT NULL constraint don't have such pg_depend rows.  Also, I won't know
how pg_dump likes this until I implement it.

-- 
Álvaro Herrera PostgreSQL Developer  —  https://www.EnterpriseDB.com/




Re: cataloguing NOT NULL constraints

2023-07-26 Thread Alvaro Herrera
On 2023-Jul-26, Alvaro Herrera wrote:

> On 2023-Jul-26, Dean Rasheed wrote:
> 
> > The new \d+ output certainly makes testing and reviewing easier,
> > though I do understand people's concerns that this may make the output
> > significantly longer in many real-world cases.
> 
> Yeah, at this point I'm inclined to get the \d+ version committed
> immediately after the main patch, and we can tweak the psql UI after the
> fact -- for instance so that they are only shown in \d++, or some other
> idea we may come across.

(For example, maybe we could add \dtc [PATTERN] or some such, that lists
all the constraints of all kinds in tables matching PATTERN.)

-- 
Álvaro HerreraBreisgau, Deutschland  —  https://www.EnterpriseDB.com/
"If you want to have good ideas, you must have many ideas.  Most of them
will be wrong, and what you have to learn is which ones to throw away."
 (Linus Pauling)




Re: cataloguing NOT NULL constraints

2023-07-26 Thread Alvaro Herrera
Thanks for spending so much time with this patch -- really appreciated.

On 2023-Jul-26, Dean Rasheed wrote:

> On Tue, 25 Jul 2023 at 13:36, Alvaro Herrera  wrote:
> >
> > Okay then, I've made these show up in the footer of \d+.  This is in
> > patch 0003 here.  Please let me know what do you think of the regression
> > changes.
> 
> The new \d+ output certainly makes testing and reviewing easier,
> though I do understand people's concerns that this may make the output
> significantly longer in many real-world cases. I don't think it would
> be a good idea to filter the list in any way though, because I think
> that will only lead to confusion. I think it should be all-or-nothing,
> though I'm not necessarily opposed to using something like \d++ to
> enable it, if that turns out to be the least-bad option.

Yeah, at this point I'm inclined to get the \d+ version committed
immediately after the main patch, and we can tweak the psql UI after the
fact -- for instance so that they are only shown in \d++, or some other
idea we may come across.

> Going back to this example:
> 
> drop table if exists p1, p2, foo;
> create table p1(a int not null check (a > 0));
> create table p2(a int not null check (a > 0));
> create table foo () inherits (p1,p2);

> I remain of the opinion that that should create 2 NOT NULL constraints
> on foo, for consistency with CHECK constraints, and the misleading
> name that results if p1_a_not_null is dropped from p1. That way, the
> names of inherited NOT NULL constraints could be kept in sync, as they
> are for other constraint types, making it easier to keep track of
> where they come from, and it wouldn't be necessary to treat them
> differently (e.g., matching by column number, when dropping NOT NULL
> constraints).

I think having two constraints is more problematic, UI-wise.  Previous
versions of this patchset did it that way, and it's not great: for
example ALTER TABLE ALTER COLUMN DROP NOT NULL fails and tells you to
choose which exact constraint you want to drop and use DROP CONSTRAINT
instead.  And when searching for the not-null constraints for a column,
the code had to consider the case of there being multiple ones, which
led to strange contortions.  Allowing a single one is simpler and covers
all important cases well.

Anyway, you still can't drop the doubly-inherited constraint directly,
because it'll complain that it is an inherited constraint.  So you have
to deinherit first and only then can you drop the constraint.

Now, one possible improvement here would be to ignore the parent
constraint's name, and have 'foo' recompute its own constraint name from
scratch, inheriting the name only if one of the parents had a
manually-specified constraint name (and we would choose the first one,
if there's more than one).  I think complicating things more than that
is unnecessary -- particularly considering that legacy inheritance is,
well, legacy, and I doubt people are relying too much on it.


> Given the following sequence:
> 
> drop table if exists p,c;
> create table p(a int primary key);
> create table c() inherits (p);
> alter table p drop constraint p_pkey;
> 
> p.a ends up being nullable, where previously it would have been left
> non-nullable. That change makes sense, and is presumably one of the
> benefits of tying the nullability of columns to pg_constraint entries.

Right.

> However, c.a remains non-nullable, with a NOT NULL constraint that
> claims to be inherited:
> 
> \d+ c
> Table "public.c"
>  Column |  Type   | Collation | Nullable | Default | Storage |
> Compression | Stats target | Description
> +-+---+--+-+-+-+--+-
>  a  | integer |   | not null | | plain   |
> |  |
> Not null constraints:
> "c_a_not_null" NOT NULL "a" (inherited)
> Inherits: p
> Access method: heap
> 
> That's a problem, because now the NOT NULL constraint on c cannot be
> dropped (attempting to drop it on c errors out because it thinks it's
> inherited, but it can't be dropped via p, because p.a is already
> nullable).

Oh, I think the bug here is just that this constraint should not claim
to be inherited, but standalone.  So you can drop it afterwards; but if
you drop it and end up with NULL values in your PK-labelled column in
the parent table, that's on you.

> I wonder if NOT NULL constraints created as a result of inherited PKs
> should have names based on the PK name (e.g.,
> __not_null), to make it more obvious where they
> came from. That would be more consistent with the way NOT NULL
> constraint names are inherited.

Hmm, interesting idea.  I'll play with it.  (It may quickly lead to
constraint names that are too long, though.)

> Given the following sequence:
> 
> drop table if exists p,c;
> create table p(a int);
> create table c() inherits (p);
> alter table p add primary key (a);
> 
> c.a ends up 

Re: cataloguing NOT NULL constraints

2023-07-26 Thread Dean Rasheed
On Tue, 25 Jul 2023 at 13:36, Alvaro Herrera  wrote:
>
> Okay then, I've made these show up in the footer of \d+.  This is in
> patch 0003 here.  Please let me know what do you think of the regression
> changes.
>

The new \d+ output certainly makes testing and reviewing easier,
though I do understand people's concerns that this may make the output
significantly longer in many real-world cases. I don't think it would
be a good idea to filter the list in any way though, because I think
that will only lead to confusion. I think it should be all-or-nothing,
though I'm not necessarily opposed to using something like \d++ to
enable it, if that turns out to be the least-bad option.

Going back to this example:

drop table if exists p1, p2, foo;
create table p1(a int not null check (a > 0));
create table p2(a int not null check (a > 0));
create table foo () inherits (p1,p2);
\d+ foo

   Table "public.foo"
 Column |  Type   | Collation | Nullable | Default | Storage |
Compression | Stats target | Description
+-+---+--+-+-+-+--+-
 a  | integer |   | not null | | plain   |
|  |
Check constraints:
"p1_a_check" CHECK (a > 0)
"p2_a_check" CHECK (a > 0)
Not null constraints:
"p1_a_not_null" NOT NULL "a" (inherited)
Inherits: p1,
  p2
Access method: heap

I remain of the opinion that that should create 2 NOT NULL constraints
on foo, for consistency with CHECK constraints, and the misleading
name that results if p1_a_not_null is dropped from p1. That way, the
names of inherited NOT NULL constraints could be kept in sync, as they
are for other constraint types, making it easier to keep track of
where they come from, and it wouldn't be necessary to treat them
differently (e.g., matching by column number, when dropping NOT NULL
constraints).

Doing a little more testing, I found some other issues.


Given the following sequence:

drop table if exists p,c;
create table p(a int primary key);
create table c() inherits (p);
alter table p drop constraint p_pkey;

p.a ends up being nullable, where previously it would have been left
non-nullable. That change makes sense, and is presumably one of the
benefits of tying the nullability of columns to pg_constraint entries.
However, c.a remains non-nullable, with a NOT NULL constraint that
claims to be inherited:

\d+ c
Table "public.c"
 Column |  Type   | Collation | Nullable | Default | Storage |
Compression | Stats target | Description
+-+---+--+-+-+-+--+-
 a  | integer |   | not null | | plain   |
|  |
Not null constraints:
"c_a_not_null" NOT NULL "a" (inherited)
Inherits: p
Access method: heap

That's a problem, because now the NOT NULL constraint on c cannot be
dropped (attempting to drop it on c errors out because it thinks it's
inherited, but it can't be dropped via p, because p.a is already
nullable).

I wonder if NOT NULL constraints created as a result of inherited PKs
should have names based on the PK name (e.g.,
__not_null), to make it more obvious where they
came from. That would be more consistent with the way NOT NULL
constraint names are inherited.


Given the following sequence:

drop table if exists p,c;
create table p(a int);
create table c() inherits (p);
alter table p add primary key (a);

c.a ends up non-nullable, but there is no pg_constraint entry
enforcing the constraint:

\d+ c
Table "public.c"
 Column |  Type   | Collation | Nullable | Default | Storage |
Compression | Stats target | Description
+-+---+--+-+-+-+--+-
 a  | integer |   | not null | | plain   |
|  |
Inherits: p
Access method: heap


Given a database containing these 2 tables:

create table p(a int primary key);
create table c() inherits (p);

doing a pg_dump and restore fails to restore the NOT NULL constraint
on c, because all constraints created by the dump are local to p.


That's it for now. I'll try to do more testing later.

Regards,
Dean




Re: cataloguing NOT NULL constraints

2023-07-25 Thread Robert Haas
On Tue, Jul 25, 2023 at 3:07 PM Isaac Morland  wrote:
> OK, I suppose ALTER CONSTRAINT to change the deferrable status and validity 
> (that is why we're doing this, right?) needs the constraint name. But the 
> constraint name is formulaic by default, and my proposal is to suppress it 
> only when it matches the formula, so you could just construct the constraint 
> name using the documented formula if it's not explicitly listed.
>
> I really don’t see it as a good use of space to add n lines to the \d+ 
> display just to confirm that the "not null" designations in the "Nullable" 
> column are implemented by named constraints with the expected names.

Yeah, I mean, I get that. That was my initial concern, too. But I also
think if there's some complicated rule that determines what gets
displayed and what doesn't, nobody's going to remember it, and then
when you don't see something, you're never going to be sure exactly
what's going on. Displaying everything is going to be clunky
especially if, like me, you tend to be careful to mark columns NOT
NULL when they are, but when something goes wrong, the last thing you
want to do is run a \d command and have it show you incomplete
information.

I can't count the number of times that somebody's shown me the output
of a query against pg_locks or pg_stat_activity that had been filtered
to remove irrelevant information and it turned out that the hidden
information was not so irrelevant as the person who wrote the query
thought. It happens all the time. I don't want to create the same kind
of situation here.

-- 
Robert Haas
EDB: http://www.enterprisedb.com




Re: cataloguing NOT NULL constraints

2023-07-25 Thread Isaac Morland
On Tue, 25 Jul 2023 at 14:59, Robert Haas  wrote:

> On Tue, Jul 25, 2023 at 1:33 PM Isaac Morland 
> wrote:
> > My suggestion is for \d+ to show NOT NULL constraints only if there is
> something weird going on (wrong name, duplicate constraints, …). If there
> is nothing weird about the constraint then explicitly listing it provides
> absolutely no information that is not given by "not null" in the "Nullable"
> column. Easier said than done I suppose. I'm just worried about my \d+
> displays becoming less useful.
>
> I mean, the problem is that if you want to ALTER TABLE .. DROP
> CONSTRAINT, you need to know what the valid arguments to that command
> are, and the names of these constraints will be just as valid as the
> names of any other constraints.
>

Can't I just ALTER TABLE … DROP NOT NULL still?

OK, I suppose ALTER CONSTRAINT to change the deferrable status and validity
(that is why we're doing this, right?) needs the constraint name. But the
constraint name is formulaic by default, and my proposal is to suppress it
only when it matches the formula, so you could just construct the
constraint name using the documented formula if it's not explicitly listed.

I really don’t see it as a good use of space to add n lines to the \d+
display just to confirm that the "not null" designations in the "Nullable"
column are implemented by named constraints with the expected names.


Re: cataloguing NOT NULL constraints

2023-07-25 Thread Robert Haas
On Tue, Jul 25, 2023 at 1:33 PM Isaac Morland  wrote:
> My suggestion is for \d+ to show NOT NULL constraints only if there is 
> something weird going on (wrong name, duplicate constraints, …). If there is 
> nothing weird about the constraint then explicitly listing it provides 
> absolutely no information that is not given by "not null" in the "Nullable" 
> column. Easier said than done I suppose. I'm just worried about my \d+ 
> displays becoming less useful.

I mean, the problem is that if you want to ALTER TABLE .. DROP
CONSTRAINT, you need to know what the valid arguments to that command
are, and the names of these constraints will be just as valid as the
names of any other constraints.

-- 
Robert Haas
EDB: http://www.enterprisedb.com




Re: cataloguing NOT NULL constraints

2023-07-25 Thread Isaac Morland
On Tue, 25 Jul 2023 at 12:24, Alvaro Herrera 
wrote:

> On 2023-Jul-25, Isaac Morland wrote:
>
> > I agree. I definitely do *not* want a bunch of NOT NULL constraint names
> > cluttering up displays. Can we legislate that all NOT NULL implementing
> > constraints are named by mashing together the table name, column name,
> and
> > something to identify it as a NOT NULL constraint?
>
> All constraints are named like that already, and NOT NULL constraints
> just inherited the same idea.  The names are __not_null
> for NOT NULL constraints.  pg_dump goes great lengths to avoid printing
> constraint names when they have this pattern.
>

OK, this is helpful. Can \d do the same thing? I use a lot of NOT NULL
constraints and I very seriously do not want \d (including \d+) to have an
extra line for almost every column. It's just noise, and while my screen is
large, it's still not infinite.

I do not want these constraint names cluttering the output either.
> That's why I propose moving them to a new \d++ command, where they will
> only bother you if you absolutely need them.  But so far I have only one
> vote supporting that idea.


My suggestion is for \d+ to show NOT NULL constraints only if there is
something weird going on (wrong name, duplicate constraints, …). If there
is nothing weird about the constraint then explicitly listing it provides
absolutely no information that is not given by "not null" in the "Nullable"
column. Easier said than done I suppose. I'm just worried about my \d+
displays becoming less useful.


Re: cataloguing NOT NULL constraints

2023-07-25 Thread Alvaro Herrera
On 2023-Jul-25, Isaac Morland wrote:

> I agree. I definitely do *not* want a bunch of NOT NULL constraint names
> cluttering up displays. Can we legislate that all NOT NULL implementing
> constraints are named by mashing together the table name, column name, and
> something to identify it as a NOT NULL constraint?

All constraints are named like that already, and NOT NULL constraints
just inherited the same idea.  The names are __not_null
for NOT NULL constraints.  pg_dump goes great lengths to avoid printing
constraint names when they have this pattern.

I do not want these constraint names cluttering the output either.
That's why I propose moving them to a new \d++ command, where they will
only bother you if you absolutely need them.  But so far I have only one
vote supporting that idea.

-- 
Álvaro Herrera   48°01'N 7°57'E  —  https://www.EnterpriseDB.com/




Re: cataloguing NOT NULL constraints

2023-07-25 Thread Isaac Morland
On Tue, 25 Jul 2023 at 11:39, Robert Haas  wrote:

>
> I'm not really thrilled with the idea of every not-null constraint
> having a name, to be honest. Of all the kinds of constraints that we
> have in the system, NOT NULL constraints are probably the ones where
> naming them is least likely to be interesting, because they don't
> really have any interesting properties. A CHECK constraint has an
> expression; a foreign key constraint has columns that it applies to on
> each side plus the identity of the table and opclass information, but
> a NOT NULL constraint seems like it can never have any property other
> than which column. So it sort of seems like a waste to name it. But if
> we want it catalogued then we don't really have an option, so I
> suppose we just have to accept a bit of clutter as the price of doing
> business.
>

I agree. I definitely do *not* want a bunch of NOT NULL constraint names
cluttering up displays. Can we legislate that all NOT NULL implementing
constraints are named by mashing together the table name, column name, and
something to identify it as a NOT NULL constraint? Maybe even something
like pg_not_null_[relname]_[attname] (with some escaping), using the pg_
prefix to make the name reserved similar to schemas and tables? And then
don't show such constraints in \d, not even \d+ - just indicate it in
the Nullable column of the column listing as done now. Show a NOT NULL
constraint if there is something odd about it - for example, if it gets
renamed, or not renamed when the table is renamed.

Sorry for the noise if this has already been decided otherwise.


Re: cataloguing NOT NULL constraints

2023-07-25 Thread Robert Haas
On Tue, Jul 25, 2023 at 8:36 AM Alvaro Herrera  wrote:
> Okay then, I've made these show up in the footer of \d+.  This is in
> patch 0003 here.  Please let me know what do you think of the regression
> changes.

Seems OK.

I'm not really thrilled with the idea of every not-null constraint
having a name, to be honest. Of all the kinds of constraints that we
have in the system, NOT NULL constraints are probably the ones where
naming them is least likely to be interesting, because they don't
really have any interesting properties. A CHECK constraint has an
expression; a foreign key constraint has columns that it applies to on
each side plus the identity of the table and opclass information, but
a NOT NULL constraint seems like it can never have any property other
than which column. So it sort of seems like a waste to name it. But if
we want it catalogued then we don't really have an option, so I
suppose we just have to accept a bit of clutter as the price of doing
business.

-- 
Robert Haas
EDB: http://www.enterprisedb.com




Re: cataloguing NOT NULL constraints

2023-07-24 Thread Robert Haas
On Mon, Jul 24, 2023 at 6:33 AM Alvaro Herrera  wrote:
> That's the first thing I proposed actually.  I got one vote down from
> Robert Haas[1], but while the idea seems to have had support from Justin
> Pryzby (in \dt++) [2] and definitely did from Peter Eisentraut [3], I do
> not like it too much myself, mainly because the partition list has a
> very similar treatment and I find that one an annoyance.

I think I might want to retract my earlier -1 vote. I mean, I agree
with former me that having the \d+ output get a whole lot longer is
not super-appealing. But I also agree with Dean that having this
information available somewhere is probably important, and I also
agree with your point that inventing \d++ for this isn't necessarily a
good idea. I fear that will just result in having to type an extra
plus sign any time you want to see all of the table details, to make
sure that psql knows that you really mean it. So, maybe showing it in
the \d+ output as Dean proposes is the least of evils.

-- 
Robert Haas
EDB: http://www.enterprisedb.com




Re: cataloguing NOT NULL constraints

2023-07-24 Thread Dean Rasheed
On Mon, 24 Jul 2023 at 17:42, Alvaro Herrera  wrote:
>
> On 2023-Jul-24, Dean Rasheed wrote:
>
> > Something else I noticed: the error message from ALTER TABLE ... ADD
> > CONSTRAINT in the case of a duplicate constraint name is not very
> > friendly:
> >
> > ERROR:  duplicate key value violates unique constraint
> > "pg_constraint_conrelid_contypid_conname_index"
> > DETAIL:  Key (conrelid, contypid, conname)=(16540, 0, nn) already exists.
> >

To reproduce this error, try to create 2 constraints with the same
name on different columns:

create table foo(a int, b int);
alter table foo add constraint nn not null a;
alter table foo add constraint nn not null b;

I found another, separate issue:

create table p1(a int not null);
create table p2(a int);
create table foo () inherits (p1,p2);
alter table p2 add not null a;

ERROR:  column "a" of table "foo" is already NOT NULL

whereas doing "alter table p2 alter column a set not null" works OK,
merging the constraints as expected.

Regards,
Dean




Re: cataloguing NOT NULL constraints

2023-07-24 Thread Vik Fearing

On 7/24/23 18:42, Alvaro Herrera wrote:

55490 17devel 3166154=# create table foo (a int constraint nn not null);
CREATE TABLE
55490 17devel 3166154=# alter table foo add constraint nn not null a;
ERROR:  column "a" of table "foo" is already NOT NULL


Surely this should be a WARNING or INFO?  I see no reason to ERROR here.
--
Vik Fearing





Re: cataloguing NOT NULL constraints

2023-07-24 Thread Alvaro Herrera
Hello,

While discussing the matter of multiple constraints with Vik Fearing, I
noticed that we were throwing an unnecessary error if you used

CREATE TABLE foo (a int NOT NULL NOT NULL);

That would die with "redundant NOT NULL declarations", but current
master doesn't do that; and we don't do it for UNIQUE UNIQUE either.
So I modified the patch to make it ignore the dupe and create a single
constraint.  This (and rebasing to current master) are the only changes
in v15.

I have not changed the psql presentation, but I'll do as soon as we have
rough consensus on what to do.  To reiterate, the options are:

1. Don't show the constraint names.  This is what the current patch does

2. Show the constraint name in \d+ in the "nullable" column.
   I did this early on, to much booing.

3. Show the constraint name in \d++ (a new command) tabular output

4. Show the constraint name in the footer of \d+
   I also did this at some point; there are some +1s and some -1s.

5. Show the constraint name in the footer of \d++


Many thanks, Dean, for the discussion so far.

-- 
Álvaro Herrera PostgreSQL Developer  —  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: cataloguing NOT NULL constraints

2023-07-24 Thread Alvaro Herrera
On 2023-Jul-24, Dean Rasheed wrote:

> Something else I noticed: the error message from ALTER TABLE ... ADD
> CONSTRAINT in the case of a duplicate constraint name is not very
> friendly:
> 
> ERROR:  duplicate key value violates unique constraint
> "pg_constraint_conrelid_contypid_conname_index"
> DETAIL:  Key (conrelid, contypid, conname)=(16540, 0, nn) already exists.
> 
> To match the error message for other constraint types, this should be:
> 
> ERROR:  constraint "nn" for relation "foo" already exists

Hmm, how did you get this one?  I can't reproduce it:

55490 17devel 3166154=# create table foo (a int constraint nn not null);
CREATE TABLE
55490 17devel 3166154=# alter table foo add constraint nn not null a;
ERROR:  column "a" of table "foo" is already NOT NULL

55490 17devel 3166154=# drop table foo;
DROP TABLE

55490 17devel 3166154=# create table foo (a int);
CREATE TABLE
Duración: 1,472 ms
55490 17devel 3166154=# alter table foo add constraint nn not null a, add 
constraint nn not null a;
ERROR:  column "a" of table "foo" is already NOT NULL

-- 
Álvaro HerreraBreisgau, Deutschland  —  https://www.EnterpriseDB.com/
"El número de instalaciones de UNIX se ha elevado a 10,
y se espera que este número aumente" (UPM, 1972)




Re: cataloguing NOT NULL constraints

2023-07-24 Thread Alvaro Herrera
On 2023-Jul-24, Dean Rasheed wrote:

> Hmm, I'm not so sure. I think perhaps multiple NOT NULL constraints on
> the same column should just be allowed, otherwise things might get
> confusing. For example:
> 
 create table p1 (a int not null check (a > 0));
 create table p2 (a int not null check (a > 0));
 create table foo () inherits (p1, p2);

Have a look at the conislocal / coninhcount values.  These should
reflect the fact that the constraint has multiple sources; and the
constraint does disappear if you drop it from both sources.

> If I then drop the p1 constraints:
> 
> alter table p1 drop constraint p1_a_check;
> alter table p1 drop constraint p1_a_not_null;
> 
> I end up with column "a" still being not null, and the "p1_a_not_null"
> constraint still being there on foo, which seems even more
> counter-intuitive, because I just dropped that constraint, and it
> really should now be the "p2_a_not_null" constraint that makes "a" not
> null:

I can see that it might make sense to not inherit the constraint name in
some cases.  Perhaps:

1. never inherit a name.  Each table has its own constraint name always
2. only inherit if there's a single parent
3. always inherit the name from the first parent (current implementation)

> So I'd say that ALTER TABLE ... ADD NOT NULL should always add a
> constraint, even if there already is one. For example ALTER TABLE ...
> ADD UNIQUE does nothing to prevent multiple unique constraints on the
> same column(s). It seems pretty dumb, but maybe there is a reason to
> allow it, and it doesn't feel like we should be second-guessing what
> the user wants.

That was my initial implementation but I changed it to allowing a single
constraint because of the way the standard describes SET NOT NULL;
specifically, 11.15  says that "If the
column descriptor of C does not contain an indication that C is defined
as NOT NULL, then:" a constraint is added; otherwise (i.e., such an
indication does exist), nothing happens.

-- 
Álvaro Herrera PostgreSQL Developer  —  https://www.EnterpriseDB.com/
"La virtud es el justo medio entre dos defectos" (Aristóteles)




Re: cataloguing NOT NULL constraints

2023-07-24 Thread Alvaro Herrera
On 2023-Jul-24, Dean Rasheed wrote:

> Hmm, I don't particularly like that approach, because I think it will
> be difficult to cram any additional details into the table, and also I
> don't know whether having multiple not null constraints for a
> particular column can be entirely ruled out.
> 
> I may well be in the minority here, but I think the best way is to
> list them in a separate footer section, in the same way as CHECK
> constraints, allowing other constraint properties to be included. So
> it might look something like:

That's the first thing I proposed actually.  I got one vote down from
Robert Haas[1], but while the idea seems to have had support from Justin
Pryzby (in \dt++) [2] and definitely did from Peter Eisentraut [3], I do
not like it too much myself, mainly because the partition list has a
very similar treatment and I find that one an annoyance.

> and also I don't know whether having multiple not null constraints for
> a particular column can be entirely ruled out.

I had another look at the standard.  In 11.26 () it says that "If [the constraint being removed]
causes some column COL to be known not nullable and no other constraint
causes COL to be known not nullable, then the nullability characteristic
of the column descriptor of COL is changed to possibly nullable".  Which
supports the idea that there might be multiple such constraints.
(However, we could also read this as meaning that the PK could be one
such constraint while NOT NULL is another one.)

However, 11.16 ( as part of 11.12 ), says that DROP NOT NULL causes the indication of
the column as NOT NULL to be removed.  This, to me, says that if you do
have multiple such constraints, you'd better remove them all with that
command.  All in all, I lean towards allowing just one as best as we
can.

[1] 
https://postgr.es/m/ca+tgmobnoxt83y1qesbnvarhfm-flwwkduyiv84e+psaydw...@mail.gmail.com
[2] https://postgr.es/m/20230301223214.GC4268%40telsasoft.com
[3] https://postgr.es/m/1c4f3755-2d10-cae9-647f-91a9f006410e%40enterprisedb.com

-- 
Álvaro HerreraBreisgau, Deutschland  —  https://www.EnterpriseDB.com/
“Cuando no hay humildad las personas se degradan” (A. Christie)




Re: cataloguing NOT NULL constraints

2023-07-24 Thread Dean Rasheed
Something else I noticed: the error message from ALTER TABLE ... ADD
CONSTRAINT in the case of a duplicate constraint name is not very
friendly:

ERROR:  duplicate key value violates unique constraint
"pg_constraint_conrelid_contypid_conname_index"
DETAIL:  Key (conrelid, contypid, conname)=(16540, 0, nn) already exists.

To match the error message for other constraint types, this should be:

ERROR:  constraint "nn" for relation "foo" already exists

Regards,
Dean




Re: cataloguing NOT NULL constraints

2023-07-24 Thread Dean Rasheed
On Thu, 20 Jul 2023 at 16:31, Alvaro Herrera  wrote:
>
> On 2023-Jul-13, Dean Rasheed wrote:
>
> > Something else I noticed is that the result from "ALTER TABLE ...
> > ALTER COLUMN ... DROP NOT NULL" is no longer easily predictable -- if
> > there are multiple NOT NULL constraints on the column, it just drops
> > one (chosen at random?) and leaves the others. I think that it should
> > either drop all the constraints, or throw an error. Either way, I
> > would expect that if DROP NOT NULL succeeds, the result is that the
> > column is nullable.
>
> Hmm, there shouldn't be multiple NOT NULL constraints for the same
> column; if there's one, a further SET NOT NULL should do nothing.  At
> some point the code was creating two constraints, but I realized that
> trying to support multiple constraints caused other problems, and it
> seems to serve no purpose, so I removed it.  Maybe there are ways to end
> up with multiple constraints, but at this stage I would say that those
> are bugs to be fixed, rather than something we want to keep.
>

Hmm, I'm not so sure. I think perhaps multiple NOT NULL constraints on
the same column should just be allowed, otherwise things might get
confusing. For example:

create table p1 (a int not null check (a > 0));
create table p2 (a int not null check (a > 0));
create table foo () inherits (p1, p2);

causes foo to have 2 CHECK constraints, but only 1 NOT NULL constraint:

\d foo
Table "public.foo"
 Column |  Type   | Collation | Nullable | Default
+-+---+--+-
 a  | integer |   | not null |
Check constraints:
"p1_a_check" CHECK (a > 0)
"p2_a_check" CHECK (a > 0)
Inherits: p1,
  p2

select conname from pg_constraint where conrelid = 'foo'::regclass;
conname
---
 p1_a_not_null
 p2_a_check
 p1_a_check
(3 rows)

which I find a little counter-intuitive / inconsistent. If I then drop
the p1 constraints:

alter table p1 drop constraint p1_a_check;
alter table p1 drop constraint p1_a_not_null;

I end up with column "a" still being not null, and the "p1_a_not_null"
constraint still being there on foo, which seems even more
counter-intuitive, because I just dropped that constraint, and it
really should now be the "p2_a_not_null" constraint that makes "a" not
null:

\d foo
Table "public.foo"
 Column |  Type   | Collation | Nullable | Default
+-+---+--+-
 a  | integer |   | not null |
Check constraints:
"p2_a_check" CHECK (a > 0)
Inherits: p1,
  p2

select conname from pg_constraint where conrelid = 'foo'::regclass;
conname
---
 p1_a_not_null
 p2_a_check
(2 rows)

I haven't thought through various other cases in any detail, but I
can't help feeling that it would be simpler and more logical /
consistent to just allow multiple NOT NULL constraints on a column,
rather than trying to enforce a rule that only one is allowed. That
way, I think it would be easier for the user to keep track of why a
column is not null.

So I'd say that ALTER TABLE ... ADD NOT NULL should always add a
constraint, even if there already is one. For example ALTER TABLE ...
ADD UNIQUE does nothing to prevent multiple unique constraints on the
same column(s). It seems pretty dumb, but maybe there is a reason to
allow it, and it doesn't feel like we should be second-guessing what
the user wants.

Regards,
Dean




Re: cataloguing NOT NULL constraints

2023-07-24 Thread Dean Rasheed
On Thu, 20 Jul 2023 at 16:31, Alvaro Herrera  wrote:
>
> On 2023-Jul-13, Dean Rasheed wrote:
>
> > I see that it's already been discussed, but I don't like the fact that
> > there is no way to get hold of the new constraint names in psql. I
> > think for the purposes of dropping named constraints, and also
> > possible future stuff like NOT VALID / DEFERRABLE constraints, having
> > some way to get their names will be important.
>
> Yeah, so there are two proposals:
>
> 1. Have \d+ replace the "not null" literal in the \d+ display with the
> constraint name; if the column is not nullable because of the primary
> key, it says "(primary key)" instead.  There's a patch for this in the
> thread somewhere.
>
> 2. Same, but use \d++ for this purpose
>
> Using ++ would be a novelty in psql, so I'm hesitant to make that an
> integral part of the current proposal.  However, to me (2) seems to most
> comfortable way forward, because while you're right that people do need
> the constraint name from time to time, this is very seldom the case, so
> polluting \d+ might inconvenience people for not much gain.  And people
> didn't like having the constraint name in \d+.
>
> Do you have an opinion on these ideas?
>

Hmm, I don't particularly like that approach, because I think it will
be difficult to cram any additional details into the table, and also I
don't know whether having multiple not null constraints for a
particular column can be entirely ruled out.

I may well be in the minority here, but I think the best way is to
list them in a separate footer section, in the same way as CHECK
constraints, allowing other constraint properties to be included. So
it might look something like:

\d foo
Table "public.foo"
 Column |  Type   | Collation | Nullable | Default
+-+---+--+-
 a  | integer |   | not null |
 b  | integer |   | not null |
 c  | integer |   | not null |
 d  | integer |   | not null |
Indexes:
"foo_pkey" PRIMARY KEY, btree (a, b)
Check constraints:
"foo_a_check" CHECK (a > 0)
"foo_b_check" CHECK (b > 0) NO INHERIT NOT VALID
Not null constraints:
"foo_c_not_null" NOT NULL c
"foo_d_not_null" NOT NULL d NO INHERIT

As for CHECK constraints, the contents of each constraint line would
match the "table_constraint" SQL syntax needed to reconstruct the
constraint. Doing it this way allows for things like NOT VALID and
DEFERRABLE to be added in the future.

I think that's probably too verbose for a plain "\d", but I think it
would be OK with "\d+".

Regards,
Dean




Re: cataloguing NOT NULL constraints

2023-07-13 Thread Dean Rasheed
On Wed, 12 Jul 2023 at 18:11, Alvaro Herrera  wrote:
>
> v13, because a conflict was just committed to alter_table.sql.
>
> Here I also call out the relcache.c change by making it a separate
> commit.  I'm likely to commit it that way, too.  To recap: the change is
> to have a partitioned table's index list include the primary key, even
> when said primary key is marked invalid.  This turns out to be necessary
> for the currently proposed pg_dump strategy to work; if this is not in
> place, attaching the per-partition PK indexes to the parent index fails
> because it sees that the columns are not marked NOT NULL.
>

Hmm, looking at that change, it looks a little ugly. I think someone
reading that code in the future will have no idea why it's including
some invalid indexes, and not others.

> There are no other changes from v12.  One thing I should probably get
> to, is fixing the constraint name comparison code in pg_dump.  Right now
> it's a bit dumb and will get in silly trouble with overlength
> table/column names (nothing that would actually break, just that it will
> emit constraint names when there's no need to.)
>

Yeah, that seems a bit ugly. Presumably, also, if something like a
column rename happens, the constraint name will no longer match.

I see that it's already been discussed, but I don't like the fact that
there is no way to get hold of the new constraint names in psql. I
think for the purposes of dropping named constraints, and also
possible future stuff like NOT VALID / DEFERRABLE constraints, having
some way to get their names will be important.

Something else I noticed is that the result from "ALTER TABLE ...
ALTER COLUMN ... DROP NOT NULL" is no longer easily predictable -- if
there are multiple NOT NULL constraints on the column, it just drops
one (chosen at random?) and leaves the others. I think that it should
either drop all the constraints, or throw an error. Either way, I
would expect that if DROP NOT NULL succeeds, the result is that the
column is nullable.

Regards,
Dean




Re: cataloguing NOT NULL constraints

2023-07-03 Thread Peter Eisentraut

On 30.06.23 13:44, Alvaro Herrera wrote:

OK, so here's a new attempt to get this working correctly.


Attached is a small fixup patch for the documentation.

Furthermore, there are a few outdated comments that are probably left 
over from previous versions of this patch set.



* src/backend/catalog/pg_constraint.c

Outdated comment:

+   /* only tuples for CHECK constraints should be given */
+   Assert(((Form_pg_constraint) GETSTRUCT(constrTup))->contype == 
CONSTRAINT_NOTNULL);



* src/backend/parser/gram.y

Should processCASbits() set >skip_validation, like in the CHECK
case?  _outConstraint() looks at the field, so it seems relevant.


* src/backend/parser/parse_utilcmd.c

The updated comment says

List   *ckconstraints;  /* CHECK and NOT NULL constraints */

but it seems to me that NOT NULL constraints are not actually added
there but in nnconstraints instead.

It would be nice if the field nnconstraints was listed after
ckconstraints consistently throughout the file.  It's a bit random
right now.

This comment is outdated:

+   /*
+* For NOT NULL declarations, we need to mark the column as
+* not nullable, and set things up to have a CHECK 
constraint

+* created.  Also, duplicate NOT NULL declarations are not
+* allowed.
+*/

About this:

case CONSTR_CHECK:
cxt->ckconstraints = lappend(cxt->ckconstraints, 
constraint);

+
+   /*
+* XXX If the user says CHECK (IS NOT NULL), should we turn
+* that into a regular NOT NULL constraint?
+*/
break;

I think this was decided against.

+   /*
+* Copy NOT NULL constraints, too (these do not require any option 
to have

+* been given).
+*/

Shouldn't that be governed by the INCLUDING CONSTRAINTS option?

Btw., there is some asymmetry here between check constraints and
not-null constraints: Check constraints are in the tuple descriptor,
but not-null constraints are not.  Should that be unified?  Or at
least explained?


* src/include/nodes/parsenodes.h

This comment appears to be outdated:

+ * intermixed in tableElts, and constraints and notnullcols are NIL.  After
+ * parse analysis, tableElts contains just ColumnDefs, notnullcols has been
+ * filled with not-nullable column names from various sources, and 
constraints

+ * contains just Constraint nodes (in fact, only CONSTR_CHECK nodes, in the
+ * present implementation).

There is no "notnullcolls".


* src/test/regress/parallel_schedule

This change appears to be correct, but unrelated to this patch, so I
suggest committing this separately.


* src/test/regress/sql/create_table.sql

-SELECT conislocal, coninhcount FROM pg_constraint WHERE conrelid = 
'part_b'::regclass;
+SELECT conislocal, coninhcount FROM pg_constraint WHERE conrelid = 
'part_b'::regclass ORDER BY coninhcount DESC, conname;


Maybe add conname to the select list here as well, for consistency with 
nearby queries.
From c481b58c3e48ff0ab4738e5cf2440d2ad6fc3e47 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut 
Date: Mon, 3 Jul 2023 15:44:59 +0200
Subject: [PATCH] fixup! Add pg_constraint rows for NOT NULL constraints

---
 doc/src/sgml/catalogs.sgml| 2 +-
 doc/src/sgml/ref/alter_table.sgml | 9 -
 2 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 9137b1bc58..36460126f4 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -2552,7 +2552,7 @@ pg_constraint 
Columns
   
c = check constraint,
f = foreign key constraint,
-   n = not null constraint,
+   n = not-null constraint,
p = primary key constraint,
u = unique constraint,
t = constraint trigger,
diff --git a/doc/src/sgml/ref/alter_table.sgml 
b/doc/src/sgml/ref/alter_table.sgml
index 0b65731b1f..2c4138e4e9 100644
--- a/doc/src/sgml/ref/alter_table.sgml
+++ b/doc/src/sgml/ref/alter_table.sgml
@@ -113,13 +113,12 @@
 
 [ CONSTRAINT constraint_name ]
 { CHECK ( expression ) [ NO 
INHERIT ] |
+  NOT NULL column_name [ NO 
INHERIT ] |
   UNIQUE [ NULLS [ NOT ] DISTINCT ] ( column_name [, ... ] ) index_parameters |
   PRIMARY KEY ( column_name [, 
... ] ) index_parameters |
   EXCLUDE [ USING index_method ] 
( exclude_element WITH 
operator [, ... ] ) index_parameters [ WHERE ( predicate ) ] |
   FOREIGN KEY ( column_name [, 
... ] ) REFERENCES reftable [ ( 
refcolumn [, ... ] ) ]
-[ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETE referential_action ] [ ON UPDATE referential_action ] |
-  NOT NULL column_name [ NO 
INHERIT ]
-}
+[ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETE referential_action ] [ ON UPDATE referential_action ] }
 [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
 
 and table_constraint_using_index is:
@@ -1765,7 +1764,7 @@ Examples
   Compatibility
 

Re: cataloguing NOT NULL constraints

2023-07-03 Thread Alvaro Herrera
On 2023-Jun-30, Andres Freund wrote:

> On 2023-06-30 13:44:03 +0200, Alvaro Herrera wrote:
> 
> > The main novelty in this version of the patch, is that we now emit
> > "throwaway" NOT NULL constraints when a column is part of the primary
> > key.  Then, after the PK is created, we run a DROP for that constraint.
> > That lets us create the PK without having to scan the table during
> > pg_upgrade.
> 
> Have you considered extending the DDL statement for this purpose? We have
>   ALTER TABLE ... ADD CONSTRAINT ... PRIMARY KEY USING INDEX ...;
> we could just do something similar for the NOT NULL constraint?  Which would
> then delete the separate constraint NOT NULL constraint.

Hmm, I hadn't.  I think if we have to explicitly list the constraint
that we want dropped, then it's pretty much the same than as if we used
a comma-separated list of subcommands, like 

ALTER TABLE ... ADD CONSTRAINT .. PRIMARY KEY (a,b),
   DROP CONSTRAINT pgdump_throwaway_notnull_0,
   DROP CONSTRAINT pgdump_throwaway_notnull_1;

However, I think it would be ideal if we *don't* have to specify the
list of constraints: we would do this on any ALTER TABLE .. ADD
CONSTRAINT PRIMARY KEY, without having any additional clause.

But how to distinguish which NOT NULL markings to drop?  Maybe we would
have to specify a flag at NOT NULL constraint creation time.  So pg_dump
would emit something like

CREATE TABLE foo (a int CONSTRAINT NOT NULL THROWAWAY);
... (much later) ...
ALTER TABLE foo ADD CONSTRAINT .. PRIMARY KEY;

and by the time this second command is run, those throwaway constraints
are removed.  The problems now are 1) how to make this CREATE statement
more SQL-conformant (answer: make pg_dump emit a separate ALTER TABLE
command for the constraint addition; it already knows how to do this, so
it'd be very little code); but also 2) where to store the flag
server-side flag that says this constraint has this property.  I think
it'd have to be a new pg_constraint column, and I don't like to add one
for such a minor issue.

On 2023-Jun-30, Alvaro Herrera wrote:

> Scanning this thread, I think I left one reported issue unfixed related
> to tables created LIKE others.  I'll give it a look later.  Other than
> that I think all bases are covered, but I intend to leave the patch open
> until near the end of the CF, in case someone wants to play with it.

So it was [1] that I meant, where this example was provided:

# create table t1 (c int primary key null unique);  

  
# create table t2 (like t1);

  
# alter table t2 alter c drop not null; 

  
ERROR:  no NOT NULL constraint found to drop

The problem here is that because we didn't give INCLUDING INDEXES in the
LIKE clause, we end up with a column marked NOT NULL for which we have
no pg_constraint row.  Okay, I thought, we can just make sure *not* to
mark that case as not null; that works fine and looks reasonable.
However, it breaks the following use case, which is already in use in
the regression tests and possibly by users:

 CREATE TABLE pk (a int PRIMARY KEY) PARTITION BY RANGE (a);
 CREATE TABLE pk4 (LIKE pk);
 ALTER TABLE pk ATTACH PARTITION pk4 FOR VALUES FROM (3000) TO (4000);
+ERROR:  column "a" in child table must be marked NOT NULL

The problem here is that we were assuming, by the time the third command
is run, that the column had been marked NOT NULL by the second command.
So my solution above is simply not acceptable.  What we must do, in
order to handle this backward-compatibly, is to ensure that a column
part of a PK automatically gets a NOT NULL constraint for all the PK
columns, for the case where INCLUDING INDEXES is not given.  This is the
same we do for regular INHERITS children and PKs.

I'll go write this code now; should be simple enough.

[1] 
https://postgr.es/m/CAMbWs48astPDb3K+L89wb8Yju0jM_Czm8svmU=uzd+wm61c...@mail.gmail.com

-- 
Álvaro Herrera PostgreSQL Developer  —  https://www.EnterpriseDB.com/
 It does it in a really, really complicated way
 why does it need to be complicated?
 Because it's MakeMaker.




Re: cataloguing NOT NULL constraints

2023-06-30 Thread Andres Freund
Hi,

On 2023-06-30 13:44:03 +0200, Alvaro Herrera wrote:
> OK, so here's a new attempt to get this working correctly.

Thanks for continuing to work on this!


> The main novelty in this version of the patch, is that we now emit
> "throwaway" NOT NULL constraints when a column is part of the primary
> key.  Then, after the PK is created, we run a DROP for that constraint.
> That lets us create the PK without having to scan the table during
> pg_upgrade.

Have you considered extending the DDL statement for this purpose? We have
  ALTER TABLE ... ADD CONSTRAINT ... PRIMARY KEY USING INDEX ...;
we could just do something similar for the NOT NULL constraint?  Which would
then delete the separate constraint NOT NULL constraint.

Greetings,

Andres Freund




Re: cataloguing NOT NULL constraints

2023-04-09 Thread Tom Lane
Alvaro Herrera  writes:
>> I'm inclined to think that this idea of suppressing the implied
>> NOT NULL from PRIMARY KEY is a nonstarter and we should just
>> go ahead and make such a constraint.  Another idea could be for
>> pg_dump to emit the NOT NULL, load data, do the ALTER ADD PRIMARY
>> KEY, and then ALTER DROP NOT NULL.

> I like that second idea, yeah.  It might be tough to make it work, but
> I'll try.

Yeah, I've been thinking more about it, and this might also yield a
workable solution for the TestUpgrade breakage.  The idea would be,
roughly, for pg_dump to emit NOT NULL column decoration in all the
same places it does now, and then to drop it again immediately after
doing ADD PRIMARY KEY if it judges that there was no other reason
to have it.  This gets rid of the inconsistency for --binary-upgrade
which I think is what is causing the breakage.

I also ran into something else I didn't much care for:

regression=# create table foo(f1 int primary key, f2 int);
CREATE TABLE
regression=# create table foochild() inherits(foo);
CREATE TABLE
regression=# alter table only foo alter column f2 set not null;
ERROR:  cannot add constraint only to table with inheritance children
HINT:  Do not specify the ONLY keyword.

Previous versions accepted this case, and I don't really see why
we can't do so with this new implementation -- isn't this exactly
what pg_constraint.connoinherit was invented to represent?  Moreover,
existing pg_dump files can contain precisely this construct, so
blowing it off isn't going to be zero-cost.

regards, tom lane




Re: cataloguing NOT NULL constraints

2023-04-09 Thread Alvaro Herrera
On 2023-Apr-09, Tom Lane wrote:

> In the new dispensation, pg_dump omits the NOT NULL clauses.
> Great, you say, that makes the output more like what the user wrote.
> I'm not so sure.  This means that the ALTER TABLE will be compelled
> to perform a full-table scan to verify that there are no nulls in the
> already-loaded data before it can add the missing NOT NULL constraint.

Yeah, I agree that this unintended consequence isn't very palatable.  I
think the other pg_upgrade problem is easily fixed (haven't tried yet),
but having to rethink the pg_dump representation would likely take
longer than we'd like.

> I'm inclined to think that this idea of suppressing the implied
> NOT NULL from PRIMARY KEY is a nonstarter and we should just
> go ahead and make such a constraint.  Another idea could be for
> pg_dump to emit the NOT NULL, load data, do the ALTER ADD PRIMARY
> KEY, and then ALTER DROP NOT NULL.

I like that second idea, yeah.  It might be tough to make it work, but
I'll try.

> In any case, I wonder whether that's the sort of redesign we should
> be doing post-feature-freeze.  It might be best to revert and try
> again in v17.

Yeah, sounds like reverting for now and retrying in v17 with the
discussed changes might be better.

-- 
Álvaro Herrera PostgreSQL Developer  —  https://www.EnterpriseDB.com/
"La espina, desde que nace, ya pincha" (Proverbio africano)




Re: cataloguing NOT NULL constraints

2023-04-09 Thread Tom Lane
Andres Freund  writes:
> I think
> https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=drongo=2023-04-07%2021%3A16%3A04
> might point out a problem with the pg_dump or pg_upgrade backward compat
> paths:

Yeah, this patch has broken every single upgrade-from-back-branch test.

I think there's a second problem, though: even without considering
back branches, this has changed pg_dump output in a way that
I fear is unacceptable.  Consider for instance this table definition
(from rules.sql):

create table rule_and_refint_t1 (
id1a integer,
id1b integer,
primary key (id1a, id1b)
);

This used to be dumped as

CREATE TABLE public.rule_and_refint_t1 (
id1a integer NOT NULL,
id1b integer NOT NULL
);
...
... load data ...
...
ALTER TABLE ONLY public.rule_and_refint_t1
ADD CONSTRAINT rule_and_refint_t1_pkey PRIMARY KEY (id1a, id1b);

In the new dispensation, pg_dump omits the NOT NULL clauses.
Great, you say, that makes the output more like what the user wrote.
I'm not so sure.  This means that the ALTER TABLE will be compelled
to perform a full-table scan to verify that there are no nulls in the
already-loaded data before it can add the missing NOT NULL constraint.
The old dump output was carefully designed to avoid the need for that
scan.  Admittedly, we have to do a scan anyway to build the index,
so this is strictly less than a 2X penalty on the ALTER, but is
that acceptable?  It might be all right in the context of regular
dump/restore, where we're surely doing a lot of per-row work anyway
to load the data and make the index.  In the context of pg_upgrade,
though, it seems absolutely disastrous: there will now be a per-row
cost where there was none before, and that is surely a deal-breaker.

BTW, I note from testing that the NOT NULL clauses *are* still
emitted in at least some cases when doing --binary-upgrade from an old
version.  (This may be directly related to the buildfarm failures,
not sure.)  That's no solution though, because now what you get in
pg_constraint will differ depending on which way you upgraded,
which seems unacceptable too.

I'm inclined to think that this idea of suppressing the implied
NOT NULL from PRIMARY KEY is a nonstarter and we should just
go ahead and make such a constraint.  Another idea could be for
pg_dump to emit the NOT NULL, load data, do the ALTER ADD PRIMARY
KEY, and then ALTER DROP NOT NULL.

In any case, I wonder whether that's the sort of redesign we should
be doing post-feature-freeze.  It might be best to revert and try
again in v17.

regards, tom lane




Re: cataloguing NOT NULL constraints

2023-04-07 Thread Andres Freund
Hi,

On 2023-04-07 17:19:42 -0700, Andres Freund wrote:
> I think
> https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=drongo=2023-04-07%2021%3A16%3A04
> might point out a problem with the pg_dump or pg_upgrade backward compat
> paths:
> 
> --- C:\\prog\\bf/root/upgrade.drongo/HEAD/origin-REL9_5_STABLE.sql.fixed  
> 2023-04-07 23:51:27.641328600 +
> +++ 
> C:\\prog\\bf/root/upgrade.drongo/HEAD/converted-REL9_5_STABLE-to-HEAD.sql.fixed
>2023-04-07 23:51:27.672571900 +
> @@ -416,9 +416,9 @@
>  -- Name: entry; Type: TABLE; Schema: public; Owner: buildfarm
>  --
>  CREATE TABLE public.entry (
> -accession text,
> -eid integer,
> -txid smallint
> +accession text NOT NULL,
> +eid integer NOT NULL,
> +txid smallint NOT NULL
>  );
>  ALTER TABLE public.entry OWNER TO buildfarm;
>  --
> 
> Looks like we're making up NOT NULL constraints when migrating from 9.5, for
> some reason?

My compiler complains:

../../../../home/andres/src/postgresql/src/backend/catalog/heap.c: In function 
‘AddRelationNotNullConstraints’:
../../../../home/andres/src/postgresql/src/backend/catalog/heap.c:2829:37: 
warning: ‘conname’ may be used uninitialized [-Wmaybe-uninitialized]
 2829 | if (strcmp(lfirst(lc2), conname) == 0)
  | ^~~~
../../../../home/andres/src/postgresql/src/backend/catalog/heap.c:2802:29: 
note: ‘conname’ was declared here
 2802 | char   *conname;
  | ^~~

I think the compiler may be right - I think the first use of conname might
have been intended as constr->conname?

Greetings,

Andres Freund




Re: cataloguing NOT NULL constraints

2023-04-07 Thread Andres Freund
Hi,

On 2023-04-07 18:26:28 -0400, Tom Lane wrote:
> Andres Freund  writes:
> > On 2023-04-07 17:46:33 -0400, Tom Lane wrote:
> >> After quickly eyeing the diffs, I'm just going to take the new output
> >> as good.  I'm not surprised that there are additional output messages
> >> given the additional catalog entries this made.  I *am* a bit surprised
> >> that some messages seem to have disappeared --- are there places where
> >> this resulted in fewer catalog accesses than before?  Nonetheless,
> >> there's no good reason to assume this test is exposing any bugs.
> 
> > I wonder if the issue is that the new paths miss a hook invocation.
> 
> Perhaps.  I'm content to silence the buildfarm for today; we can
> investigate more closely later.

Makes sense.

I think
https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=drongo=2023-04-07%2021%3A16%3A04
might point out a problem with the pg_dump or pg_upgrade backward compat
paths:

--- C:\\prog\\bf/root/upgrade.drongo/HEAD/origin-REL9_5_STABLE.sql.fixed
2023-04-07 23:51:27.641328600 +
+++ 
C:\\prog\\bf/root/upgrade.drongo/HEAD/converted-REL9_5_STABLE-to-HEAD.sql.fixed 
2023-04-07 23:51:27.672571900 +
@@ -416,9 +416,9 @@
 -- Name: entry; Type: TABLE; Schema: public; Owner: buildfarm
 --
 CREATE TABLE public.entry (
-accession text,
-eid integer,
-txid smallint
+accession text NOT NULL,
+eid integer NOT NULL,
+txid smallint NOT NULL
 );
 ALTER TABLE public.entry OWNER TO buildfarm;
 --

Looks like we're making up NOT NULL constraints when migrating from 9.5, for
some reason?

Greetings,

Andres Freund




Re: cataloguing NOT NULL constraints

2023-04-07 Thread Tom Lane
... BTW, shouldn't
https://commitfest.postgresql.org/42/3869/
now get closed as committed?

regards, tom lane




Re: cataloguing NOT NULL constraints

2023-04-07 Thread Tom Lane
Andres Freund  writes:
> On 2023-04-07 17:46:33 -0400, Tom Lane wrote:
>> After quickly eyeing the diffs, I'm just going to take the new output
>> as good.  I'm not surprised that there are additional output messages
>> given the additional catalog entries this made.  I *am* a bit surprised
>> that some messages seem to have disappeared --- are there places where
>> this resulted in fewer catalog accesses than before?  Nonetheless,
>> there's no good reason to assume this test is exposing any bugs.

> I wonder if the issue is that the new paths miss a hook invocation.

Perhaps.  I'm content to silence the buildfarm for today; we can
investigate more closely later.

regards, tom lane




Re: cataloguing NOT NULL constraints

2023-04-07 Thread Andres Freund
Hi,

On 2023-04-07 17:46:33 -0400, Tom Lane wrote:
> Andres Freund  writes:
> > On 2023-04-07 23:11:55 +0200, Alvaro Herrera wrote:
> >> Ah, cool, no worries.  I would have stopped indeed, but I had to stay
> >> around in case of any test failures.
> 
> > Looks like there's work for you if you want ;)
> > https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=rhinoceros=2023-04-07%2018%3A52%3A13
> 
> > But IMO fixing sepgsql can easily wait till tomorrow.
> 
> I can deal with that one -- it's a bit annoying to work with sepgsql
> if you're not on a Red Hat platform.

Indeed. I tried to get them running a while back, to enable the tests with
meson, without lot of success. Then I realized that they're also not wired up
in make... ;)


> After quickly eyeing the diffs, I'm just going to take the new output
> as good.  I'm not surprised that there are additional output messages
> given the additional catalog entries this made.  I *am* a bit surprised
> that some messages seem to have disappeared --- are there places where
> this resulted in fewer catalog accesses than before?  Nonetheless,
> there's no good reason to assume this test is exposing any bugs.

I wonder if the issue is that the new paths miss a hook invocation.

@@ -160,11 +160,7 @@
 ALTER TABLE regtest_table ALTER b SET DEFAULT 'XYZ';-- not supported yet
 ALTER TABLE regtest_table ALTER b DROP DEFAULT; -- not supported yet
 ALTER TABLE regtest_table ALTER b SET NOT NULL;
-LOG:  SELinux: allowed { setattr } 
scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 
tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column 
name="regtest_schema_2.regtest_table.b" permissive=0
-LOG:  SELinux: allowed { setattr } 
scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 
tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column 
name="regtest_schema.regtest_table_2.b" permissive=0
 ALTER TABLE regtest_table ALTER b DROP NOT NULL;
-LOG:  SELinux: allowed { setattr } 
scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 
tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column 
name="regtest_schema_2.regtest_table.b" permissive=0
-LOG:  SELinux: allowed { setattr } 
scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 
tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column 
name="regtest_schema.regtest_table_2.b" permissive=0
 ALTER TABLE regtest_table ALTER b SET STATISTICS -1;
 LOG:  SELinux: allowed { setattr } 
scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 
tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column 
name="regtest_schema_2.regtest_table.b" permissive=0
 LOG:  SELinux: allowed { setattr } 
scontext=unconfined_u:unconfined_r:sepgsql_regtest_superuser_t:s0 
tcontext=unconfined_u:object_r:sepgsql_table_t:s0 tclass=db_column 
name="regtest_schema.regtest_table_2.b" permissive=0

The 'not supported yet' cases don't emit messages. Previously SET NOT NULL
wasn't among that set, but seemingly it now is.

Greetings,

Andres Freund




Re: cataloguing NOT NULL constraints

2023-04-07 Thread Tom Lane
Andres Freund  writes:
> On 2023-04-07 23:11:55 +0200, Alvaro Herrera wrote:
>> Ah, cool, no worries.  I would have stopped indeed, but I had to stay
>> around in case of any test failures.

> Looks like there's work for you if you want ;)
> https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=rhinoceros=2023-04-07%2018%3A52%3A13

> But IMO fixing sepgsql can easily wait till tomorrow.

I can deal with that one -- it's a bit annoying to work with sepgsql
if you're not on a Red Hat platform.

After quickly eyeing the diffs, I'm just going to take the new output
as good.  I'm not surprised that there are additional output messages
given the additional catalog entries this made.  I *am* a bit surprised
that some messages seem to have disappeared --- are there places where
this resulted in fewer catalog accesses than before?  Nonetheless,
there's no good reason to assume this test is exposing any bugs.

regards, tom lane




Re: cataloguing NOT NULL constraints

2023-04-07 Thread Andres Freund
Hi,

On 2023-04-07 23:11:55 +0200, Alvaro Herrera wrote:
> On 2023-Apr-07, Andres Freund wrote:
> 
> > I just pushed a fix - sorry, I thought you might have stopped working for 
> > the
> > day and CI finished with the modification a few seconds before your email
> > arrived...
> 
> Ah, cool, no worries.  I would have stopped indeed, but I had to stay
> around in case of any test failures.

Looks like there's work for you if you want ;)
https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=rhinoceros=2023-04-07%2018%3A52%3A13

But IMO fixing sepgsql can easily wait till tomorrow.

Greetings,

Andres Freund




Re: cataloguing NOT NULL constraints

2023-04-07 Thread Alvaro Herrera
On 2023-Apr-07, Andres Freund wrote:

> I just pushed a fix - sorry, I thought you might have stopped working for the
> day and CI finished with the modification a few seconds before your email
> arrived...

Ah, cool, no worries.  I would have stopped indeed, but I had to stay
around in case of any test failures.

-- 
Álvaro Herrera   48°01'N 7°57'E  —  https://www.EnterpriseDB.com/
"No me acuerdo, pero no es cierto.  No es cierto, y si fuera cierto,
 no me acuerdo." (Augusto Pinochet a una corte de justicia)




Re: cataloguing NOT NULL constraints

2023-04-07 Thread Andres Freund
Hi,

On 2023-04-07 23:00:01 +0200, Alvaro Herrera wrote:
> On 2023-Apr-07, Andres Freund wrote:
> 
> > src/test/regress/sql/triggers.sql
> > 2127:create table child partition of parent for values in ('AAA');
> > 2266:create table child () inherits (parent);
> > 2759:create table child () inherits (parent);
> > 
> > The inherit.sql part is new.
> 
> Yeah.
> 
> > I'll see how hard it is to fix.
> 
> Running the tests for it now -- it's a short fix.

I just pushed a fix - sorry, I thought you might have stopped working for the
day and CI finished with the modification a few seconds before your email
arrived...

Greetings,

Andres Freund




Re: cataloguing NOT NULL constraints

2023-04-07 Thread Alvaro Herrera
On 2023-Apr-07, Andres Freund wrote:

> src/test/regress/sql/triggers.sql
> 2127:create table child partition of parent for values in ('AAA');
> 2266:create table child () inherits (parent);
> 2759:create table child () inherits (parent);
> 
> The inherit.sql part is new.

Yeah.

> I'll see how hard it is to fix.

Running the tests for it now -- it's a short fix.

-- 
Álvaro HerreraBreisgau, Deutschland  —  https://www.EnterpriseDB.com/
"Learn about compilers. Then everything looks like either a compiler or
a database, and now you have two problems but one of them is fun."
https://twitter.com/thingskatedid/status/1456027786158776329




Re: cataloguing NOT NULL constraints

2023-04-07 Thread Andres Freund
Hi,

On 2023-04-07 13:38:43 -0700, Andres Freund wrote:
> I suspect there's a naming conflict between tests in different groups.

Yep:

test: create_aggregate create_function_sql create_cast constraints triggers 
select inherit typed_table vacuum drop_if_exists updatable_views roleattributes 
create_am hash_func errors infinite_recurse

src/test/regress/sql/inherit.sql
851:create table child(f1 int not null, f2 text not null) 
inherits(inh_parent_1, inh_parent_2);

src/test/regress/sql/triggers.sql
2127:create table child partition of parent for values in ('AAA');
2266:create table child () inherits (parent);
2759:create table child () inherits (parent);

The inherit.sql part is new.

I'll see how hard it is to fix.

Greetings,

Andres Freund




Re: cataloguing NOT NULL constraints

2023-04-07 Thread Andres Freund
Hi,

I think there's some test instability:

Fail:
https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=parula=2023-04-07%2018%3A43%3A02
Subsequent success, without relevant changes:
https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=parula=2023-04-07%2020%3A22%3A01
Followed by a failure:
https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=parula=2023-04-07%2020%3A31%3A02

Similar failures on other animals:
https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=komodoensis=2023-04-07%2020%3A27%3A43
https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=siskin=2023-04-07%2020%3A09%3A25

There's also as second type of failure:
https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=dragonet=2023-04-07%2020%3A23%3A35
..

I suspect there's a naming conflict between tests in different groups.

Greetings,

Andres Freund




  1   2   >