diff options
author | Álvaro Herrera <alvherre@alvh.no-ip.org> | 2024-11-08 13:28:48 +0100 |
---|---|---|
committer | Álvaro Herrera <alvherre@alvh.no-ip.org> | 2024-11-08 13:28:48 +0100 |
commit | 14e87ffa5c543b5f30ead7413084c25f7735039f (patch) | |
tree | 2af8f459779aef83acf20fbb8ef480d2ddc2af89 /src/backend/utils/cache/relcache.c | |
parent | 075acdd93388c080c0fb0aca5723144ad7a56dac (diff) | |
download | postgresql-14e87ffa5c543b5f30ead7413084c25f7735039f.tar.gz postgresql-14e87ffa5c543b5f30ead7413084c25f7735039f.zip |
Add pg_constraint rows for not-null constraints
We now create contype='n' pg_constraint rows for not-null constraints on
user tables. Only one such constraint is allowed for a column.
We propagate these constraints to other tables during operations such as
adding inheritance relationships, creating and attaching partitions and
creating tables LIKE other tables. These related constraints mostly
follow the well-known rules of conislocal and coninhcount that we have
for CHECK constraints, with some adaptations: for example, as opposed to
CHECK constraints, we don't match not-null ones by name when descending
a hierarchy to alter or remove it, instead matching by the name of the
column that they apply to. This means we don't require the constraint
names to be identical across a hierarchy.
The inheritance status of these constraints can be controlled: now we
can be sure that if a parent table has one, then all children will have
it as well. They can optionally be marked NO INHERIT, and then children
are free not to have one. (There's currently no support for altering a
NO INHERIT constraint into inheriting down the hierarchy, but that's a
desirable future feature.)
This also opens the door for having these constraints be marked NOT
VALID, as well as allowing UNIQUE+NOT NULL to be used for functional
dependency determination, as envisioned by commit e49ae8d3bc58. It's
likely possible to allow DEFERRABLE constraints as followup work, as
well.
psql shows these constraints in \d+, though we may want to reconsider if
this turns out to be too noisy. Earlier versions of this patch hid
constraints that were on the same columns of the primary key, but I'm
not sure that that's very useful. If clutter is a problem, we might be
better off inventing a new \d++ command and not showing the constraints
in \d+.
For now, we omit these constraints on system catalog columns, because
they're unlikely to achieve anything.
The main difference to the previous attempt at this (b0e96f311985) is
that we now require that such a constraint always exists when a primary
key is in the column; we didn't require this previously which had a
number of unpalatable consequences. With this requirement, the code is
easier to reason about. For example:
- We no longer have "throwaway constraints" during pg_dump. We needed
those for the case where a table had a PK without a not-null
underneath, to prevent a slow scan of the data during restore of the
PK creation, which was particularly problematic for pg_upgrade.
- We no longer have to cope with attnotnull being set spuriously in
case a primary key is dropped indirectly (e.g., via DROP COLUMN).
Some bits of code in this patch were authored by Jian He.
Author: Álvaro Herrera <alvherre@alvh.no-ip.org>
Author: Bernd Helmle <mailings@oopsware.de>
Reviewed-by: 何建 (jian he) <jian.universality@gmail.com>
Reviewed-by: 王刚 (Tender Wang) <tndrwang@gmail.com>
Reviewed-by: Justin Pryzby <pryzby@telsasoft.com>
Reviewed-by: Peter Eisentraut <peter.eisentraut@enterprisedb.com>
Reviewed-by: Dean Rasheed <dean.a.rasheed@gmail.com>
Discussion: https://postgr.es/m/202408310358.sdhumtyuy2ht@alvherre.pgsql
Diffstat (limited to 'src/backend/utils/cache/relcache.c')
-rw-r--r-- | src/backend/utils/cache/relcache.c | 44 |
1 files changed, 34 insertions, 10 deletions
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 5bbb654a5db..342467fd186 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -4817,18 +4817,38 @@ RelationGetIndexList(Relation relation) result = lappend_oid(result, index->indexrelid); /* - * Invalid, non-unique, non-immediate or predicate indexes aren't - * interesting for either oid indexes or replication identity indexes, - * so don't check them. + * Non-unique or predicate indexes aren't interesting for either oid + * indexes or replication identity indexes, so don't check them. + * Deferred ones are not useful for replication identity either; but + * we do include them if they are PKs. */ - if (!index->indisvalid || !index->indisunique || - !index->indimmediate || + if (!index->indisunique || !heap_attisnull(htup, Anum_pg_index_indpred, NULL)) continue; - /* remember primary key index if any */ - if (index->indisprimary) + /* + * Remember primary key index, if any. For regular tables we do this + * only if the index is valid; but for partitioned tables, then we do + * it even if it's invalid. + * + * The reason for returning invalid primary keys for partitioned + * tables is that we need it to prevent drop of not-null constraints + * that may underlie such a primary key, which is only a problem for + * partitioned tables. + */ + if (index->indisprimary && + (index->indisvalid || + relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)) + { pkeyIndex = index->indexrelid; + pkdeferrable = !index->indimmediate; + } + + if (!index->indimmediate) + continue; + + if (!index->indisvalid) + continue; /* remember explicitly chosen replica index */ if (index->indisreplident) @@ -4952,10 +4972,10 @@ RelationGetStatExtList(Relation relation) * RelationGetPrimaryKeyIndex -- get OID of the relation's primary key index * * Returns InvalidOid if there is no such index, or if the primary key is - * DEFERRABLE. + * DEFERRABLE and the caller isn't OK with that. */ Oid -RelationGetPrimaryKeyIndex(Relation relation) +RelationGetPrimaryKeyIndex(Relation relation, bool deferrable_ok) { List *ilist; @@ -4967,7 +4987,11 @@ RelationGetPrimaryKeyIndex(Relation relation) Assert(relation->rd_indexvalid); } - return relation->rd_ispkdeferrable ? InvalidOid : relation->rd_pkindex; + if (deferrable_ok) + return relation->rd_pkindex; + else if (relation->rd_ispkdeferrable) + return InvalidOid; + return relation->rd_pkindex; } /* |