Thread: Getting FK relationships from information_schema

Getting FK relationships from information_schema

From
Kyle
Date:
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



Re: Getting FK relationships from information_schema

From
Tom Lane
Date:
Kyle <kyle@actarg.com> writes:
> 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.
> ...
> 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.

Hmm, this is messy :-(.  The SQL spec requires constraint names to be
unique within a schema.  Postgres doesn't require them to be unique even
within a table.  We were aware that there were some compatibility issues
there, but I hadn't realized that the information_schema design is
fundamentally dependent on the assumption of schema-wide uniqueness for
these names.

For a number of reasons (backwards compatibility being the hardest to
argue with), adopting the spec's restriction on constraint names seems
unlikely to happen.  You could of course follow it within your own
database designs, but I don't foresee Postgres enforcing it on
everyone.

In the short run I think your only answer is to dig deeper than
information_schema and look directly at the Postgres catalogs.
In the long run it'd be nice to have a cleaner answer, but I'm not
sure what it ought to look like.  Can we get away with adding
implementation-specific columns to information_schema tables?
If not, what other alternatives are there?
        regards, tom lane


Re: Getting FK relationships from information_schema

From
Kyle
Date:
Tom Lane wrote: <blockquote cite="mid26677.1086673982@sss.pgh.pa.us" type="cite"><pre wrap="">Kyle <a
class="moz-txt-link-rfc2396E"href="mailto:kyle@actarg.com"><kyle@actarg.com></a> writes: </pre><blockquote
type="cite"><prewrap="">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.   </pre></blockquote><pre wrap="">
Hmm, this is messy :-(.  The SQL spec requires constraint names to be
unique within a schema.  Postgres doesn't require them to be unique even
within a table.  </pre></blockquote> I think this is only an issue when the user relies on postgres to choose a
constraintname automatically.  Seems like a reasonable approach would be to have postgres choose a name for the
constraintthat happens to be unique in the schema (like tablename_fkey_$1).  Then if the user wants to make named
constraintsthat all have the same name, he can (and information_schema will be less useful) or he can rely on the
automaticallygenerated names to be a bit more descriptive in information_schema.<br /><blockquote
cite="mid26677.1086673982@sss.pgh.pa.us"type="cite"><pre wrap="">Can we get away with adding
 
implementation-specific columns to information_schema tables?
If not, what other alternatives are there?
 </pre></blockquote> Another possible approach: Does the constraint name showing up in information_schema really have
tobe the _real_ name?  Or can we just make a name consisting of the table name appended to the internal postgres
constraintname.<br /><br /> I think I like this less than the first idea.<br /><br /> Kyle<br /><br /> 

Re: Getting FK relationships from information_schema

From
Tom Lane
Date:
Kyle <kyle@actarg.com> writes:
> I think this is only an issue when the user relies on postgres to choose 
> a constraint name automatically.  Seems like a reasonable approach would 
> be to have postgres choose a name for the constraint that happens to be 
> unique in the schema (like tablename_fkey_$1).

We have discussed changing the default names of FK constraints before.
I have no problem with doing something like the above --- any objection
out there?

(Of course, this is only a long-term fix for your original problem,
as it'll take a good long while for any such naming change to propagate
to Joe's-Corner-Bar's database.)
        regards, tom lane


Re: Getting FK relationships from information_schema

From
Peter Eisentraut
Date:
Tom Lane wrote:
> Kyle <kyle@actarg.com> writes:
> > I think this is only an issue when the user relies on postgres to
> > choose a constraint name automatically.  Seems like a reasonable
> > approach would be to have postgres choose a name for the constraint
> > that happens to be unique in the schema (like tablename_fkey_$1).
>
> We have discussed changing the default names of FK constraints
> before. I have no problem with doing something like the above --- any
> objection out there?

I think it's a good idea.  It will also make the error messages of the 
kind "foreign key $1 violated" a bit clearer by default.

There will, however, be complaints that the constraint names are not 
automatically renamed with the table; but we are used to those by now.



Re: Getting FK relationships from information_schema

From
Tom Lane
Date:
Peter Eisentraut <peter_e@gmx.net> writes:
> Tom Lane wrote:
>> We have discussed changing the default names of FK constraints
>> before. I have no problem with doing something like the above --- any
>> objection out there?

> I think it's a good idea.  It will also make the error messages of the 
> kind "foreign key $1 violated" a bit clearer by default.

Right ho --- I'm working on a patch that will choose default names
of the form table_column_checkN and table_column_fkeyN, similar to
what we already do for index constraints.
        regards, tom lane