Обсуждение: information_schema.check_constraints Inconsistencies

Поиск
Список
Период
Сортировка

information_schema.check_constraints Inconsistencies

От
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;
(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


Re: information_schema.check_constraints Inconsistencies

От
"David G. Johnston"
Дата:
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:


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.

Re: information_schema.check_constraints Inconsistencies

От
Andrew Gierth
Дата:
>>>>> "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)


Re: information_schema.check_constraints Inconsistencies

От
Hristo Ivanov
Дата:
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 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'
This completely disregards the information_schema objects.

Looking forward to hearing from you.

Best regards,
Hristo Ivanov

Re: information_schema.check_constraints Inconsistencies

От
Tom Lane
Дата:
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