From 520695701ca6ef6ad5e5a984a98f89b3ede31e3c Mon Sep 17 00:00:00 2001 From: Dave Cramer Date: Wed, 5 Jun 2002 19:12:01 +0000 Subject: [PATCH] fixed getImported/ExportedKeys to be simpler, and return the correct number of keys --- .../postgresql/jdbc2/DatabaseMetaData.java | 380 ++++++++++++------ .../test/jdbc2/DatabaseMetaDataTest.java | 8 +- 2 files changed, 271 insertions(+), 117 deletions(-) diff --git a/src/interfaces/jdbc/org/postgresql/jdbc2/DatabaseMetaData.java b/src/interfaces/jdbc/org/postgresql/jdbc2/DatabaseMetaData.java index e819d4b926..d8327f1544 100644 --- a/src/interfaces/jdbc/org/postgresql/jdbc2/DatabaseMetaData.java +++ b/src/interfaces/jdbc/org/postgresql/jdbc2/DatabaseMetaData.java @@ -15,7 +15,7 @@ import org.postgresql.util.PSQLException; /* * This class provides information about the database as a whole. * - * $Id: DatabaseMetaData.java,v 1.52 2002/04/16 13:28:44 davec Exp $ + * $Id: DatabaseMetaData.java,v 1.53 2002/06/05 19:12:01 davec Exp $ * *

Many of the methods here return lists of information in ResultSets. You * can use the normal ResultSet methods such as getString and getInt to @@ -760,8 +760,10 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData */ public boolean supportsANSI92EntryLevelSQL() throws SQLException { - Driver.debug("supportsANSI92EntryLevelSQL false "); - return false; + boolean schemas = connection.haveMinimumServerVersion("7.3"); + Driver.debug("supportsANSI92EntryLevelSQL " + schemas); + return schemas; + } /* @@ -941,8 +943,10 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData */ public boolean supportsSchemasInTableDefinitions() throws SQLException { - Driver.debug("supportsSchemasInTableDefinitions false"); - return false; + boolean schemas = connection.haveMinimumServerVersion("7.3"); + + Driver.debug("supportsSchemasInTableDefinitions " + schemas); + return schemas; } /* @@ -2410,6 +2414,71 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData " ORDER BY table_name, pk_name, key_seq" ); } +/* + SELECT + c.relname as primary, + c2.relname as foreign, + t.tgconstrname, + ic.relname as fkeyname, + af.attnum as fkeyseq, + ipc.relname as pkeyname, + ap.attnum as pkeyseq, + t.tgdeferrable, + t.tginitdeferred, + t.tgnargs,t.tgargs, + p1.proname as updaterule, + p2.proname as deleterule +FROM + pg_trigger t, + pg_trigger t1, + pg_class c, + pg_class c2, + pg_class ic, + pg_class ipc, + pg_proc p1, + pg_proc p2, + pg_index if, + pg_index ip, + pg_attribute af, + pg_attribute ap +WHERE + (t.tgrelid=c.oid + AND t.tgisconstraint + AND t.tgconstrrelid=c2.oid + AND t.tgfoid=p1.oid + and p1.proname like '%%upd') + + and + (t1.tgrelid=c.oid + and t1.tgisconstraint + and t1.tgconstrrelid=c2.oid + AND t1.tgfoid=p2.oid + and p2.proname like '%%del') + + AND c2.relname='users' + + AND + (if.indrelid=c.oid + AND if.indexrelid=ic.oid + and ic.oid=af.attrelid + AND if.indisprimary) + + and + (ip.indrelid=c2.oid + and ip.indexrelid=ipc.oid + and ipc.oid=ap.attrelid + and ip.indisprimary) + +*/ +/** + * + * @param catalog + * @param schema + * @param primaryTable if provided will get the keys exported by this table + * @param foreignTable if provided will get the keys imported by this table + * @return ResultSet + * @throws SQLException + */ private java.sql.ResultSet getImportedExportedKeys(String catalog, String schema, String primaryTable, String foreignTable) throws SQLException { @@ -2430,120 +2499,203 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData f[12] = new Field(connection, "PK_NAME", iVarcharOid, 32); f[13] = new Field(connection, "DEFERRABILITY", iInt2Oid, 2); - java.sql.ResultSet rs = connection.ExecSQL("SELECT c.relname,c2.relname," - + "t.tgconstrname,ic.relname," - + "t.tgdeferrable,t.tginitdeferred," - + "t.tgnargs,t.tgargs,p.proname " - + "FROM pg_trigger t,pg_class c,pg_class c2," - + "pg_class ic,pg_proc p, pg_index i " - + "WHERE t.tgrelid=c.oid AND t.tgconstrrelid=c2.oid " - + "AND t.tgfoid=p.oid AND tgisconstraint " - + ((primaryTable != null) ? "AND c.relname='" + primaryTable + "' " : "") - + ((foreignTable != null) ? "AND c2.relname='" + foreignTable + "' " : "") - + "AND i.indrelid=c.oid " - + "AND i.indexrelid=ic.oid AND i.indisprimary " - + "ORDER BY c.relname,c2.relname" - ); + java.sql.ResultSet rs = connection.ExecSQL( + "SELECT " + + "c.relname as prelname, " + + "c2.relname as frelname, " + + "t.tgconstrname, " + + "a.attnum as keyseq, " + + "ic.relname as fkeyname, " + + "t.tgdeferrable, " + + "t.tginitdeferred, " + + "t.tgnargs,t.tgargs, " + + "p1.proname as updaterule, " + + "p2.proname as deleterule " + + "FROM " + + "pg_trigger t, " + + "pg_trigger t1, " + + "pg_class c, " + + "pg_class c2, " + + "pg_class ic, " + + "pg_proc p1, " + + "pg_proc p2, " + + "pg_index i, " + + "pg_attribute a " + + "WHERE " + // isolate the update rule + + "(t.tgrelid=c.oid " + + "AND t.tgisconstraint " + + "AND t.tgconstrrelid=c2.oid " + + "AND t.tgfoid=p1.oid " + + "and p1.proname like '%%upd') " + + + "and " + // isolate the delete rule + + "(t1.tgrelid=c.oid " + + "and t1.tgisconstraint " + + "and t1.tgconstrrelid=c2.oid " + + "AND t1.tgfoid=p2.oid " + + "and p2.proname like '%%del') " + + // if we are looking for exported keys then primary table will be used + + ((primaryTable != null) ? "AND c.relname='" + primaryTable + "' " : "") + + // if we are looking for imported keys then the foreign table will be used + + ((foreignTable != null) ? "AND c2.relname='" + foreignTable + "' " : "") + + "AND i.indrelid=c.oid " + + "AND i.indexrelid=ic.oid " + + "AND ic.oid=a.attrelid " + + "AND i.indisprimary " + + "ORDER BY " + + // orderby is as follows getExported, orders by FKTABLE, + // getImported orders by PKTABLE + // getCrossReference orders by FKTABLE, so this should work for both, + // since when getting crossreference, primaryTable will be defined + + + (primaryTable != null ? "frelname" : "prelname") + ",keyseq"); + +// returns the following columns +// and some example data with a table defined as follows + +// create table people ( id int primary key); +// create table policy ( id int primary key); +// create table users ( id int primary key, people_id int references people(id), policy_id int references policy(id)) + +// prelname | frelname | tgconstrname | keyseq | fkeyName | tgdeferrable | tginitdeferred +// 1 | 2 | 3 | 4 | 5 | 6 | 7 + +// people | users | | 1 | people_pkey | f | f + +// | tgnargs | tgargs | updaterule | deleterule +// | 8 | 9 | 10 | 11 +// | 6 | \000users\000people\000UNSPECIFIED\000people_id\000id\000 | RI_FKey_noaction_upd | RI_FKey_noaction_del + Vector tuples = new Vector(); - short seq = 0; - if (rs.next()) - { - boolean hasMore; - do - { - byte tuple[][] = new byte[14][0]; - for (int k = 0;k < 14;k++) - tuple[k] = null; - String fKeyName = rs.getString(3); - boolean foundRule = false; - do - { - String proname = rs.getString(9); - if (proname != null && proname.startsWith("RI_FKey_")) - { - int col = -1; - if (proname.endsWith("_upd")) - col = 9; // UPDATE_RULE - else if (proname.endsWith("_del")) - col = 10; // DELETE_RULE - if (col > -1) - { - String rule = proname.substring(8, proname.length() - 4); - int action = importedKeyNoAction; - if ("cascade".equals(rule)) - action = importedKeyCascade; - else if ("setnull".equals(rule)) - action = importedKeySetNull; - else if ("setdefault".equals(rule)) - action = importedKeySetDefault; - tuple[col] = Integer.toString(action).getBytes(); - - if (!foundRule) - { - tuple[2] = rs.getBytes(1); //PKTABLE_NAME - tuple[6] = rs.getBytes(2); //FKTABLE_NAME - - // Parse the tgargs data - StringBuffer fkeyColumns = new StringBuffer(); - StringBuffer pkeyColumns = new StringBuffer(); - int numColumns = (rs.getInt(7) >> 1) - 2; - String s = rs.getString(8); - int pos = s.lastIndexOf("\\000"); - for (int c = 0;c < numColumns;c++) - { - if (pos > -1) - { - int pos2 = s.lastIndexOf("\\000", pos - 1); - if (pos2 > -1) - { - if (pkeyColumns.length() > 0) - pkeyColumns.insert(0, ','); - pkeyColumns.insert(0, s.substring(pos2 + 4, pos)); //PKCOLUMN_NAME - pos = s.lastIndexOf("\\000", pos2 - 1); - if (pos > -1) - { - if (fkeyColumns.length() > 0) - fkeyColumns.insert(0, ','); - fkeyColumns.insert(0, s.substring(pos + 4, pos2)); //FKCOLUMN_NAME - } - } - } - } - tuple[3] = pkeyColumns.toString().getBytes(); //PKCOLUMN_NAME - tuple[7] = fkeyColumns.toString().getBytes(); //FKCOLUMN_NAME - - tuple[8] = Integer.toString(seq++).getBytes(); //KEY_SEQ - tuple[11] = fKeyName.getBytes(); //FK_NAME - tuple[12] = rs.getBytes(4); //PK_NAME - - // DEFERRABILITY - int deferrability = importedKeyNotDeferrable; - boolean deferrable = rs.getBoolean(5); - boolean initiallyDeferred = rs.getBoolean(6); - if (deferrable) - { - if (initiallyDeferred) - deferrability = importedKeyInitiallyDeferred; - else - deferrability = importedKeyInitiallyImmediate; - } - tuple[13] = Integer.toString(deferrability).getBytes(); - - foundRule = true; - } - } - } - } - while ((hasMore = rs.next()) && fKeyName.equals(rs.getString(3))); - if(foundRule) tuples.addElement(tuple); + while ( rs.next() ) + { + byte tuple[][] = new byte[14][]; - } - while (hasMore); - } + tuple[2] = rs.getBytes(1); //PKTABLE_NAME + tuple[6] = rs.getBytes(2); //FKTABLE_NAME + String fKeyName = rs.getString(3); + String updateRule = rs.getString(10); - return new ResultSet(connection, f, tuples, "OK", 1); + if (updateRule != null ) + { + // Rules look like this RI_FKey_noaction_del so we want to pull out the part between the 'Key_' and the last '_' s + + String rule = updateRule.substring(8, updateRule.length() - 4); + + int action = importedKeyNoAction; + + if ( rule == null || "noaction".equals(rule) ) + action = importedKeyNoAction; + if ("cascade".equals(rule)) + action = importedKeyCascade; + else if ("setnull".equals(rule)) + action = importedKeySetNull; + else if ("setdefault".equals(rule)) + action = importedKeySetDefault; + else if ("restrict".equals(rule)) + action = importedKeyRestrict; + + tuple[9] = Integer.toString(action).getBytes(); + + } + + String deleteRule = rs.getString(11); + + if ( deleteRule != null ) + { + + String rule = updateRule.substring(8, updateRule.length() - 4); + + int action = importedKeyNoAction; + if ("cascade".equals(rule)) + action = importedKeyCascade; + else if ("setnull".equals(rule)) + action = importedKeySetNull; + else if ("setdefault".equals(rule)) + action = importedKeySetDefault; + tuple[10] = Integer.toString(action).getBytes(); + } + + + // Parse the tgargs data + StringBuffer fkeyColumns = new StringBuffer(); + StringBuffer pkeyColumns = new StringBuffer(); + + + // Note, I am guessing at most of this, but it should be close + // if not, please correct + // the keys are in pairs and start after the first four arguments + // the arguments are seperated by \000 + + int numColumns = (rs.getInt(8) >> 1) - 2; + + + + // get the args + String targs = rs.getString(9); + + // start parsing from the end + int pos = targs.lastIndexOf("\\000"); + + for (int c = 0;c < numColumns;c++) + { + // this should never be, since we should never get to the beginning of the string + // as the number of columns should override this, but it is a safe test + if (pos > -1) + { + int pos2 = targs.lastIndexOf("\\000", pos - 1); + if (pos2 > -1) + { + // seperate the pkColumns by ',' s + if (pkeyColumns.length() > 0) + pkeyColumns.insert(0, ','); + + // extract the column name out 4 characters ahead essentially removing the /000 + pkeyColumns.insert(0, targs.substring(pos2 + 4, pos)); //PKCOLUMN_NAME + + // now find the associated fkColumn + pos = targs.lastIndexOf("\\000", pos2 - 1); + if (pos > -1) + { + if (fkeyColumns.length() > 0) + fkeyColumns.insert(0, ','); + fkeyColumns.insert(0, targs.substring(pos + 4, pos2)); //FKCOLUMN_NAME + } + } + } + } + + tuple[3] = pkeyColumns.toString().getBytes(); //PKCOLUMN_NAME + tuple[7] = fkeyColumns.toString().getBytes(); //FKCOLUMN_NAME + + tuple[8] = rs.getBytes(4); //KEY_SEQ + tuple[11] = rs.getBytes(5); //FK_NAME + tuple[12] = rs.getBytes(3); //PK_NAME + + // DEFERRABILITY + int deferrability = importedKeyNotDeferrable; + boolean deferrable = rs.getBoolean(6); + boolean initiallyDeferred = rs.getBoolean(7); + if (deferrable) + { + if (initiallyDeferred) + deferrability = importedKeyInitiallyDeferred; + else + deferrability = importedKeyInitiallyImmediate; + } + tuple[13] = Integer.toString(deferrability).getBytes(); + + tuples.addElement(tuple); + } + + return new ResultSet(connection, f, tuples, "OK", 1); } /* diff --git a/src/interfaces/jdbc/org/postgresql/test/jdbc2/DatabaseMetaDataTest.java b/src/interfaces/jdbc/org/postgresql/test/jdbc2/DatabaseMetaDataTest.java index 10502279c3..e6acfc0d70 100644 --- a/src/interfaces/jdbc/org/postgresql/test/jdbc2/DatabaseMetaDataTest.java +++ b/src/interfaces/jdbc/org/postgresql/test/jdbc2/DatabaseMetaDataTest.java @@ -9,7 +9,7 @@ import java.sql.*; * * PS: Do you know how difficult it is to type on a train? ;-) * - * $Id: DatabaseMetaDataTest.java,v 1.7 2002/05/30 16:39:26 davec Exp $ + * $Id: DatabaseMetaDataTest.java,v 1.8 2002/06/05 19:12:01 davec Exp $ */ public class DatabaseMetaDataTest extends TestCase @@ -235,6 +235,7 @@ public class DatabaseMetaDataTest extends TestCase Connection con1 = JDBC2Tests.openDB(); JDBC2Tests.createTable( con1, "people", "id int4 primary key, name text" ); JDBC2Tests.createTable( con1, "policy", "id int4 primary key, name text" ); + JDBC2Tests.createTable( con1, "users", "id int4 primary key, people_id int4, policy_id int4,"+ "CONSTRAINT people FOREIGN KEY (people_id) references people(id),"+ "constraint policy FOREIGN KEY (policy_id) references policy(id)" ); @@ -261,9 +262,10 @@ public class DatabaseMetaDataTest extends TestCase assertTrue( fkColumnName.equals( "people_id" ) || fkColumnName.equals( "policy_id" ) ) ; String fkName = rs.getString( "FK_NAME" ); - assertTrue( fkName.equals( "people") || fkName.equals( "policy" ) ); + assertTrue( fkName.equals( "people_pkey") || fkName.equals( "policy_pkey" ) ); String pkName = rs.getString( "PK_NAME" ); +// assertTrue( pkName.equals("users") ); } @@ -280,7 +282,7 @@ public class DatabaseMetaDataTest extends TestCase assertTrue( rs.getString( "FKTABLE_NAME" ).equals( "users" ) ); assertTrue( rs.getString( "FKCOLUMN_NAME" ).equals( "people_id" ) ); - assertTrue( rs.getString( "FK_NAME" ).equals( "people" ) ); + assertTrue( rs.getString( "FK_NAME" ).equals( "people_pkey" ) ); JDBC2Tests.dropTable( con1, "users" ); -- 2.40.0