Thread: 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'
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
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)
;
(
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
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
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:
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 https://wiki.postgresql.org/wiki/PostgreSQL_vs_SQL_Standard#NOT_NULL_constraints_on_composite-type_columns 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>:
This completely disregards the information_schema objects.
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 also
https://wiki.postgresql.org/wiki/PostgreSQL_vs_SQL_ Standard#NOT_NULL_constraints_ on_composite-type_columns
which 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'
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_
and a.schemaname = 'events'
and c.contype = 'c'
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