information_schema.check_constraints Inconsistencies
Hello,
I am writing with regards to some wrong results I keep on receiving when
using check_constraints view from information_schema:
(1) First, it shows constraints grouped by name, regardless of the relation
used: when having two constraints with the same name in different tables,
it shows both in both tables, regardless of their belonging;
(2) Second, it also lists NOT NULL constraints, even though they are not
created as check constraints.
See, for example, the following select:
select *
from information_schema.table_constraints c
join information_schema.check_constraints ck
on c.constraint_name = ck.constraint_name
and c.constraint_schema = ck.constraint_schema
and c.constraint_catalog = ck.constraint_catalog
where c.constraint_type = 'CHECK'
and c.table_schema = 'events'
and c.table_name = 'horse_racing_purchase_event'
It returns data in --PASTE1-- (at end).
Postgresql version used:
PostgreSQL 10.3, compiled by Visual C++ build 1800, 64-bit
Data used:
create schema events;
create table if not exists events.event
(
id bigserial not null
constraint event_pkey
primary key,
occurred_at timestamp default CURRENT_TIMESTAMP not null,
operator_id integer not null,
player_id integer not null,
player_ip inet not null,
session_id uuid not null,
has_player_confirmation boolean default true not null,
transformed_event text,
arjel_type_id smallint
)
;
create table if not exists events.horse_racing_purchase_event
(
purchase_id bigint not null,
event_label_as_shown_to_player text not null
constraint c_event_label_as_shown_to_player
check (public.is_valid_string(256,
event_label_as_shown_to_player)),
balance_before money,
balance_after money,
bet_amount money,
bet_contribution_from_operator money,
bonus_before money,
bonus_after money,
bonus_amount money,
bonus_name text,
race_type text not null
constraint c_race_type
check (public.is_valid_string(64, race_type)),
bet_name_as_shown_to_player text not null
constraint c_bet_name_as_shown_to_player
check (public.is_valid_string(64, bet_name_as_shown_to_player)),
event_date timestamp not null,
constraint horse_racing_purchase_event_pkey
primary key (id),
constraint c_balance
check ((((balance_before + balance_after) + bet_amount) IS NOT
NULL) OR (COALESCE(balance_before, balance_after, bet_amount) IS NULL)),
constraint c_bonus
check ((((bonus_before + bonus_after) + bonus_amount) IS NOT NULL)
OR (COALESCE(bonus_before, bonus_after, bonus_amount) IS NULL))
)
inherits (events.event)
;
comment on column events.horse_racing_purchase_event.bet_amount is 'without
operator contribution and bonuses'
;
create table if not exists events.balance_adjustment_event
(
account_type_id smallint not null
constraint balance_adjustment_event_account_type_id_fkey
references agreement_type,
information_text text not null,
adjustment_type_id smallint not null
constraint balance_adjustment_event_adjustment_type_id_fkey
references adjustment_type,
balance_before numeric(21,4),
balance_after numeric(21,4),
adjustment_to_balance_amount numeric(21,4),
bonus_before numeric(21,4),
bonus_after numeric(21,4),
adjustment_to_bonus_amount numeric(21,4),
constraint balance_adjustment_event_pkey
primary key (id),
constraint c_bonus
check ((((adjustment_to_bonus_amount + bonus_before) + bonus_after)
IS NOT NULL) OR (COALESCE(adjustment_to_bonus_amount, bonus_before,
bonus_after) IS NULL)),
constraint c_balance
check ((((adjustment_to_balance_amount + balance_before) +
balance_after) IS NOT NULL) OR (COALESCE(adjustment_to_balance_amount,
balance_before, balance_after) IS NULL))
)
inherits (events.event)
;
--PASTE1--
i_r_french_regulation events c_balance i_r_french_regulation
events horse_racing_purchase_event CHECK NO NO
i_r_french_regulation events c_balance (((((balance_before +
balance_after) + bet_amount) IS NOT NULL) OR (COALESCE(balance_before,
balance_after, bet_amount) IS NULL)))
i_r_french_regulation events c_balance i_r_french_regulation
events horse_racing_purchase_event CHECK NO NO
i_r_french_regulation events c_balance
(((((adjustment_to_balance_amount + balance_before) + balance_after) IS NOT
NULL) OR (COALESCE(adjustment_to_balance_amount, balance_before,
balance_after) IS NULL)))
i_r_french_regulation events c_bet_name_as_shown_to_player
i_r_french_regulation events horse_racing_purchase_event CHECK
NO NO i_r_french_regulation events
c_bet_name_as_shown_to_player (is_valid_string(64,
bet_name_as_shown_to_player))
i_r_french_regulation events c_bonus i_r_french_regulation
events horse_racing_purchase_event CHECK NO NO
i_r_french_regulation events c_bonus
(((((adjustment_to_bonus_amount + bonus_before) + bonus_after) IS NOT NULL)
OR (COALESCE(adjustment_to_bonus_amount, bonus_before, bonus_after) IS
NULL)))
i_r_french_regulation events c_bonus i_r_french_regulation
events horse_racing_purchase_event CHECK NO NO
i_r_french_regulation events c_bonus (((((bonus_before +
bonus_after) + bonus_amount) IS NOT NULL) OR (COALESCE(bonus_before,
bonus_after, bonus_amount) IS NULL)))
i_r_french_regulation events c_event_label_as_shown_to_player
i_r_french_regulation events horse_racing_purchase_event CHECK
NO NO i_r_french_regulation events
c_event_label_as_shown_to_player (is_valid_string(256,
event_label_as_shown_to_player))
i_r_french_regulation events c_event_label_as_shown_to_player
i_r_french_regulation events horse_racing_purchase_event CHECK
NO NO i_r_french_regulation events
c_event_label_as_shown_to_player (((event_label_as_shown_to_player IS
NULL) OR is_valid_string(256, event_label_as_shown_to_player)))
i_r_french_regulation events c_race_type i_r_french_regulation
events horse_racing_purchase_event CHECK NO NO
i_r_french_regulation events c_race_type (is_valid_string(64,
race_type))
i_r_french_regulation events 16395_16531_1_not_null
i_r_french_regulation events horse_racing_purchase_event CHECK
NO NO i_r_french_regulation events 16395_16531_1_not_null id
IS NOT NULL
i_r_french_regulation events 16395_16531_2_not_null
i_r_french_regulation events horse_racing_purchase_event CHECK
NO NO i_r_french_regulation events 16395_16531_2_not_null
occurred_at IS NOT NULL
i_r_french_regulation events 16395_16531_3_not_null
i_r_french_regulation events horse_racing_purchase_event CHECK
NO NO i_r_french_regulation events 16395_16531_3_not_null
operator_id IS NOT NULL
i_r_french_regulation events 16395_16531_4_not_null
i_r_french_regulation events horse_racing_purchase_event CHECK
NO NO i_r_french_regulation events 16395_16531_4_not_null
player_id IS NOT NULL
i_r_french_regulation events 16395_16531_5_not_null
i_r_french_regulation events horse_racing_purchase_event CHECK
NO NO i_r_french_regulation events 16395_16531_5_not_null
player_ip IS NOT NULL
i_r_french_regulation events 16395_16531_6_not_null
i_r_french_regulation events horse_racing_purchase_event CHECK
NO NO i_r_french_regulation events 16395_16531_6_not_null
session_id IS NOT NULL
i_r_french_regulation events 16395_16531_7_not_null
i_r_french_regulation events horse_racing_purchase_event CHECK
NO NO i_r_french_regulation events 16395_16531_7_not_null
has_player_confirmation IS NOT NULL
i_r_french_regulation events 16395_16531_8_not_null
i_r_french_regulation events horse_racing_purchase_event CHECK
NO NO i_r_french_regulation events 16395_16531_8_not_null
purchase_id IS NOT NULL
i_r_french_regulation events 16395_16531_9_not_null
i_r_french_regulation events horse_racing_purchase_event CHECK
NO NO i_r_french_regulation events 16395_16531_9_not_null
event_label_as_shown_to_player IS NOT NULL
i_r_french_regulation events 16395_16531_18_not_null
i_r_french_regulation events horse_racing_purchase_event CHECK
NO NO i_r_french_regulation events 16395_16531_18_not_null
race_type IS NOT NULL
i_r_french_regulation events 16395_16531_19_not_null
i_r_french_regulation events horse_racing_purchase_event CHECK
NO NO i_r_french_regulation events 16395_16531_19_not_null
bet_name_as_shown_to_player IS NOT NULL
i_r_french_regulation events 16395_16531_20_not_null
i_r_french_regulation events horse_racing_purchase_event CHECK
NO NO i_r_french_regulation events 16395_16531_20_not_null
event_date IS NOT NULL
Best regards,
Hristo Ivanov
On Tue, Sep 18, 2018 at 11:16 AM, Hristo Ivanov <hristo.atanassov@gmail.com>
wrote:
Hello,
I am writing with regards to some wrong results I keep on receiving when
using check_constraints view from information_schema:(1) First, it shows constraints grouped by name, regardless of the
relation used: when having two constraints with the same name in different
tables, it shows both in both tables, regardless of their belonging;
Per the note here:
https://www.postgresql.org/docs/current/static/information-schema.html
You cannot use the standard information_schema.check_constraints in your
database because you have not conformed to the standard when naming your
constraints. Either ensure unique names for all constraints (in a schema)
or use pg_catalog.
(2) Second, it also lists NOT NULL constraints, even though they are not
created as check constraints.
That is has special syntax for its creation doesn't mean it isn't
functionally a check constraint...so on its face this seems OK.
David J.
"Hristo" == Hristo Ivanov <hristo.atanassov@gmail.com> writes:
Hristo> Hello,
Hristo> I am writing with regards to some wrong results I keep on
Hristo> receiving when using check_constraints view from
Hristo> information_schema:
Hristo> (1) First, it shows constraints grouped by name, regardless of
Hristo> the relation used: when having two constraints with the same
Hristo> name in different tables, it shows both in both tables,
Hristo> regardless of their belonging;
In the SQL spec, the name of a constraint is not local to the table; in
a given schema, the name must be unique. PostgreSQL does not enforce
this, and generally treats constraint names as local to a single
_table_; this is difficult to fix retroactively because it would make
old databases fail to restore if the spec's conditions were enforced.
https://wiki.postgresql.org/wiki/PostgreSQL_vs_SQL_Standard#Constraint_name_scope
Hristo> (2) Second, it also lists NOT NULL constraints, even though
Hristo> they are not created as check constraints.
This is required by the SQL spec, which treats NOT NULL as merely a
syntactic shorthand for CHECK(colname IS NOT NULL). But see also
which implies that for composite-type columns, NOT NULL and
CHECK(colname IS NOT NULL) actually have different semantics in PG.
--
Andrew (irc:RhodiumToad)
Hello guys,
Thanks for your fast replies.
2018-09-18 22:59 GMT+03:00 Andrew Gierth <andrew@tao11.riddles.org.uk>:
In the SQL spec, the name of a constraint is not local to the table; in
a given schema, the name must be unique. PostgreSQL does not enforce
this, and generally treats constraint names as local to a single
_table_; this is difficult to fix retroactively because it would make
old databases fail to restore if the spec's conditions were enforced.https://wiki.postgresql.org/wiki/PostgreSQL_vs_SQL_
Standard#Constraint_name_scope
I didn't know that constraint names had to be unique. Even if that is true,
I don't think returning wrong constraints in this case (belonging to a
different table) is the right thing to do. This means that PostgreSQL is
conforming to the standard in only places, while the dependencies are
clearly not standard compliant. Since the likelihood of fixing dependencies
is fairly small, I would suggest fixing the constraints selection behavior.
Hristo> (2) Second, it also lists NOT NULL constraints, even though
Hristo> they are not created as check constraints.
This is required by the SQL spec, which treats NOT NULL as merely a
syntactic shorthand for CHECK(colname IS NOT NULL). But see alsohttps://wiki.postgresql.org/wiki/PostgreSQL_vs_SQL_
Standard#NOT_NULL_constraints_on_composite-type_columnswhich implies that for composite-type columns, NOT NULL and
CHECK(colname IS NOT NULL) actually have different semantics in PG.
Fair enough. Could I suggest having a column to discriminate non-null
constraints from the rest? Like, named "not_null", to be either "yes" or
"no", or, better, simple Boolean?
FYI, the only solution I found to this problem, is:
select c.*
from pg_class t
join pg_tables a
on t.relname = a.tablename
join pg_constraint c
on c.conrelid = t.oid
where a.tablename = 'horse_racing_purchase_event'
and a.schemaname = 'events'
and c.contype = 'c'
This completely disregards the information_schema objects.
Looking forward to hearing from you.
Best regards,
Hristo Ivanov
Hristo Ivanov <hristo.atanassov@gmail.com> writes:
2018-09-18 22:59 GMT+03:00 Andrew Gierth <andrew@tao11.riddles.org.uk>:
In the SQL spec, the name of a constraint is not local to the table; in
a given schema, the name must be unique. PostgreSQL does not enforce
this, and generally treats constraint names as local to a single
_table_; this is difficult to fix retroactively because it would make
old databases fail to restore if the spec's conditions were enforced.
I didn't know that constraint names had to be unique. Even if that is true,
I don't think returning wrong constraints in this case (belonging to a
different table) is the right thing to do. This means that PostgreSQL is
conforming to the standard in only places, while the dependencies are
clearly not standard compliant. Since the likelihood of fixing dependencies
is fairly small, I would suggest fixing the constraints selection behavior.
It was already explained to you that we're not changing this. The
information_schema outputs conform to the spec as long as the inputs
(i.e., the set of constraint names created by your application) do.
It's not very plausible to insist on spec compliance for what you see
in information_schema when the violation is your own fault. Moreover,
the only thing we could do to make the situation more compliant would
be to enforce constraint name uniqueness schema-wide, which is not really
very desirable (on any metric other than blind standards compliance)
and would create major backwards-compatibility issues. So no, it's not
going to change.
Fair enough. Could I suggest having a column to discriminate non-null
constraints from the rest?
Not in the information_schema you can't :-(. The set of columns in
those views is dictated by the standard. Adding more would just be
another way of not being compliant.
FYI, the only solution I found to this problem, is: ...
This completely disregards the information_schema objects.
Yup, if you want to deal with non-standard-compliant objects or
situations, you generally need to ignore information_schema and
look directly at the catalogs.
regards, tom lane