On 2024-May-07, Kyotaro Horiguchi wrote:

> Hello,
> 
> At Wed, 1 May 2024 19:49:35 +0200, Alvaro Herrera <alvhe...@alvh.no-ip.org> 
> 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: Alvaro Herrera <alvhe...@alvh.no-ip.org>
Date: Wed, 8 May 2024 11:15:53 +0200
Subject: [PATCH 1/6] Fix typos in error messages

Reported by Kyotaro Horiguchi
---
 src/backend/catalog/pg_constraint.c   | 2 +-
 src/backend/commands/tablecmds.c      | 3 +--
 src/test/regress/expected/inherit.out | 4 ++--
 3 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 6b8496e085..12a73d5a30 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -758,7 +758,7 @@ AdjustNotNullInheritance1(Oid relid, AttrNumber attnum, int count,
 			if (!allow_noinherit_change)
 				ereport(ERROR,
 						errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
-						errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" in relation \"%s\"",
+						errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on relation \"%s\"",
 							   NameStr(conform->conname), get_rel_name(relid)));
 
 			conform->connoinherit = false;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 5bf5e69c5b..925978c35b 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -7905,11 +7905,10 @@ ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
 		if (conForm->connoinherit && recurse)
 			ereport(ERROR,
 					errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-					errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" in relation \"%s\"",
+					errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on relation \"%s\"",
 						   NameStr(conForm->conname),
 						   RelationGetRelationName(rel)));
 
-
 		/*
 		 * If we find an appropriate constraint, we're almost done, but just
 		 * need to change some properties on it: if we're recursing, increment
diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out
index eaa65049c7..a621db0aa3 100644
--- a/src/test/regress/expected/inherit.out
+++ b/src/test/regress/expected/inherit.out
@@ -2280,9 +2280,9 @@ drop table inh_nn_parent, inh_nn_child, inh_nn_child2;
 CREATE TABLE inh_nn_parent (a int, NOT NULL a NO INHERIT);
 CREATE TABLE inh_nn_child() INHERITS (inh_nn_parent);
 ALTER TABLE inh_nn_parent ADD CONSTRAINT nna NOT NULL a;
-ERROR:  cannot change NO INHERIT status of NOT NULL constraint "inh_nn_parent_a_not_null" in relation "inh_nn_parent"
+ERROR:  cannot change NO INHERIT status of NOT NULL constraint "inh_nn_parent_a_not_null" on relation "inh_nn_parent"
 ALTER TABLE inh_nn_parent ALTER a SET NOT NULL;
-ERROR:  cannot change NO INHERIT status of NOT NULL constraint "inh_nn_parent_a_not_null" in relation "inh_nn_parent"
+ERROR:  cannot change NO INHERIT status of NOT NULL constraint "inh_nn_parent_a_not_null" on relation "inh_nn_parent"
 DROP TABLE inh_nn_parent cascade;
 NOTICE:  drop cascades to table inh_nn_child
 -- Adding a PK at the top level of a hierarchy should cause all descendants
-- 
2.39.2

>From e6b5187a616bf3a37df7fd39cb71c710950bb826 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvhe...@alvh.no-ip.org>
Date: Wed, 8 May 2024 13:27:27 +0200
Subject: [PATCH 2/6] Mechanical move of not-null code out of getTableAttrs

---
 src/bin/pg_dump/pg_dump.c | 314 ++++++++++++++++++++------------------
 1 file changed, 166 insertions(+), 148 deletions(-)

diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 379debac24..1d8b137814 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -292,6 +292,8 @@ static void getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, c
 static void makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo);
 static void buildMatViewRefreshDependencies(Archive *fout);
 static void getTableDataFKConstraints(void);
+static void determineNotNullFlags(Archive *fout, PGresult *res, int r,
+								  TableInfo *tbinfo, int j, int *notnullcount);
 static char *format_function_arguments(const FuncInfo *finfo, const char *funcargs,
 									   bool is_agg);
 static char *format_function_signature(Archive *fout,
@@ -8702,10 +8704,6 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
 	int			i_attlen;
 	int			i_attalign;
 	int			i_attislocal;
-	int			i_notnull_name;
-	int			i_notnull_noinherit;
-	int			i_notnull_is_pk;
-	int			i_notnull_inh;
 	int			i_attoptions;
 	int			i_attcollation;
 	int			i_attcompression;
@@ -8900,10 +8898,6 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
 	i_attlen = PQfnumber(res, "attlen");
 	i_attalign = PQfnumber(res, "attalign");
 	i_attislocal = PQfnumber(res, "attislocal");
-	i_notnull_name = PQfnumber(res, "notnull_name");
-	i_notnull_noinherit = PQfnumber(res, "notnull_noinherit");
-	i_notnull_is_pk = PQfnumber(res, "notnull_is_pk");
-	i_notnull_inh = PQfnumber(res, "notnull_inh");
 	i_attoptions = PQfnumber(res, "attoptions");
 	i_attcollation = PQfnumber(res, "attcollation");
 	i_attcompression = PQfnumber(res, "attcompression");
@@ -8980,10 +8974,6 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
 
 		for (int j = 0; j < numatts; j++, r++)
 		{
-			bool		use_named_notnull = false;
-			bool		use_unnamed_notnull = false;
-			bool		use_throwaway_notnull = false;
-
 			if (j + 1 != atoi(PQgetvalue(res, r, i_attnum)))
 				pg_fatal("invalid column numbering in table \"%s\"",
 						 tbinfo->dobj.name);
@@ -9003,142 +8993,9 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
 			tbinfo->attalign[j] = *(PQgetvalue(res, r, i_attalign));
 			tbinfo->attislocal[j] = (PQgetvalue(res, r, i_attislocal)[0] == 't');
 
-			/*
-			 * Not-null constraints require a jumping through a few hoops.
-			 * First, if the user has specified a constraint name that's not
-			 * the system-assigned default name, then we need to preserve
-			 * that. But if they haven't, then we don't want to use the
-			 * verbose syntax in the dump output. (Also, in versions prior to
-			 * 17, there was no constraint name at all.)
-			 *
-			 * (XXX Comparing the name this way to a supposed default name is
-			 * a bit of a hack, but it beats having to store a boolean flag in
-			 * pg_constraint just for this, or having to compute the knowledge
-			 * at pg_dump time from the server.)
-			 *
-			 * We also need to know if a column is part of the primary key. In
-			 * that case, we want to mark the column as not-null at table
-			 * creation time, so that the table doesn't have to be scanned to
-			 * check for nulls when the PK is created afterwards; this is
-			 * especially critical during pg_upgrade (where the data would not
-			 * be scanned at all otherwise.)  If the column is part of the PK
-			 * and does not have any other not-null constraint, then we
-			 * fabricate a throwaway constraint name that we later use to
-			 * remove the constraint after the PK has been created.
-			 *
-			 * For inheritance child tables, we don't want to print not-null
-			 * when the constraint was defined at the parent level instead of
-			 * locally.
-			 */
-
-			/*
-			 * We use notnull_inh to suppress unwanted not-null constraints in
-			 * inheritance children, when said constraints come from the
-			 * parent(s).
-			 */
-			tbinfo->notnull_inh[j] = PQgetvalue(res, r, i_notnull_inh)[0] == 't';
-
-			if (fout->remoteVersion < 170000)
-			{
-				if (!PQgetisnull(res, r, i_notnull_name) &&
-					dopt->binary_upgrade &&
-					!tbinfo->ispartition &&
-					tbinfo->notnull_inh[j])
-				{
-					use_named_notnull = true;
-					/* XXX should match ChooseConstraintName better */
-					tbinfo->notnull_constrs[j] =
-						psprintf("%s_%s_not_null", tbinfo->dobj.name,
-								 tbinfo->attnames[j]);
-				}
-				else if (PQgetvalue(res, r, i_notnull_is_pk)[0] == 't')
-				{
-					/*
-					 * We want this flag to be set for columns of a primary
-					 * key in which data is going to be loaded by the dump we
-					 * produce; thus a partitioned table doesn't need it.
-					 */
-					if (tbinfo->relkind != RELKIND_PARTITIONED_TABLE)
-						use_throwaway_notnull = true;
-				}
-				else if (!PQgetisnull(res, r, i_notnull_name))
-					use_unnamed_notnull = true;
-			}
-			else
-			{
-				if (!PQgetisnull(res, r, i_notnull_name))
-				{
-					/*
-					 * In binary upgrade of inheritance child tables, must
-					 * have a constraint name that we can UPDATE later.
-					 */
-					if (dopt->binary_upgrade &&
-						!tbinfo->ispartition &&
-						tbinfo->notnull_inh[j])
-					{
-						use_named_notnull = true;
-						tbinfo->notnull_constrs[j] =
-							pstrdup(PQgetvalue(res, r, i_notnull_name));
-
-					}
-					else
-					{
-						char	   *default_name;
-
-						/* XXX should match ChooseConstraintName better */
-						default_name = psprintf("%s_%s_not_null", tbinfo->dobj.name,
-												tbinfo->attnames[j]);
-						if (strcmp(default_name,
-								   PQgetvalue(res, r, i_notnull_name)) == 0)
-							use_unnamed_notnull = true;
-						else
-						{
-							use_named_notnull = true;
-							tbinfo->notnull_constrs[j] =
-								pstrdup(PQgetvalue(res, r, i_notnull_name));
-						}
-					}
-				}
-				else if (PQgetvalue(res, r, i_notnull_is_pk)[0] == 't')
-				{
-					/* see above */
-					if (tbinfo->relkind != RELKIND_PARTITIONED_TABLE)
-						use_throwaway_notnull = true;
-				}
-			}
-
-			if (use_unnamed_notnull)
-			{
-				tbinfo->notnull_constrs[j] = "";
-				tbinfo->notnull_throwaway[j] = false;
-			}
-			else if (use_named_notnull)
-			{
-				/* The name itself has already been determined */
-				tbinfo->notnull_throwaway[j] = false;
-			}
-			else if (use_throwaway_notnull)
-			{
-				/*
-				 * Give this constraint a throwaway name.
-				 */
-				tbinfo->notnull_constrs[j] =
-					psprintf("pgdump_throwaway_notnull_%d", notnullcount++);
-				tbinfo->notnull_throwaway[j] = true;
-				tbinfo->notnull_inh[j] = false;
-			}
-			else
-			{
-				tbinfo->notnull_constrs[j] = NULL;
-				tbinfo->notnull_throwaway[j] = false;
-			}
-
-			/*
-			 * Throwaway constraints must always be NO INHERIT; otherwise do
-			 * what the catalog says.
-			 */
-			tbinfo->notnull_noinh[j] = use_throwaway_notnull ||
-				PQgetvalue(res, r, i_notnull_noinherit)[0] == 't';
+			/* Handle not-null constraint name flags separately */
+			determineNotNullFlags(fout, res, r,
+								  tbinfo, j, &notnullcount);
 
 			tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, r, i_attoptions));
 			tbinfo->attcollation[j] = atooid(PQgetvalue(res, r, i_attcollation));
