Getting FK relationships from information_schema - Mailing list pgsql-sql

From Kyle
Subject Getting FK relationships from information_schema
Date
Msg-id 40C500D8.9060009@actarg.com
Whole thread Raw
Responses Re: Getting FK relationships from information_schema
List pgsql-sql
I'm trying to get my application to deduce foreign key relationships 
automatically so it can perform appropriate joins for the user.  I'm new 
to information_schema and having problems getting what I want.  Here is 
a test script to be run on a database called "test."
------------------------------------------------------------------------------------
-- Demonstrate issues with information_schema

create table empl (   empl_pk     int4    primary key,   name        varchar
);

create table empl_addr (   empl_k      int4    references empl,   type        varchar,   address     varchar,   primary
key(empl_k, type)
 
);

create table doc (   doc_pk      int4    primary key,   name        varchar
);

create table empl_doc (   empl_k      int4,   doc_k       int4,
   primary key (empl_k,doc_k),   foreign key (empl_k) references empl,   foreign key (doc_k) references doc
);

create table doc_empl (   doc_k       int4,   empl_k      int4,
   primary key (doc_k,empl_k),   foreign key (doc_k) references doc,   foreign key (empl_k) references empl
);

-- Show info about the tables' primary keys
select   tc.table_name,   tc.constraint_type,   cu.column_name,   cu.ordinal_position
   from   information_schema.key_column_usage cu,   information_schema.table_constraints tc
   where       cu.constraint_name = tc.constraint_name   and         cu.table_name = tc.table_name
   and         tc.constraint_type = 'PRIMARY KEY'
   and         tc.table_catalog = 'test'   and         tc.table_schema = 'public'   order by 2
;

-- Show info about the tables' foreign keys
select   tc.table_name,   tc.constraint_type,   cu.column_name,   cu.ordinal_position
   from   information_schema.key_column_usage cu,   information_schema.table_constraints tc
   where       cu.constraint_name = tc.constraint_name   and         cu.table_name = tc.table_name
   and         tc.constraint_type = 'FOREIGN KEY'
   and         tc.table_catalog = 'test'   and         tc.table_schema = 'public'   order by 2
;

-- Show constraints for our table
select   constraint_name,   table_name,   constraint_type, is_deferrable, initially_deferred
   from information_schema.table_constraints
   order by 2,3,1
;

-- Show foreign key constraints
select
--    *   constraint_name,   unique_constraint_name,   match_option,update_rule,delete_rule
   from information_schema.referential_constraints
   order by 2,1
;
------------------------------------------------------------------------------------
This produces the following output:

CREATE TABLE
CREATE TABLE
CREATE TABLE
CREATE TABLE
CREATE TABLEtable_name | constraint_type | column_name | ordinal_position
------------+-----------------+-------------+------------------empl       | PRIMARY KEY     | empl_pk     |
  1empl_addr  | PRIMARY KEY     | empl_k      |                1empl_addr  | PRIMARY KEY     | type        |
   2doc        | PRIMARY KEY     | doc_pk      |                1empl_doc   | PRIMARY KEY     | empl_k      |
    1empl_doc   | PRIMARY KEY     | doc_k       |                2doc_empl   | PRIMARY KEY     | doc_k       |
     1doc_empl   | PRIMARY KEY     | empl_k      |                2
 
(8 rows)
table_name | constraint_type | column_name | ordinal_position
------------+-----------------+-------------+------------------empl_addr  | FOREIGN KEY     | empl_k      |
  1empl_doc   | FOREIGN KEY     | empl_k      |                1empl_doc   | FOREIGN KEY     | doc_k       |
   1doc_empl   | FOREIGN KEY     | doc_k       |                1doc_empl   | FOREIGN KEY     | empl_k      |
    1
 
(5 rows)
constraint_name | table_name | constraint_type | is_deferrable | 
initially_deferred
-----------------+------------+-----------------+---------------+--------------------doc_pkey        | doc        |
PRIMARYKEY     | NO            | NO$1              | doc_empl   | FOREIGN KEY     | NO            | NO$2              |
doc_empl  | FOREIGN KEY     | NO            | NOdoc_empl_pkey   | doc_empl   | PRIMARY KEY     | NO            |
NOempl_pkey      | empl       | PRIMARY KEY     | NO            | NO$1              | empl_addr  | FOREIGN KEY     | NO
          | NOempl_addr_pkey  | empl_addr  | PRIMARY KEY     | NO            | NO$1              | empl_doc   | FOREIGN
KEY    | NO            | NO$2              | empl_doc   | FOREIGN KEY     | NO            | NOempl_doc_pkey   |
empl_doc  | PRIMARY KEY     | NO            | NO
 
(10 rows)
constraint_name | unique_constraint_name | match_option | update_rule | 
delete_rule
-----------------+------------------------+--------------+-------------+-------------$1              | doc_pkey
     | NONE         | NO ACTION   | 
 
NO ACTION$2              | doc_pkey               | NONE         | NO ACTION   | 
NO ACTION$1              | empl_pkey              | NONE         | NO ACTION   | 
NO ACTION$1              | empl_pkey              | NONE         | NO ACTION   | 
NO ACTION$2              | empl_pkey              | NONE         | NO ACTION   | 
NO ACTION
(5 rows)

I can determine all the primary key fields nicely, and I can tell what 
fields are foreign keys.  The problem is, I can't determine where the 
foreign keys are pointing.  The problem is, the constraint names ($1, 
$2, etc.) are not unique so I don't know how to join the third query 
into the fourth.

For example, if I want to know about the foreign keys for empl_doc, I 
can determine from query 3 that there are two foreign key constraints on 
that table and they are called $1 and $2.  From query 4, I should be 
able to see what primary key constraints they point to, except there are 
3 $1's and 2 $2's in that table so I can't really see where they point.  
(Notice that the third and fourth records in the last query are identical!)

Should I be looking somehow else in information_schema to see where 
foreign keys link?  Or am I correct that either:

1. The automatically generated FK constraint_name should be guaranteed 
to be unique; or
2. There should be an additional column in the last query somewhere to 
tell me what table the FK reference is coming from.

Kyle



pgsql-sql by date:

Previous
From: Marcus Whitney
Date:
Subject: Re: pl/pgsql and transaction locks
Next
From: "Brian G. Huber"
Date:
Subject: Cursor returned from procedure ignores setFetchSize() on CallableStatement