postgresql/src/backend/catalog
Tom Lane f4a3fdfbdc Avoid order-of-execution problems with ALTER TABLE ADD PRIMARY KEY.
Up to now, DefineIndex() was responsible for adding attnotnull constraints
to the columns of a primary key, in any case where it hadn't been
convenient for transformIndexConstraint() to mark those columns as
is_not_null.  It (or rather its minion index_check_primary_key) did this
by executing an ALTER TABLE SET NOT NULL command for the target table.

The trouble with this solution is that if we're creating the index due
to ALTER TABLE ADD PRIMARY KEY, and the outer ALTER TABLE has additional
sub-commands, the inner ALTER TABLE's operations executed at the wrong
time with respect to the outer ALTER TABLE's operations.  In particular,
the inner ALTER would perform a validation scan at a point where the
table's storage might be inconsistent with its catalog entries.  (This is
on the hairy edge of being a security problem, but AFAICS it isn't one
because the inner scan would only be interested in the tuples' null
bitmaps.)  This can result in unexpected failures, such as the one seen
in bug #15580 from Allison Kaptur.

To fix, let's remove the attempt to do SET NOT NULL from DefineIndex(),
reducing index_check_primary_key's role to verifying that the columns are
already not null.  (It shouldn't ever see such a case, but it seems wise
to keep the check for safety.)  Instead, make transformIndexConstraint()
generate ALTER TABLE SET NOT NULL subcommands to be executed ahead of
the ADD PRIMARY KEY operation in every case where it can't force the
column to be created already-not-null.  This requires only minor surgery
in parse_utilcmd.c, and it makes for a much more satisfying spec for
transformIndexConstraint(): it's no longer having to take it on faith
that someone else will handle addition of NOT NULL constraints.

To make that work, we have to move the execution of AT_SetNotNull into
an ALTER pass that executes ahead of AT_PASS_ADD_INDEX.  I moved it to
AT_PASS_COL_ATTRS, and put that after AT_PASS_ADD_COL to avoid failure
when the column is being added in the same command.  This incidentally
fixes a bug in the only previous usage of AT_PASS_COL_ATTRS, for
AT_SetIdentity: it didn't work either for a newly-added column.

Playing around with this exposed a separate bug in ALTER TABLE ONLY ...
ADD PRIMARY KEY for partitioned tables.  The intent of the ONLY modifier
in that context is to prevent doing anything that would require holding
lock for a long time --- but the implied SET NOT NULL would recurse to
the child partitions, and do an expensive validation scan for any child
where the column(s) were not already NOT NULL.  To fix that, invent a
new ALTER subcommand AT_CheckNotNull that just insists that a child
column be already NOT NULL, and apply that, not AT_SetNotNull, when
recursing to children in this scenario.  This results in a slightly laxer
definition of ALTER TABLE ONLY ... SET NOT NULL for partitioned tables,
too: that command will now work as long as all children are already NOT
NULL, whereas before it just threw up its hands if there were any
partitions.

In passing, clean up the API of generateClonedIndexStmt(): remove a
useless argument, ensure that the output argument is not left undefined,
update the header comment.

A small side effect of this change is that no-such-column errors in ALTER
TABLE ADD PRIMARY KEY now produce a different message that includes the
table name, because they are now detected by the SET NOT NULL step which
has historically worded its error that way.  That seems fine to me, so
I didn't make any effort to avoid the wording change.

The basic bug #15580 is of very long standing, and these other bugs
aren't new in v12 either.  However, this is a pretty significant change
in the way ALTER TABLE ADD PRIMARY KEY works.  On balance it seems best
not to back-patch, at least not till we get some more confidence that
this patch has no new bugs.

Patch by me, but thanks to Jie Zhang for a preliminary version.

Discussion: https://postgr.es/m/15580-d1a6de5a3d65da51@postgresql.org
Discussion: https://postgr.es/m/1396E95157071C4EBBA51892C5368521017F2E6E63@G08CNEXMBPEKD02.g08.fujitsu.local
2019-04-23 12:25:27 -04:00
..
.gitignore Replace our traditional initial-catalog-data format with a better design. 2018-04-08 13:17:27 -04:00
Catalog.pm Create a script that can renumber manually-assigned OIDs. 2019-03-12 10:50:48 -04:00
Makefile Use Getopt::Long for catalog scripts 2019-02-12 12:22:08 -03:00
aclchk.c tableam: Add and use scan APIs. 2019-03-11 12:46:41 -07:00
catalog.c Move generic snapshot related code from tqual.h to snapmgr.h. 2019-01-21 17:06:41 -08:00
dependency.c REINDEX CONCURRENTLY 2019-03-29 08:26:33 +01:00
genbki.pl Sync commentary in transam.h and bki.sgml. 2019-03-14 00:23:40 -04:00
heap.c Generated columns 2019-03-30 08:15:57 +01:00
index.c Avoid order-of-execution problems with ALTER TABLE ADD PRIMARY KEY. 2019-04-23 12:25:27 -04:00
indexing.c Don't include genam.h from execnodes.h and relscan.h anymore. 2019-01-14 17:02:12 -08:00
information_schema.sql Generated columns 2019-03-30 08:15:57 +01:00
namespace.c Restrict the use of temporary namespace in two-phase transactions 2019-01-18 09:21:44 +09:00
objectaccess.c Update copyright for 2019 2019-01-02 12:44:25 -05:00
objectaddress.c Make object address handling more robust 2019-02-20 11:26:08 -03:00
partition.c Add index_get_partition convenience function 2019-03-20 18:18:50 -03:00
pg_aggregate.c Implement OR REPLACE option for CREATE AGGREGATE. 2019-03-19 01:16:50 +00:00
pg_collation.c Collations with nondeterministic comparison 2019-03-22 12:12:43 +01:00
pg_constraint.c Move code for managing PartitionDescs into a new file, partdesc.c 2019-02-21 11:45:02 -05:00
pg_conversion.c tableam: Add and use scan APIs. 2019-03-11 12:46:41 -07:00
pg_db_role_setting.c tableam: Add and use scan APIs. 2019-03-11 12:46:41 -07:00
pg_depend.c Fix REINDEX CONCURRENTLY of partitions 2019-04-12 08:36:05 +02:00
pg_enum.c Remove superfluous tqual.h includes. 2019-01-21 12:15:02 -08:00
pg_inherits.c Remove superfluous tqual.h includes. 2019-01-21 12:15:02 -08:00
pg_largeobject.c Remove superfluous tqual.h includes. 2019-01-21 12:15:02 -08:00
pg_namespace.c Replace uses of heap_open et al with the corresponding table_* function. 2019-01-21 10:51:37 -08:00
pg_operator.c Replace uses of heap_open et al with the corresponding table_* function. 2019-01-21 10:51:37 -08:00
pg_proc.c Implement OR REPLACE option for CREATE AGGREGATE. 2019-03-19 01:16:50 +00:00
pg_publication.c tableam: Add and use scan APIs. 2019-03-11 12:46:41 -07:00
pg_range.c Remove superfluous tqual.h includes. 2019-01-21 12:15:02 -08:00
pg_shdepend.c Sort dependent objects before reporting them in DROP ROLE. 2019-03-24 18:17:53 -04:00
pg_subscription.c tableam: Add and use scan APIs. 2019-03-11 12:46:41 -07:00
pg_type.c Replace uses of heap_open et al with the corresponding table_* function. 2019-01-21 10:51:37 -08:00
sql_feature_packages.txt > I have installed your patch and adjusted the names of the standards 2004-12-02 22:51:28 +00:00
sql_features.txt Improve documentation about our XML functionality. 2019-04-01 16:20:22 -04:00
storage.c tableam: relation creation, VACUUM FULL/CLUSTER, SET TABLESPACE. 2019-03-28 20:01:43 -07:00
system_views.sql Return NULL for checksum failures if checksums are not enabled 2019-04-17 13:51:48 +02:00
toasting.c Ignore attempts to add TOAST table to shared or catalog tables 2019-03-19 11:15:50 +01:00