@@ -9428,6 +9285,167 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
 	destroyPQExpBuffer(checkoids);
 }
 
+/*
+ * Based on the getTableAttrs query for one row, set the name and flags to
+ * handle its not-null constraint.
+ *
+ * Result row 'r' is for tbinfo's attribute 'j'.
+ */
+static void
+determineNotNullFlags(Archive *fout, PGresult *res, int r,
+					  TableInfo *tbinfo, int j, int *notnullcount)
+{
+	DumpOptions *dopt = fout->dopt;
+	int			i_notnull_name;
+	int			i_notnull_noinherit;
+	int			i_notnull_is_pk;
+	int			i_notnull_inh;
+	bool		use_named_notnull = false;
+	bool		use_unnamed_notnull = false;
+	bool		use_throwaway_notnull = false;
+
+	i_notnull_name = PQfnumber(res, "notnull_name");
+	i_notnull_noinherit = PQfnumber(res, "notnull_noinherit");
+	i_notnull_is_pk = PQfnumber(res, "notnull_is_pk");
+	i_notnull_inh = PQfnumber(res, "notnull_inh");
+
+	/*
+	 * Not-null constraints require a jumping through a few hoops.
+	 * First, if the user has specified a constraint name that's not
+	 * the system-assigned default name, then we need to preserve
+	 * that. But if they haven't, then we don't want to use the
+	 * verbose syntax in the dump output. (Also, in versions prior to
+	 * 17, there was no constraint name at all.)
+	 *
+	 * (XXX Comparing the name this way to a supposed default name is
+	 * a bit of a hack, but it beats having to store a boolean flag in
+	 * pg_constraint just for this, or having to compute the knowledge
+	 * at pg_dump time from the server.)
+	 *
+	 * We also need to know if a column is part of the primary key. In
+	 * that case, we want to mark the column as not-null at table
+	 * creation time, so that the table doesn't have to be scanned to
+	 * check for nulls when the PK is created afterwards; this is
+	 * especially critical during pg_upgrade (where the data would not
+	 * be scanned at all otherwise.)  If the column is part of the PK
+	 * and does not have any other not-null constraint, then we
+	 * fabricate a throwaway constraint name that we later use to
+	 * remove the constraint after the PK has been created.
+	 *
+	 * For inheritance child tables, we don't want to print not-null
+	 * when the constraint was defined at the parent level instead of
+	 * locally.
+	 */
+
+	/*
+	 * We use notnull_inh to suppress unwanted not-null constraints in
+	 * inheritance children, when said constraints come from the
+	 * parent(s).
+	 */
+	tbinfo->notnull_inh[j] = PQgetvalue(res, r, i_notnull_inh)[0] == 't';
+
+	if (fout->remoteVersion < 170000)
+	{
+		if (!PQgetisnull(res, r, i_notnull_name) &&
+			dopt->binary_upgrade &&
+			!tbinfo->ispartition &&
+			tbinfo->notnull_inh[j])
+		{
+			use_named_notnull = true;
+			/* XXX should match ChooseConstraintName better */
+			tbinfo->notnull_constrs[j] =
+				psprintf("%s_%s_not_null", tbinfo->dobj.name,
+						 tbinfo->attnames[j]);
+		}
+		else if (PQgetvalue(res, r, i_notnull_is_pk)[0] == 't')
+		{
+			/*
+			 * We want this flag to be set for columns of a primary
+			 * key in which data is going to be loaded by the dump we
+			 * produce; thus a partitioned table doesn't need it.
+			 */
+			if (tbinfo->relkind != RELKIND_PARTITIONED_TABLE)
+				use_throwaway_notnull = true;
+		}
+		else if (!PQgetisnull(res, r, i_notnull_name))
+			use_unnamed_notnull = true;
+	}
+	else
+	{
+		if (!PQgetisnull(res, r, i_notnull_name))
+		{
+			/*
+			 * In binary upgrade of inheritance child tables, must
+			 * have a constraint name that we can UPDATE later.
+			 */
+			if (dopt->binary_upgrade &&
+				!tbinfo->ispartition &&
+				tbinfo->notnull_inh[j])
+			{
+				use_named_notnull = true;
+				tbinfo->notnull_constrs[j] =
+					pstrdup(PQgetvalue(res, r, i_notnull_name));
+			}
+			else
+			{
+				char	   *default_name;
+
+				/* XXX should match ChooseConstraintName better */
+				default_name = psprintf("%s_%s_not_null", tbinfo->dobj.name,
+										tbinfo->attnames[j]);
+				if (strcmp(default_name,
+						   PQgetvalue(res, r, i_notnull_name)) == 0)
+					use_unnamed_notnull = true;
+				else
+				{
+					use_named_notnull = true;
+					tbinfo->notnull_constrs[j] =
+						pstrdup(PQgetvalue(res, r, i_notnull_name));
+				}
+			}
+		}
+		else if (PQgetvalue(res, r, i_notnull_is_pk)[0] == 't')
+		{
+			/* see above */
+			if (tbinfo->relkind != RELKIND_PARTITIONED_TABLE)
+				use_throwaway_notnull = true;
+		}
+	}
+
+	if (use_unnamed_notnull)
+	{
+		tbinfo->notnull_constrs[j] = "";
+		tbinfo->notnull_throwaway[j] = false;
+	}
+	else if (use_named_notnull)
+	{
+		/* The name itself has already been determined */
+		tbinfo->notnull_throwaway[j] = false;
+	}
+	else if (use_throwaway_notnull)
+	{
+		/*
+		 * Give this constraint a throwaway name.
+		 */
+		tbinfo->notnull_constrs[j] =
+			psprintf("pgdump_throwaway_notnull_%d", (*notnullcount)++);
+		tbinfo->notnull_throwaway[j] = true;
+		tbinfo->notnull_inh[j] = false;
+	}
+	else
+	{
+		tbinfo->notnull_constrs[j] = NULL;
+		tbinfo->notnull_throwaway[j] = false;
+	}
+
+	/*
+	 * Throwaway constraints must always be NO INHERIT; otherwise do
+	 * what the catalog says.
+	 */
+	tbinfo->notnull_noinh[j] = use_throwaway_notnull ||
+		PQgetvalue(res, r, i_notnull_noinherit)[0] == 't';
+}
+
 /*
  * Test whether a column should be printed as part of table's CREATE TABLE.
  * Column number is zero-based.
-- 
2.39.2

>From 00f2d7f61e5ec50f33366fe63015b2edfbf71f8a Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvhe...@alvh.no-ip.org>
Date: Wed, 8 May 2024 13:28:01 +0200
Subject: [PATCH 3/6] pgindent run

---
 src/bin/pg_dump/pg_dump.c | 58 ++++++++++++++++++---------------------
 1 file changed, 27 insertions(+), 31 deletions(-)

diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 1d8b137814..739b16516f 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -9310,37 +9310,33 @@ determineNotNullFlags(Archive *fout, PGresult *res, int r,
 	i_notnull_inh = PQfnumber(res, "notnull_inh");
 
 	/*
-	 * Not-null constraints require a jumping through a few hoops.
-	 * First, if the user has specified a constraint name that's not
-	 * the system-assigned default name, then we need to preserve
-	 * that. But if they haven't, then we don't want to use the
-	 * verbose syntax in the dump output. (Also, in versions prior to
-	 * 17, there was no constraint name at all.)
+	 * Not-null constraints require a jumping through a few hoops. First, if
+	 * the user has specified a constraint name that's not the system-assigned
+	 * default name, then we need to preserve that. But if they haven't, then
+	 * we don't want to use the verbose syntax in the dump output. (Also, in
+	 * versions prior to 17, there was no constraint name at all.)
 	 *
-	 * (XXX Comparing the name this way to a supposed default name is
-	 * a bit of a hack, but it beats having to store a boolean flag in
-	 * pg_constraint just for this, or having to compute the knowledge
-	 * at pg_dump time from the server.)
+	 * (XXX Comparing the name this way to a supposed default name is a bit of
+	 * a hack, but it beats having to store a boolean flag in pg_constraint
+	 * just for this, or having to compute the knowledge at pg_dump time from
+	 * the server.)
 	 *
-	 * We also need to know if a column is part of the primary key. In
-	 * that case, we want to mark the column as not-null at table
-	 * creation time, so that the table doesn't have to be scanned to
-	 * check for nulls when the PK is created afterwards; this is
-	 * especially critical during pg_upgrade (where the data would not
-	 * be scanned at all otherwise.)  If the column is part of the PK
-	 * and does not have any other not-null constraint, then we
-	 * fabricate a throwaway constraint name that we later use to
-	 * remove the constraint after the PK has been created.
+	 * We also need to know if a column is part of the primary key. In that
+	 * case, we want to mark the column as not-null at table creation time, so
+	 * that the table doesn't have to be scanned to check for nulls when the
+	 * PK is created afterwards; this is especially critical during pg_upgrade
+	 * (where the data would not be scanned at all otherwise.)  If the column
+	 * is part of the PK and does not have any other not-null constraint, then
+	 * we fabricate a throwaway constraint name that we later use to remove
+	 * the constraint after the PK has been created.
 	 *
-	 * For inheritance child tables, we don't want to print not-null
-	 * when the constraint was defined at the parent level instead of
-	 * locally.
+	 * For inheritance child tables, we don't want to print not-null when the
+	 * constraint was defined at the parent level instead of locally.
 	 */
 
 	/*
 	 * We use notnull_inh to suppress unwanted not-null constraints in
-	 * inheritance children, when said constraints come from the
-	 * parent(s).
+	 * inheritance children, when said constraints come from the parent(s).
 	 */
 	tbinfo->notnull_inh[j] = PQgetvalue(res, r, i_notnull_inh)[0] == 't';
 
@@ -9360,9 +9356,9 @@ determineNotNullFlags(Archive *fout, PGresult *res, int r,
 		else if (PQgetvalue(res, r, i_notnull_is_pk)[0] == 't')
 		{
 			/*
-			 * We want this flag to be set for columns of a primary
-			 * key in which data is going to be loaded by the dump we
-			 * produce; thus a partitioned table doesn't need it.
+			 * We want this flag to be set for columns of a primary key in
+			 * which data is going to be loaded by the dump we produce; thus a
+			 * partitioned table doesn't need it.
 			 */
 			if (tbinfo->relkind != RELKIND_PARTITIONED_TABLE)
 				use_throwaway_notnull = true;
@@ -9375,8 +9371,8 @@ determineNotNullFlags(Archive *fout, PGresult *res, int r,
 		if (!PQgetisnull(res, r, i_notnull_name))
 		{
 			/*
-			 * In binary upgrade of inheritance child tables, must
-			 * have a constraint name that we can UPDATE later.
+			 * In binary upgrade of inheritance child tables, must have a
+			 * constraint name that we can UPDATE later.
 			 */
 			if (dopt->binary_upgrade &&
 				!tbinfo->ispartition &&
@@ -9439,8 +9435,8 @@ determineNotNullFlags(Archive *fout, PGresult *res, int r,
 	}
 
 	/*
-	 * Throwaway constraints must always be NO INHERIT; otherwise do
-	 * what the catalog says.
+	 * Throwaway constraints must always be NO INHERIT; otherwise do what the
+	 * catalog says.
 	 */
 	tbinfo->notnull_noinh[j] = use_throwaway_notnull ||
 		PQgetvalue(res, r, i_notnull_noinherit)[0] == 't';
-- 
2.39.2

>From 1511877eb2b0eb3cc70cbe616f3652583daff8f9 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvhe...@alvh.no-ip.org>
Date: Wed, 8 May 2024 19:41:08 +0200
Subject: [PATCH 4/6] Take PQfnumber() calls out of the routine

---
 src/bin/pg_dump/pg_dump.c | 29 +++++++++++++++++------------
 1 file changed, 17 insertions(+), 12 deletions(-)

diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 739b16516f..8af9127ba2 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -293,7 +293,9 @@ static void makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo);
 static void buildMatViewRefreshDependencies(Archive *fout);
 static void getTableDataFKConstraints(void);
 static void determineNotNullFlags(Archive *fout, PGresult *res, int r,
-								  TableInfo *tbinfo, int j, int *notnullcount);
+								  TableInfo *tbinfo, int j, int *notnullcount,
+								  int i_notnull_name, int i_notnull_noinherit,
+								  int i_notnull_is_pk, int i_notnull_inh);
 static char *format_function_arguments(const FuncInfo *finfo, const char *funcargs,
 									   bool is_agg);
 static char *format_function_signature(Archive *fout,
@@ -8704,6 +8706,10 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
 	int			i_attlen;
 	int			i_attalign;
 	int			i_attislocal;
+	int			i_notnull_name;
+	int			i_notnull_noinherit;
+	int			i_notnull_is_pk;
+	int			i_notnull_inh;
 	int			i_attoptions;
 	int			i_attcollation;
 	int			i_attcompression;
@@ -8898,6 +8904,10 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
 	i_attlen = PQfnumber(res, "attlen");
 	i_attalign = PQfnumber(res, "attalign");
 	i_attislocal = PQfnumber(res, "attislocal");
+	i_notnull_name = PQfnumber(res, "notnull_name");
+	i_notnull_noinherit = PQfnumber(res, "notnull_noinherit");
+	i_notnull_is_pk = PQfnumber(res, "notnull_is_pk");
+	i_notnull_inh = PQfnumber(res, "notnull_inh");
 	i_attoptions = PQfnumber(res, "attoptions");
 	i_attcollation = PQfnumber(res, "attcollation");
 	i_attcompression = PQfnumber(res, "attcompression");
@@ -8995,7 +9005,9 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
 
 			/* Handle not-null constraint name flags separately */
 			determineNotNullFlags(fout, res, r,
-								  tbinfo, j, &notnullcount);
+								  tbinfo, j, &notnullcount,
+								  i_notnull_name, i_notnull_noinherit,
+								  i_notnull_is_pk, i_notnull_inh);
 
 			tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, r, i_attoptions));
 			tbinfo->attcollation[j] = atooid(PQgetvalue(res, r, i_attcollation));
@@ -9293,22 +9305,15 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
  */
 static void
 determineNotNullFlags(Archive *fout, PGresult *res, int r,
-					  TableInfo *tbinfo, int j, int *notnullcount)
+					  TableInfo *tbinfo, int j, int *notnullcount,
+					  int i_notnull_name, int i_notnull_noinherit,
+					  int i_notnull_is_pk, int i_notnull_inh)
 {
 	DumpOptions *dopt = fout->dopt;
-	int			i_notnull_name;
-	int			i_notnull_noinherit;
-	int			i_notnull_is_pk;
-	int			i_notnull_inh;
 	bool		use_named_notnull = false;
 	bool		use_unnamed_notnull = false;
 	bool		use_throwaway_notnull = false;
 
-	i_notnull_name = PQfnumber(res, "notnull_name");
-	i_notnull_noinherit = PQfnumber(res, "notnull_noinherit");
-	i_notnull_is_pk = PQfnumber(res, "notnull_is_pk");
-	i_notnull_inh = PQfnumber(res, "notnull_inh");
-
 	/*
 	 * Not-null constraints require a jumping through a few hoops. First, if
 	 * the user has specified a constraint name that's not the system-assigned
-- 
2.39.2

>From e7b208fbccf74a9ea098b94392edf642712a9c44 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvhe...@alvh.no-ip.org>
Date: Wed, 8 May 2024 17:42:19 +0200
Subject: [PATCH 5/6] Use NOT NULL NO INHERIT only in pg_upgrade mode

---
 src/bin/pg_dump/pg_dump.c        |  6 ++++--
 src/bin/pg_dump/t/002_pg_dump.pl | 17 +++++++++++++++--
 2 files changed, 19 insertions(+), 4 deletions(-)

diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 8af9127ba2..3b587499e1 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -9365,7 +9365,8 @@ determineNotNullFlags(Archive *fout, PGresult *res, int r,
 			 * which data is going to be loaded by the dump we produce; thus a
 			 * partitioned table doesn't need it.
 			 */
-			if (tbinfo->relkind != RELKIND_PARTITIONED_TABLE)
+			if (dopt->binary_upgrade &&
+				tbinfo->relkind != RELKIND_PARTITIONED_TABLE)
 				use_throwaway_notnull = true;
 		}
 		else if (!PQgetisnull(res, r, i_notnull_name))
