Commit 52069570 authored by Dave Cramer's avatar Dave Cramer

fixed getImported/ExportedKeys to be simpler, and return the correct number of keys

parent 40690c12
......@@ -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 $
*
* <p>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,97 +2499,190 @@ 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 "
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 i.indisprimary "
+ "ORDER BY c.relname,c2.relname"
);
+ "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 | <unnamed> | 1 | people_pkey | f | f
// | tgnargs | tgargs | updaterule | deleterule
// | 8 | 9 | 10 | 11
// | 6 | <unnamed>\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
while ( rs.next() )
{
byte tuple[][] = new byte[14][0];
for (int k = 0;k < 14;k++)
tuple[k] = null;
byte tuple[][] = new byte[14][];
tuple[2] = rs.getBytes(1); //PKTABLE_NAME
tuple[6] = rs.getBytes(2); //FKTABLE_NAME
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 updateRule = rs.getString(10);
if (updateRule != null )
{
String rule = proname.substring(8, proname.length() - 4);
// 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;
tuple[col] = Integer.toString(action).getBytes();
else if ("restrict".equals(rule))
action = importedKeyRestrict;
tuple[9] = Integer.toString(action).getBytes();
}
String deleteRule = rs.getString(11);
if (!foundRule)
if ( deleteRule != null )
{
tuple[2] = rs.getBytes(1); //PKTABLE_NAME
tuple[6] = rs.getBytes(2); //FKTABLE_NAME
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();
int numColumns = (rs.getInt(7) >> 1) - 2;
String s = rs.getString(8);
int pos = s.lastIndexOf("\\000");
// 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 = s.lastIndexOf("\\000", pos - 1);
int pos2 = targs.lastIndexOf("\\000", pos - 1);
if (pos2 > -1)
{
// seperate the pkColumns by ',' s
if (pkeyColumns.length() > 0)
pkeyColumns.insert(0, ',');
pkeyColumns.insert(0, s.substring(pos2 + 4, pos)); //PKCOLUMN_NAME
pos = s.lastIndexOf("\\000", pos2 - 1);
// 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, s.substring(pos + 4, pos2)); //FKCOLUMN_NAME
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] = Integer.toString(seq++).getBytes(); //KEY_SEQ
tuple[11] = fKeyName.getBytes(); //FK_NAME
tuple[12] = rs.getBytes(4); //PK_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(5);
boolean initiallyDeferred = rs.getBoolean(6);
boolean deferrable = rs.getBoolean(6);
boolean initiallyDeferred = rs.getBoolean(7);
if (deferrable)
{
if (initiallyDeferred)
......@@ -2530,17 +2692,7 @@ public class DatabaseMetaData implements java.sql.DatabaseMetaData
}
tuple[13] = Integer.toString(deferrability).getBytes();
foundRule = true;
}
}
}
}
while ((hasMore = rs.next()) && fKeyName.equals(rs.getString(3)));
if(foundRule) tuples.addElement(tuple);
}
while (hasMore);
tuples.addElement(tuple);
}
return new ResultSet(connection, f, tuples, "OK", 1);
......
......@@ -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" );
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment