PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Valeurs NULL pour les colonnes referential_constraints.unique_constraint_* dans le schéma d'informations

Configuration du test

Vous assumez le nom de la contrainte test_def_abc_id_fkey , le nom par défaut résultant de votre configuration dans Postgres 11 ou une version antérieure. Il convient de noter, cependant, que les noms par défaut ont été améliorés pour Postgres 12, où la même configuration aboutit à test_def_abc_id_abc_id2_fkey . Les notes de version de Postgres 12 :

Voir :

db<>violon ici

Utilisons donc le nom explicite test_def_abc_fkey pour la contrainte FK afin d'éviter toute confusion :

CREATE TABLE test_abc (
  pk  int PRIMARY KEY
, id  int NOT NULL
, id2 int NOT NULL
);

CREATE UNIQUE INDEX test_abc_ids ON test_abc(id,id2);

CREATE TABLE test_def (
  id      int PRIMARY KEY
, abc_id  int
, abc_id2 int
, CONSTRAINT test_def_abc_fkey  -- !
     FOREIGN KEY (abc_id,abc_id2) REFERENCES test_abc(id,id2)
);

Et cela fonctionne dans Postgres 9.5 - Postgres 12.
Même dans Postgres 9.3.
(J'avais eu la mauvaise impression d'une réelle contrainte serait nécessaire.)

Répondre

Votre observation en interrogeant le schéma d'information tient :

SELECT *
FROM   information_schema.referential_constraints
WHERE  constraint_name = 'test_def_abc_fkey';  -- unequivocal name

Nous obtenons une ligne, mais les trois champs unique_constraint_catalog , unique_constraint_schema et unique_constraint_name sont NULL .

L'explication paraît simple. Ces colonnes décrivent, comme le dit le manuel :

Mais il n'y a pas de UNIQUE contrainte , juste un UNIQUE index . Un UNIQUE contrainte est implémentée en utilisant un UNIQUE index dans Postgres. Les contraintes sont définies par le standard SQL, les index sont des détails d'implémentation. Il existe des différences comme celle que vous avez découverte. Connexe :

Le même test avec un réel UNIQUE contrainte affiche les données comme prévu :

db<>violon ici

Cela semble donc logique. D'autant plus que le schéma d'information est également défini par le comité des normes SQL et les index ne sont pas standardisés, seulement des contraintes. (Aucune information d'index dans les vues de schéma d'information.)

Tout est clair? Pas tout à fait.

Cependant

Il existe une autre vue de schéma d'informations key_column_usage . Sa dernière colonne est décrite comme :

Gras l'accent est mis sur le mien. Ici, la position ordinale de la colonne dans l'index est répertorié de toute façon :

SELECT *
FROM   information_schema.key_column_usage
WHERE  constraint_name = 'test_def_abc_fkey';

Voir :

db<>violon ici

Semble incohérent.

Pire encore, le manuel prétend qu'une PRIMARY KEY réelle ou UNIQUE contrainte serait nécessaire pour la création d'une FOREIGN KEY contrainte :

Semble être un bogue de documentation ? Si personne ne peut indiquer où je me trompe ici, je déposerai un rapport de bogue.

Connexe :

Solution

Dans Postgres, le catalogue système est la véritable source de vérité. Voir :

Vous pouvez donc utiliser quelque chose comme ça (comme j'ai aussi ajouté dans le violon ci-dessus):

SELECT c.conname
     , c.conrelid::regclass  AS fk_table, k1.fk_columns
     , c.confrelid::regclass AS ref_table, k2.ref_key_columns
FROM   pg_catalog.pg_constraint c
LEFT   JOIN LATERAL (
   SELECT ARRAY (
      SELECT a.attname
      FROM   pg_catalog.pg_attribute a
           , unnest(c.conkey) WITH ORDINALITY AS k(attnum, ord)
      WHERE  a.attrelid = c.conrelid
      AND    a.attnum = k.attnum
      ORDER  BY k.ord
      ) AS fk_columns
   ) k1 ON true
LEFT   JOIN LATERAL (
   SELECT ARRAY (
      SELECT a.attname
      FROM   pg_catalog.pg_attribute a
           , unnest(c.confkey) WITH ORDINALITY AS k(attnum, ord)
      WHERE  a.attrelid = c.confrelid
      AND    a.attnum = k.attnum
      ORDER  BY k.ord
      ) AS ref_key_columns
   ) k2 ON true
WHERE  conname = 'test_def_abc_fkey';

Renvoie :

conname           | fk_table | fk_columns       | ref_table | ref_key_columns
:---------------- | :------- | :--------------- | :-------- | :--------------
test_def_abc_fkey | test_def | {abc_id,abc_id2} | test_abc  | {id,id2}       

Connexe :