@@ -9408,7 +9409,8 @@ determineNotNullFlags(Archive *fout, PGresult *res, int r,
 		else if (PQgetvalue(res, r, i_notnull_is_pk)[0] == 't')
 		{
 			/* see above */
-			if (tbinfo->relkind != RELKIND_PARTITIONED_TABLE)
+			if (dopt->binary_upgrade &&
+				tbinfo->relkind != RELKIND_PARTITIONED_TABLE)
 				use_throwaway_notnull = true;
 		}
 	}
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index 7085053a2d..52eeb95bb6 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -3242,7 +3242,7 @@ my %tests = (
 					   );',
 		regexp => qr/^
 			\QCREATE TABLE dump_test.fk_reference_test_table (\E
-			\n\s+\Qcol1 integer CONSTRAINT \E[a-z0-9_]*\Q NOT NULL NO INHERIT\E
+			\n\s+\Qcol1 integer\E
 			\n\);
 			/xm,
 		like =>
@@ -3250,9 +3250,21 @@ my %tests = (
 		unlike => {
 			exclude_dump_test_schema => 1,
 			only_dump_measurement => 1,
+			binary_upgrade => 1,
 		},
 	},
 
+	# The same table as above when dumped in binary-upgrade mode
+	'binary-upgrade dump for fk_reference_test_table' => {
+		regexp => qr/^
+		\QCREATE TABLE dump_test.fk_reference_test_table (\E
+		\n\s+\Qcol1 integer CONSTRAINT \E[a-z0-9_]*\Q NOT NULL NO INHERIT\E
+		\n\);
+		/xm,
+		like =>
+		{ binary_upgrade => 1 }
+	},
+
 	'CREATE TABLE test_second_table' => {
 		create_order => 6,
 		create_sql => 'CREATE TABLE dump_test.test_second_table (
@@ -3635,7 +3647,7 @@ my %tests = (
 					   );',
 		regexp => qr/^
 			\QCREATE TABLE dump_test.test_table_generated (\E\n
-			\s+\Qcol1 integer CONSTRAINT \E[a-z0-9_]*\Q NOT NULL NO INHERIT,\E\n
+			\s+\Qcol1 integer,\E\n
 			\s+\Qcol2 integer GENERATED ALWAYS AS ((col1 * 2)) STORED\E\n
 			\);
 			/xms,
@@ -3644,6 +3656,7 @@ my %tests = (
 		unlike => {
 			exclude_dump_test_schema => 1,
 			only_dump_measurement => 1,
+			binary_upgrade => 1,
 		},
 	},
 
-- 
2.39.2

>From 92363783fa8bdd23e4a34d147a173354d7025e67 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvhe...@alvh.no-ip.org>
Date: Wed, 8 May 2024 18:38:20 +0200
Subject: [PATCH 6/6] Don't accept NO INHERIT changes to INHERIT in normal mode

---
 src/backend/catalog/heap.c                | 20 +++-------
 src/test/regress/expected/constraints.out | 48 ++++-------------------
 src/test/regress/expected/inherit.out     | 13 ++----
 src/test/regress/sql/constraints.sql      | 22 ++---------
 src/test/regress/sql/inherit.sql          |  6 +--
 5 files changed, 21 insertions(+), 88 deletions(-)

diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 922ba79ac2..38df163edb 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -2549,23 +2549,15 @@ AddRelationNewConstraints(Relation rel,
 
 			/*
 			 * If the column already has an inheritable not-null constraint,
-			 * 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.
+			 * we need only adjust its coninhcount and we're done.  In binary
+			 * upgrade, 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.
 			 */
 			existing = AdjustNotNullInheritance1(RelationGetRelid(rel), colnum,
 												 cdef->inhcount, cdef->is_no_inherit,
-												 IsBinaryUpgrade || allow_merge);
+												 IsBinaryUpgrade);
 			if (existing == 1)
 				continue;		/* all done! */
 			else if (existing == -1)
diff --git a/src/test/regress/expected/constraints.out b/src/test/regress/expected/constraints.out
index ec7c9e53d0..0ffe4fd3f7 100644
--- a/src/test/regress/expected/constraints.out
+++ b/src/test/regress/expected/constraints.out
@@ -321,29 +321,22 @@ ALTER TABLE ATACC1 ADD NOT NULL a NO INHERIT;
 Inherits: atacc1
 
 DROP TABLE ATACC1, ATACC2;
--- no can do
+-- partitioned tables don't accept NO INHERIT constraints
 CREATE TABLE ATACC1 (a int NOT NULL NO INHERIT) PARTITION BY LIST (a);
 ERROR:  not-null constraints on partitioned tables cannot be NO INHERIT
 CREATE TABLE ATACC1 (a int, NOT NULL a NO INHERIT) PARTITION BY LIST (a);
 ERROR:  not-null constraints on partitioned tables cannot be NO INHERIT
--- overridding a no-inherit constraint with an inheritable one
+-- not possible to override a no-inherit constraint with an inheritable one
 CREATE TABLE ATACC2 (a int, CONSTRAINT a_is_not_null NOT NULL a NO INHERIT);
 CREATE TABLE ATACC1 (a int);
-CREATE TABLE ATACC3 (a int) INHERITS (ATACC2);
-NOTICE:  merging column "a" with inherited definition
-INSERT INTO ATACC3 VALUES (null);	-- make sure we scan atacc3
 ALTER TABLE ATACC2 INHERIT ATACC1;
 ALTER TABLE ATACC1 ADD CONSTRAINT ditto NOT NULL a;
-ERROR:  column "a" of relation "atacc3" contains null values
-DELETE FROM ATACC3;
-ALTER TABLE ATACC1 ADD CONSTRAINT ditto NOT NULL a;
-\d+ ATACC[123]
+ERROR:  cannot change NO INHERIT status of NOT NULL constraint "a_is_not_null" on relation "atacc2"
+\d+ ATACC[12]
                                   Table "public.atacc1"
  Column |  Type   | Collation | Nullable | Default | Storage | Stats target | Description 
 --------+---------+-----------+----------+---------+---------+--------------+-------------
- a      | integer |           | not null |         | plain   |              | 
-Not-null constraints:
-    "ditto" NOT NULL "a"
+ a      | integer |           |          |         | plain   |              | 
 Child tables: atacc2
 
                                   Table "public.atacc2"
@@ -351,37 +344,10 @@ Child tables: atacc2
 --------+---------+-----------+----------+---------+---------+--------------+-------------
  a      | integer |           | not null |         | plain   |              | 
 Not-null constraints:
-    "a_is_not_null" NOT NULL "a" (local, inherited)
+    "a_is_not_null" NOT NULL "a" NO INHERIT
 Inherits: atacc1
-Child tables: atacc3
 
-                                  Table "public.atacc3"
- Column |  Type   | Collation | Nullable | Default | Storage | Stats target | Description 
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a      | integer |           | not null |         | plain   |              | 
-Not-null constraints:
-    "ditto" NOT NULL "a" (inherited)
-Inherits: atacc2
-
-ALTER TABLE ATACC2 DROP CONSTRAINT a_is_not_null;
-ALTER TABLE ATACC1 DROP CONSTRAINT ditto;
-\d+ ATACC3
-                                  Table "public.atacc3"
- Column |  Type   | Collation | Nullable | Default | Storage | Stats target | Description 
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a      | integer |           |          |         | plain   |              | 
-Inherits: atacc2
-
-DROP TABLE ATACC1, ATACC2, ATACC3;
--- The same cannot be achieved this way
-CREATE TABLE ATACC2 (a int, CONSTRAINT a_is_not_null NOT NULL a NO INHERIT);
-CREATE TABLE ATACC1 (a int, CONSTRAINT ditto NOT NULL a);
-CREATE TABLE ATACC3 (a int) INHERITS (ATACC2);
-NOTICE:  merging column "a" with inherited definition
-ALTER TABLE ATACC2 INHERIT ATACC1;
-ERROR:  cannot add NOT NULL constraint to column "a" of relation "atacc2" with inheritance children
-DETAIL:  Existing constraint "a_is_not_null" is marked NO INHERIT.
-DROP TABLE ATACC1, ATACC2, ATACC3;
+DROP TABLE ATACC1, ATACC2;
 --
 -- Check constraints on INSERT INTO
 --
diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out
index a621db0aa3..1d40468015 100644
--- a/src/test/regress/expected/inherit.out
+++ b/src/test/regress/expected/inherit.out
@@ -2290,21 +2290,14 @@ NOTICE:  drop cascades to table inh_nn_child
 CREATE TABLE inh_nn_lvl1 (a int);
 CREATE TABLE inh_nn_lvl2 () INHERITS (inh_nn_lvl1);
 CREATE TABLE inh_nn_lvl3 (CONSTRAINT foo NOT NULL a NO INHERIT) INHERITS (inh_nn_lvl2);
-CREATE TABLE inh_nn_lvl4 () INHERITS (inh_nn_lvl3);
-CREATE TABLE inh_nn_lvl5 () INHERITS (inh_nn_lvl4);
-INSERT INTO inh_nn_lvl2 VALUES (NULL);
 ALTER TABLE inh_nn_lvl1 ADD PRIMARY KEY (a);
-ERROR:  column "a" of relation "inh_nn_lvl2" contains null values
-DELETE FROM inh_nn_lvl2;
-INSERT INTO inh_nn_lvl5 VALUES (NULL);
+ERROR:  cannot change NO INHERIT status of NOT NULL constraint "foo" on relation "inh_nn_lvl3"
+ALTER TABLE inh_nn_lvl3 DROP CONSTRAINT foo;
 ALTER TABLE inh_nn_lvl1 ADD PRIMARY KEY (a);
-ERROR:  column "a" of relation "inh_nn_lvl5" contains null values
 DROP TABLE inh_nn_lvl1 CASCADE;
-NOTICE:  drop cascades to 4 other objects
+NOTICE:  drop cascades to 2 other objects
 DETAIL:  drop cascades to table inh_nn_lvl2
 drop cascades to table inh_nn_lvl3
-drop cascades to table inh_nn_lvl4
-drop cascades to table inh_nn_lvl5
 --
 -- test inherit/deinherit
 --
diff --git a/src/test/regress/sql/constraints.sql b/src/test/regress/sql/constraints.sql
index e753b8c345..f8ca855e8b 100644
--- a/src/test/regress/sql/constraints.sql
+++ b/src/test/regress/sql/constraints.sql
@@ -212,31 +212,17 @@ ALTER TABLE ATACC1 ADD NOT NULL a NO INHERIT;
 \d+ ATACC2
 DROP TABLE ATACC1, ATACC2;
 
--- no can do
+-- partitioned tables don't accept NO INHERIT constraints
 CREATE TABLE ATACC1 (a int NOT NULL NO INHERIT) PARTITION BY LIST (a);
 CREATE TABLE ATACC1 (a int, NOT NULL a NO INHERIT) PARTITION BY LIST (a);
 
--- overridding a no-inherit constraint with an inheritable one
+-- not possible to override a no-inherit constraint with an inheritable one
 CREATE TABLE ATACC2 (a int, CONSTRAINT a_is_not_null NOT NULL a NO INHERIT);
 CREATE TABLE ATACC1 (a int);
-CREATE TABLE ATACC3 (a int) INHERITS (ATACC2);
-INSERT INTO ATACC3 VALUES (null);	-- make sure we scan atacc3
 ALTER TABLE ATACC2 INHERIT ATACC1;
 ALTER TABLE ATACC1 ADD CONSTRAINT ditto NOT NULL a;
-DELETE FROM ATACC3;
-ALTER TABLE ATACC1 ADD CONSTRAINT ditto NOT NULL a;
-\d+ ATACC[123]
-ALTER TABLE ATACC2 DROP CONSTRAINT a_is_not_null;
-ALTER TABLE ATACC1 DROP CONSTRAINT ditto;
-\d+ ATACC3
-DROP TABLE ATACC1, ATACC2, ATACC3;
-
--- The same cannot be achieved this way
-CREATE TABLE ATACC2 (a int, CONSTRAINT a_is_not_null NOT NULL a NO INHERIT);
-CREATE TABLE ATACC1 (a int, CONSTRAINT ditto NOT NULL a);
-CREATE TABLE ATACC3 (a int) INHERITS (ATACC2);
-ALTER TABLE ATACC2 INHERIT ATACC1;
-DROP TABLE ATACC1, ATACC2, ATACC3;
+\d+ ATACC[12]
+DROP TABLE ATACC1, ATACC2;
 
 --
 -- Check constraints on INSERT INTO
diff --git a/src/test/regress/sql/inherit.sql b/src/test/regress/sql/inherit.sql
index 2205e59aff..d37d2d3ce8 100644
--- a/src/test/regress/sql/inherit.sql
+++ b/src/test/regress/sql/inherit.sql
@@ -855,12 +855,8 @@ DROP TABLE inh_nn_parent cascade;
 CREATE TABLE inh_nn_lvl1 (a int);
 CREATE TABLE inh_nn_lvl2 () INHERITS (inh_nn_lvl1);
 CREATE TABLE inh_nn_lvl3 (CONSTRAINT foo NOT NULL a NO INHERIT) INHERITS (inh_nn_lvl2);
-CREATE TABLE inh_nn_lvl4 () INHERITS (inh_nn_lvl3);
-CREATE TABLE inh_nn_lvl5 () INHERITS (inh_nn_lvl4);
-INSERT INTO inh_nn_lvl2 VALUES (NULL);
 ALTER TABLE inh_nn_lvl1 ADD PRIMARY KEY (a);
-DELETE FROM inh_nn_lvl2;
-INSERT INTO inh_nn_lvl5 VALUES (NULL);
+ALTER TABLE inh_nn_lvl3 DROP CONSTRAINT foo;
 ALTER TABLE inh_nn_lvl1 ADD PRIMARY KEY (a);
 DROP TABLE inh_nn_lvl1 CASCADE;
 
-- 
2.39.2

Reply via email to