From 14e87ffa5c543b5f30ead7413084c25f7735039f Mon Sep 17 00:00:00 2001 From: Álvaro Herrera Date: Fri, 8 Nov 2024 13:28:48 +0100 Subject: 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 Author: Bernd Helmle Reviewed-by: 何建 (jian he) Reviewed-by: 王刚 (Tender Wang) Reviewed-by: Justin Pryzby Reviewed-by: Peter Eisentraut Reviewed-by: Dean Rasheed Discussion: https://postgr.es/m/202408310358.sdhumtyuy2ht@alvherre.pgsql --- doc/src/sgml/catalogs.sgml | 12 ++---- doc/src/sgml/ddl.sgml | 65 ++++++++++++++++++++++-------- doc/src/sgml/ref/alter_foreign_table.sgml | 6 ++- doc/src/sgml/ref/alter_table.sgml | 30 ++++++++------ doc/src/sgml/ref/create_foreign_table.sgml | 10 ++++- doc/src/sgml/ref/create_table.sgml | 17 ++++---- 6 files changed, 89 insertions(+), 51 deletions(-) (limited to 'doc/src') diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 964c819a02d..c180ed7abbc 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -1271,7 +1271,7 @@ attnotnull bool - This represents a not-null constraint. + This column has a not-null constraint. @@ -2502,14 +2502,10 @@ SCRAM-SHA-256$<iteration count>:&l - The catalog pg_constraint stores check, primary - key, unique, foreign key, and exclusion constraints on tables, as well as - not-null constraints on domains. + The catalog pg_constraint stores check, not-null, + primary key, unique, foreign key, and exclusion constraints on tables. (Column constraints are not treated specially. Every column constraint is equivalent to some table constraint.) - Not-null constraints on relations are represented in the - pg_attribute - catalog, not here. @@ -2571,7 +2567,7 @@ SCRAM-SHA-256$<iteration count>:&l c = check constraint, f = foreign key constraint, - n = not-null constraint (domains only), + n = not-null constraint, p = primary key constraint, u = unique constraint, t = constraint trigger, diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml index 898b6ddc8df..3c56610d2ac 100644 --- a/doc/src/sgml/ddl.sgml +++ b/doc/src/sgml/ddl.sgml @@ -772,17 +772,38 @@ CREATE TABLE products ( price numeric ); + An explicit constraint name can also be specified, for example: + +CREATE TABLE products ( + product_no integer NOT NULL, + name text CONSTRAINT products_name_not_null NOT NULL, + price numeric +); + + + + + A not-null constraint is usually written as a column constraint. The + syntax for writing it as a table constraint is + +CREATE TABLE products ( + product_no integer, + name text, + price numeric, + NOT NULL product_no, + NOT NULL name +); + + But this syntax is not standard and mainly intended for use by + pg_dump. - A not-null constraint is always written as a column constraint. A - not-null constraint is functionally equivalent to creating a check + A not-null constraint is functionally equivalent to creating a check constraint CHECK (column_name IS NOT NULL), but in PostgreSQL creating an explicit - not-null constraint is more efficient. The drawback is that you - cannot give explicit names to not-null constraints created this - way. + not-null constraint is more efficient. @@ -799,6 +820,10 @@ CREATE TABLE products ( order the constraints are checked. + + However, a column can have at most one explicit not-null constraint. + + The NOT NULL constraint has an inverse: the NULL constraint. This does not mean that the @@ -992,7 +1017,7 @@ CREATE TABLE example ( A table can have at most one primary key. (There can be any number - of unique and not-null constraints, which are functionally almost the + of unique constraints, which combined with not-null constraints are functionally almost the same thing, but only one can be identified as the primary key.) Relational database theory dictates that every table must have a primary key. This rule is @@ -1652,11 +1677,16 @@ ALTER TABLE products ADD CHECK (name <> ''); ALTER TABLE products ADD CONSTRAINT some_name UNIQUE (product_no); ALTER TABLE products ADD FOREIGN KEY (product_group_id) REFERENCES product_groups; - To add a not-null constraint, which cannot be written as a table - constraint, use this syntax: + + + + To add a not-null constraint, which is normally not written as a table + constraint, this special syntax is available: ALTER TABLE products ALTER COLUMN product_no SET NOT NULL; + This command silently does nothing if the column already has a + not-null constraint. @@ -1697,12 +1727,15 @@ ALTER TABLE products DROP CONSTRAINT some_name; - This works the same for all constraint types except not-null - constraints. To drop a not-null constraint use: + Simplified syntax is available to drop a not-null constraint: ALTER TABLE products ALTER COLUMN product_no DROP NOT NULL; - (Recall that not-null constraints do not have names.) + This mirrors the SET NOT NULL syntax for adding a + not-null constraint. This command will silently do nothing if the column + does not have a not-null constraint. (Recall that a column can have at + most one not-null constraint, so it is never ambiguous which constraint + this command acts on.) @@ -4446,12 +4479,10 @@ ALTER INDEX measurement_city_id_logdate_key Both CHECK and NOT NULL constraints of a partitioned table are always inherited by all its - partitions. CHECK constraints that are marked - NO INHERIT are not allowed to be created on - partitioned tables. - You cannot drop a NOT NULL constraint on a - partition's column if the same constraint is present in the parent - table. + partitions; it is not allowed to create NO INHERIT + constraints of those types. + You cannot drop a constraint of those types if the same constraint + is present in the parent table. diff --git a/doc/src/sgml/ref/alter_foreign_table.sgml b/doc/src/sgml/ref/alter_foreign_table.sgml index 3cb6f08fcf2..e2da3cc719f 100644 --- a/doc/src/sgml/ref/alter_foreign_table.sgml +++ b/doc/src/sgml/ref/alter_foreign_table.sgml @@ -173,7 +173,8 @@ ALTER FOREIGN TABLE [ IF EXISTS ] name This form adds a new constraint to a foreign table, using the same syntax as CREATE FOREIGN TABLE. - Currently only CHECK constraints are supported. + Currently only CHECK and NOT NULL + constraints are supported. @@ -182,7 +183,8 @@ ALTER FOREIGN TABLE [ IF EXISTS ] nameCREATE FOREIGN TABLE.) - If the constraint is marked NOT VALID, then it isn't + If the constraint is marked NOT VALID (allowed only for + the CHECK case), then it isn't assumed to hold, but is only recorded for possible future use. diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml index 61a0fb3dec1..6098ebed433 100644 --- a/doc/src/sgml/ref/alter_table.sgml +++ b/doc/src/sgml/ref/alter_table.sgml @@ -98,7 +98,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 | @@ -114,6 +114,7 @@ WITH ( MODULUS numeric_literal, REM [ 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 ) ] | @@ -849,19 +850,16 @@ WITH ( MODULUS numeric_literal, REM table. Subsequently, queries against the parent will include records of the target table. To be added as a child, the target table must already contain all the same columns as the parent (it could have - additional columns, too). The columns must have matching data types, - and if they have NOT NULL constraints in the parent - then they must also have NOT NULL constraints in the - child. + additional columns, too). The columns must have matching data types. - There must also be matching child-table constraints for all - CHECK constraints of the parent, except those - marked non-inheritable (that is, created with ALTER TABLE ... ADD CONSTRAINT ... NO INHERIT) - in the parent, which are ignored; all child-table constraints matched - must not be marked non-inheritable. - Currently + In addition, all CHECK and NOT NULL + constraints on the parent must also exist on the child, except those + marked non-inheritable (that is, created with + ALTER TABLE ... ADD CONSTRAINT ... NO INHERIT), which + are ignored. All child-table constraints matched must not be marked + non-inheritable. Currently UNIQUE, PRIMARY KEY, and FOREIGN KEY constraints are not considered, but this might change in the future. @@ -1793,11 +1791,17 @@ ALTER TABLE measurement Compatibility - The forms ADD (without USING INDEX), + The forms ADD [COLUMN], DROP [COLUMN], DROP IDENTITY, RESTART, SET DEFAULT, SET DATA TYPE (without USING), SET GENERATED, and SET sequence_option - conform with the SQL standard. The other forms are + conform with the SQL standard. + The form ADD table_constraint + conforms with the SQL standard when the USING INDEX and + NOT VALID clauses are omitted and the constraint type is + one of CHECK, UNIQUE, PRIMARY KEY, + or REFERENCES. + The other forms are PostgreSQL extensions of the SQL standard. Also, the ability to specify more than one manipulation in a single ALTER TABLE command is an extension. diff --git a/doc/src/sgml/ref/create_foreign_table.sgml b/doc/src/sgml/ref/create_foreign_table.sgml index dc4b9075990..fc81ba3c498 100644 --- a/doc/src/sgml/ref/create_foreign_table.sgml +++ b/doc/src/sgml/ref/create_foreign_table.sgml @@ -43,7 +43,7 @@ CREATE FOREIGN TABLE [ IF NOT EXISTS ] table_name where column_constraint is: [ CONSTRAINT constraint_name ] -{ NOT NULL | +{ NOT NULL [ NO INHERIT ] | NULL | CHECK ( expression ) [ NO INHERIT ] | DEFAULT default_expr | @@ -52,6 +52,7 @@ CREATE FOREIGN TABLE [ IF NOT EXISTS ] table_name and table_constraint is: [ CONSTRAINT constraint_name ] + NOT NULL column_name [ NO INHERIT ] | CHECK ( expression ) [ NO INHERIT ] and partition_bound_spec is: @@ -203,11 +204,16 @@ WITH ( MODULUS numeric_literal, REM - NOT NULL + NOT NULL [ NO INHERIT ] The column is not allowed to contain null values. + + + A constraint marked with NO INHERIT will not propagate to + child tables. + diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml index 83859bac76f..dd83b07d65f 100644 --- a/doc/src/sgml/ref/create_table.sgml +++ b/doc/src/sgml/ref/create_table.sgml @@ -61,7 +61,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI where column_constraint is: [ CONSTRAINT constraint_name ] -{ NOT NULL | +{ NOT NULL [ NO INHERIT ] | NULL | CHECK ( expression ) [ NO INHERIT ] | DEFAULT default_expr | @@ -77,6 +77,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI [ CONSTRAINT constraint_name ] { CHECK ( expression ) [ NO INHERIT ] | + NOT NULL column_name [ NO INHERIT ] | UNIQUE [ NULLS [ NOT ] DISTINCT ] ( column_name [, ... ] [, column_name WITHOUT OVERLAPS ] ) index_parameters | PRIMARY KEY ( column_name [, ... ] [, column_name WITHOUT OVERLAPS ] ) index_parameters | EXCLUDE [ USING index_method ] ( exclude_element WITH operator [, ... ] ) index_parameters [ WHERE ( predicate ) ] | @@ -818,11 +819,16 @@ WITH ( MODULUS numeric_literal, REM - NOT NULL + NOT NULL [ NO INHERIT ] The column is not allowed to contain null values. + + + A constraint marked with NO INHERIT will not propagate to + child tables. + @@ -2398,13 +2404,6 @@ CREATE TABLE cities_partdef constraint, and index names must be unique across all relations within the same schema. - - - Currently, PostgreSQL does not record names - for not-null constraints at all, so they are not - subject to the uniqueness restriction. This might change in a future - release. - -- cgit v1.2.3