Thread: Getting FK relationships from information_schema
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
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
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 />
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
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.
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