Repository: xerial/sqlite-jdbc
Issue: DatabaseMetaData getImportedKeys does not return primary keys or foreign key names

DatabaseMetaData `getImportedKeys` does not return primary keys or foreign key names

Changed files:
src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java, src/test/java/org/sqlite/DBMetaDataTest.java

diff --git a/src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java b/src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java
index 4b453ec..fb2d658 100644
--- a/src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java
+++ b/src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java
@@ -10,8 +10,10 @@ import java.sql.Statement;
 import java.sql.Struct;
 import java.sql.Types;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.Properties;
 import java.util.regex.Matcher;
@@ -20,6 +22,7 @@ import java.util.regex.Pattern;
 import org.sqlite.SQLiteConnection;
 import org.sqlite.core.Codes;
 import org.sqlite.core.CoreStatement;
+import org.sqlite.jdbc3.JDBC3DatabaseMetaData.ImportedKeyFinder.ForeignKey;
 import org.sqlite.util.StringUtils;
 
 public abstract class JDBC3DatabaseMetaData extends org.sqlite.core.CoreDatabaseMetaData {
@@ -1417,7 +1420,7 @@ public abstract class JDBC3DatabaseMetaData extends org.sqlite.core.CoreDatabase
      * Pattern used to extract a named primary key.
      */
      protected final static Pattern FK_NAMED_PATTERN =
-        Pattern.compile(".*\\\\sCONSTRAINT\\\\s+(.*?)\\\\s*FOREIGN\\\\s+KEY\\\\s*\\\\((.*?)\\\\).*",
+        Pattern.compile("\\\\sCONSTRAINT\\\\s+(.*?)\\\\s*FOREIGN\\\\s+KEY\\\\s*\\\\((.*?)\\\\)",
             Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
 
      /**
@@ -1434,6 +1437,7 @@ public abstract class JDBC3DatabaseMetaData extends org.sqlite.core.CoreDatabase
 
         StringBuilder exportedKeysQuery = new StringBuilder(512);
 
+        String target = null;
         int count = 0;
         if (pkColumns != null) {
             // retrieve table list
@@ -1441,13 +1445,18 @@ public abstract class JDBC3DatabaseMetaData extends org.sqlite.core.CoreDatabase
             ArrayList<String> tableList = new ArrayList<String>();
 
             while (rs.next()) {
-                tableList.add(rs.getString(1));
+            	String tblname = rs.getString(1);
+                tableList.add(tblname);
+                if (tblname.equalsIgnoreCase(table)) {
+                	// get the correct case as in the database
+                	// (not uppercase nor lowercase)
+                	target = tblname;
+                }
             }
 
             rs.close();
 
             ResultSet fk = null;
-            String target = table.toLowerCase();
             // find imported keys for each table
             for (String tbl : tableList) {
                 try {
@@ -1459,45 +1468,52 @@ public abstract class JDBC3DatabaseMetaData extends org.sqlite.core.CoreDatabase
                     throw e;
                 }
 
-                Statement stat2 = null;
                 try {
-                    stat2 = conn.createStatement();
-
+                	final ImportedKeyFinder impFkFinder = new ImportedKeyFinder(tbl);
+                	List<ForeignKey> fkNames = impFkFinder.getFkList();  
+                	
+                	int i = 0;
                     while(fk.next()) {
                         int keySeq = fk.getInt(2) + 1;
-                        String PKTabName = fk.getString(3).toLowerCase();
+                        String PKTabName = fk.getString(3);
 
-                        if (PKTabName == null || !PKTabName.equals(target)) {
+                        if (PKTabName == null || !PKTabName.equalsIgnoreCase(target)) {
                             continue;
                         }
 
                         String PKColName = fk.getString(5);
-                        PKColName = (PKColName == null) ? pkColumns[0] : PKColName.toLowerCase();
-
+                        PKColName = (PKColName == null) ? "" : PKColName;
+
+                        boolean usePkName = false;
+                        for (int j = 0; j < pkColumns.length; j++) {
+							if (pkColumns[j] != null && pkColumns[j].equalsIgnoreCase(PKColName)) {
+								usePkName = true;
+								break;
+							}
+						}
+                        String pkName = (usePkName && pkFinder.getName() != null)? pkFinder.getName(): "";
+                        	
                         exportedKeysQuery
                             .append(count > 0 ? " union all select " : "select ")
-                            .append(Integer.toString(keySeq)).append(" as ks, lower('")
-                            .append(escape(tbl)).append("') as fkt, lower('")
-                            .append(escape(fk.getString(4))).append("') as fcn, '")
-                            .append(escape(PKColName)).append("' as pcn, ")
+                            .append(Integer.toString(keySeq)).append(" as ks, '")
+                            .append(escape(tbl)).append("' as fkt, '")
+                            .append(escape(fk.getString(4))).append("' as fcn, '")
+                            .append(escape(PKColName)).append("' as pcn, '")
+                            .append(escape(pkName)).append("' as pkn, ")
                             .append(RULE_MAP.get(fk.getString(6))).append(" as ur, ")
                             .append(RULE_MAP.get(fk.getString(7))).append(" as dr, ");
 
-                        rs = stat2.executeQuery("select sql from sqlite_master where" +
-                            " lower(name) = lower('" + escape(tbl) + "')");
-
-                        if (rs.next()) {
-                            Matcher matcher = FK_NAMED_PATTERN.matcher(rs.getString(1));
-
-                            if (matcher.find()){
-                                exportedKeysQuery.append("'").append(escape(matcher.group(1).toLowerCase())).append("' as fkn");
-                            }
-                            else {
-                                exportedKeysQuery.append("'' as fkn");
-                            }
+                        String fkName = null;
+                        if (fkNames.size() > i) fkName = fkNames.get(i).getFkName();
+                        
+                        if (fkName != null){
+                            exportedKeysQuery.append("'").append(escape(fkName)).append("' as fkn");
                         }
-
-                        rs.close();
+                        else {
+                            exportedKeysQuery.append("'' as fkn");
+                        }
+                        
+                        i++;
                         count++;
                     }
                 }
@@ -1505,9 +1521,6 @@ public abstract class JDBC3DatabaseMetaData extends org.sqlite.core.CoreDatabase
                     try{
                         if (rs != null) rs.close();
                     }catch(SQLException e) {}
-                    try{
-                        if (stat2 != null) stat2.close();
-                    }catch(SQLException e) {}
                     try{
                         if (fk != null) fk.close();
                     }catch(SQLException e) {}
@@ -1517,10 +1530,10 @@ public abstract class JDBC3DatabaseMetaData extends org.sqlite.core.CoreDatabase
 
         boolean hasImportedKey = (count > 0);
         StringBuilder sql = new StringBuilder(512);
-        sql.append("select ")
+		sql.append("select ")
             .append(catalog).append(" as PKTABLE_CAT, ")
             .append(schema).append(" as PKTABLE_SCHEM, ")
-            .append(quote(table)).append(" as PKTABLE_NAME, ")
+            .append(quote(target)).append(" as PKTABLE_NAME, ")
             .append(hasImportedKey ? "pcn" : "''").append(" as PKCOLUMN_NAME, ")
             .append(catalog).append(" as FKTABLE_CAT, ")
             .append(schema).append(" as FKTABLE_SCHEM, ")
@@ -1530,12 +1543,12 @@ public abstract class JDBC3DatabaseMetaData extends org.sqlite.core.CoreDatabase
             .append(hasImportedKey ? "ur" : "3").append(" as UPDATE_RULE, ")
             .append(hasImportedKey ? "dr" : "3").append(" as DELETE_RULE, ")
             .append(hasImportedKey ? "fkn" : "''").append(" as FK_NAME, ")
-            .append(pkFinder.getName() != null ? pkFinder.getName() : "''").append(" as PK_NAME, ")
+            .append(hasImportedKey ? "pkn" : "''").append(" as PK_NAME, ")
             .append(Integer.toString(DatabaseMetaData.importedKeyInitiallyDeferred)) // FIXME: Check for pragma foreign_keys = true ?
             .append(" as DEFERRABILITY ");
 
         if (hasImportedKey) {
-            sql.append("from (").append(exportedKeysQuery).append(") order by fkt");
+            sql.append("from (").append(exportedKeysQuery).append(") ORDER BY FKTABLE_CAT, FKTABLE_SCHEM, FKTABLE_NAME, KEY_SEQ");
         }
         else {
             sql.append("limit 0");
@@ -1547,7 +1560,10 @@ public abstract class JDBC3DatabaseMetaData extends org.sqlite.core.CoreDatabase
     private StringBuilder appendDummyForeignKeyList(StringBuilder sql) {
       sql.append("select -1 as ks, '' as ptn, '' as fcn, '' as pcn, ")
       .append(DatabaseMetaData.importedKeyNoAction).append(" as ur, ")
-      .append(DatabaseMetaData.importedKeyNoAction).append(" as dr) limit 0;");
+      .append(DatabaseMetaData.importedKeyNoAction).append(" as dr, ")
+      .append(" '' as fkn, ")
+      .append(" '' as pkn ")
+      .append(") limit 0;");
       return sql;
     }
 
@@ -1566,7 +1582,7 @@ public abstract class JDBC3DatabaseMetaData extends org.sqlite.core.CoreDatabase
             .append(quote(catalog)).append(" as FKTABLE_CAT, ")
             .append(quote(schema)).append(" as FKTABLE_SCHEM, ")
             .append(quote(table)).append(" as FKTABLE_NAME, ")
-            .append("fcn as FKCOLUMN_NAME, ks as KEY_SEQ, ur as UPDATE_RULE, dr as DELETE_RULE, '' as FK_NAME, '' as PK_NAME, ")
+            .append("fcn as FKCOLUMN_NAME, ks as KEY_SEQ, ur as UPDATE_RULE, dr as DELETE_RULE, fkn as FK_NAME, pkn as PK_NAME, ")
             .append(Integer.toString(DatabaseMetaData.importedKeyInitiallyDeferred)).append(" as DEFERRABILITY from (");
 
         // Use a try catch block to avoid "query does not return ResultSet" error
@@ -1577,6 +1593,9 @@ public abstract class JDBC3DatabaseMetaData extends org.sqlite.core.CoreDatabase
             sql = appendDummyForeignKeyList(sql);
             return ((CoreStatement)stat).executeQuery(sql.toString(), true);
         }
+        
+    	final ImportedKeyFinder impFkFinder = new ImportedKeyFinder(table);
+    	List<ForeignKey> fkNames = impFkFinder.getFkList();  
 
         int i = 0;
         for (; rs.next(); i++) {
@@ -1585,8 +1604,10 @@ public abstract class JDBC3DatabaseMetaData extends org.sqlite.core.CoreDatabase
             String FKColName = rs.getString(4);
             String PKColName = rs.getString(5);
 
+            PrimaryKeyFinder pkFinder = new PrimaryKeyFinder(PKTabName);
+            String pkName = pkFinder.getName();
             if (PKColName == null) {
-                PKColName = new PrimaryKeyFinder(PKTabName).getColumns()[0];
+				PKColName = pkFinder.getColumns()[0];
             }
 
             String updateRule = rs.getString(6);
@@ -1596,6 +1617,9 @@ public abstract class JDBC3DatabaseMetaData extends org.sqlite.core.CoreDatabase
                 sql.append(" union all ");
             }
 
+            String fkName = null;
+            if (fkNames.size() > i) fkName = fkNames.get(i).getFkName();
+            
             sql.append("select ").append(keySeq).append(" as ks,")
                 .append("'").append(escape(PKTabName)).append("' as ptn, '")
                 .append(escape(FKColName)).append("' as fcn, '")
@@ -1611,15 +1635,18 @@ public abstract class JDBC3DatabaseMetaData extends org.sqlite.core.CoreDatabase
                 .append(" when 'CASCADE' then ").append(DatabaseMetaData.importedKeyCascade)
                 .append(" when 'RESTRICT' then ").append(DatabaseMetaData.importedKeyRestrict)
                 .append(" when 'SET NULL' then ").append(DatabaseMetaData.importedKeySetNull)
-                .append(" when 'SET DEFAULT' then ").append(DatabaseMetaData.importedKeySetDefault).append(" end as dr");
+                .append(" when 'SET DEFAULT' then ").append(DatabaseMetaData.importedKeySetDefault).append(" end as dr, ")
+                .append(fkName == null? "''": quote(fkName)).append(" as fkn, ")
+                .append(pkName == null? "''": quote(pkName)).append(" as pkn");
         }
         rs.close();
 
         if(i == 0) {
           sql = appendDummyForeignKeyList(sql);
         }
-
-        return ((CoreStatement)stat).executeQuery(sql.append(");").toString(), true);
+        sql.append(") ORDER BY PKTABLE_CAT, PKTABLE_SCHEM, PKTABLE_NAME, KEY_SEQ;");
+        
+        return ((CoreStatement)stat).executeQuery(sql.toString(), true);
     }
 
     /**
@@ -2008,6 +2035,164 @@ public abstract class JDBC3DatabaseMetaData extends org.sqlite.core.CoreDatabase
             return pkColumns;
         }
     }
+    
+    class ImportedKeyFinder {
+    	
+    	private String fkTableName;
+    	private List<ForeignKey> fkList = new ArrayList<ForeignKey>();
+    	
+    	public ImportedKeyFinder(String table) throws SQLException {
+
+            if (table == null || table.trim().length() == 0) {
+                throw new SQLException("Invalid table name: '" + table + "'");
+            }
+
+            this.fkTableName = table;
+            
+            List<String> fkNames = getForeignKeyNames(this.fkTableName);
+
+            Statement stat = null;
+            ResultSet rs = null;
+
+            try {
+                stat = conn.createStatement();
+                rs = stat.executeQuery("pragma foreign_key_list("+ this.fkTableName.toLowerCase()
+                		+ ")");
+
+                int prevFkId = -1;
+                int count = 0;
+                ForeignKey fk = null;
+                while(rs.next()) {
+                	int fkId = rs.getInt(1);
+                	int colSeq = rs.getInt(2);
+                	String pkTableName = rs.getString(3);
+                	String fkColName = rs.getString(4);
+                	String pkColName = rs.getString(5);
+                	String onUpdate = rs.getString(6);
+                	String onDelete = rs.getString(7);
+                	String match = rs.getString(8);
+                	
+                	String fkName = null;
+                    if (fkNames.size() > count) fkName = fkNames.get(count);
+                    
+                	if (fkId != prevFkId) {
+                		fk = new ForeignKey(fkName, pkTableName, pkTableName, onUpdate, onDelete, match);
+                		fkList.add(fk);
+                		prevFkId = fkId;
+                		count++;
+                	}
+                	fk.addColumnMapping(fkColName, pkColName);
+                }
+            }
+            finally {
+                try {
+                    if (rs != null) rs.close();
+                } catch (Exception e) {}
+                try {
+                    if (stat != null) stat.close();
+                } catch (Exception e) {}
+            }
+        }
+    	
+    	private List<String> getForeignKeyNames(String tbl) throws SQLException {		
+    		List<String> fkNames = new ArrayList<String>();
+    		if (tbl==null) {
+    			return fkNames;
+    		}
+    		Statement stat2 = null;
+    		ResultSet rs = null;
+    		try {
+    			stat2 = conn.createStatement();
+
+    			rs = stat2.executeQuery(
+    					"select sql from sqlite_master where" + " lower(name) = lower('" + escape(tbl) + "')");
+    			if (rs.next()) {
+    				Matcher matcher = FK_NAMED_PATTERN.matcher(rs.getString(1));
+
+    				while (matcher.find()) {
+    					fkNames.add(matcher.group(1));
+    				}
+    			}
+    		} finally {
+    			try {
+    				if (rs != null)
+    					rs.close();
+    			} catch (SQLException e) {
+    			}
+    			try {
+    				if (stat2 != null)
+    					stat2.close();
+    			} catch (SQLException e) {
+    			}
+    		}
+    		Collections.reverse(fkNames);
+    		return fkNames;
+    	}
+    	
+    	public String getFkTableName() {
+			return fkTableName;
+		}
+
+		public List<ForeignKey> getFkList() {
+			return fkList;
+		}
+
+		class ForeignKey {
+			
+			private String fkName;
+			private String pkTableName;
+    		private String fkTableName;
+    		private List<String> fkColNames = new ArrayList<String>();
+    		private List<String> pkColNames = new ArrayList<String>();
+    		private String onUpdate;
+    		private String onDelete;
+    		private String match;
+    		
+    		ForeignKey(String fkName, String pkTableName, String fkTableName, String onUpdate, String onDelete, String match) {
+				this.fkName = fkName;
+				this.pkTableName = pkTableName;
+				this.fkTableName = fkTableName;
+				this.onUpdate = onUpdate;
+				this.onDelete = onDelete;
+				this.match = match;
+			}
+    		
+
+    		public String getFkName() {
+				return fkName;
+			}
+
+			void addColumnMapping(String fkColName, String pkColName) {
+    			fkColNames.add(fkColName);
+    			pkColNames.add(pkColName);
+    		}
+    		
+    		public String[] getColumnMapping(int colSeq) {
+    			return new String[] {fkColNames.get(colSeq), pkColNames.get(colSeq)};
+    		}
+
+			public String getPkTableName() {
+				return pkTableName;
+			}
+
+			public String getFkTableName() {
+				return fkTableName;
+			}
+
+			public String getOnUpdate() {
+				return onUpdate;
+			}
+
+			public String getOnDelete() {
+				return onDelete;
+			}
+
+			public String getMatch() {
+				return match;
+			}
+    	}
+    	
+    }
 
     /**
      * @see java.lang.Object#finalize()
diff --git a/src/test/java/org/sqlite/DBMetaDataTest.java b/src/test/java/org/sqlite/DBMetaDataTest.java
index ff416d2..35c665f 100644
--- a/src/test/java/org/sqlite/DBMetaDataTest.java
+++ b/src/test/java/org/sqlite/DBMetaDataTest.java
@@ -260,13 +260,6 @@ public class DBMetaDataTest
         importedKeys.close();
     }
 
-/*    @Test
-    public void columnOrderOfgetExportedKeys() throws SQLException {
-
-    exportedKeys.close();
-
-    }*/
-
     @Test
     public void numberOfgetExportedKeysCols() throws SQLException {
 
@@ -316,6 +309,215 @@ public class DBMetaDataTest
         exportedKeys.close();
     }
 
+    @Test
+    public void getExportedKeysColsForNamedKeys() throws SQLException {
+
+    	ResultSet exportedKeys;
+
+    	// 1. Check for named primary keys
+    	// SQL is deliberately in uppercase, to make sure case-sensitivity is maintained
+        stat.executeUpdate("CREATE TABLE PARENT1 (ID1 INTEGER, DATA1 INTEGER, CONSTRAINT PK_PARENT PRIMARY KEY (ID1))");
+        stat.executeUpdate("CREATE TABLE CHILD1 (ID1 INTEGER, DATA2 INTEGER, FOREIGN KEY(ID1) REFERENCES PARENT1(ID1))");
+
+		exportedKeys = meta.getExportedKeys(null, null, "PARENT1");
+
+        assertTrue(exportedKeys.next());
+        assertEquals("PARENT1", exportedKeys.getString("PKTABLE_NAME"));
+        assertEquals("ID1", exportedKeys.getString("PKCOLUMN_NAME"));
+        assertEquals("PK_PARENT", exportedKeys.getString("PK_NAME"));
+        assertEquals("", exportedKeys.getString("FK_NAME"));
+        assertEquals("CHILD1", exportedKeys.getString("FKTABLE_NAME"));
+        assertEquals("ID1", exportedKeys.getString("FKCOLUMN_NAME"));
+
+        assertFalse(exportedKeys.next());
+
+        exportedKeys.close();
+        
+    	// 2. Check for named foreign keys
+    	// SQL is deliberately in mixed case, to make sure case-sensitivity is maintained
+        stat.executeUpdate("CREATE TABLE Parent2 (Id1 INTEGER, DATA1 INTEGER, PRIMARY KEY (Id1))");
+        stat.executeUpdate("CREATE TABLE Child2 (Id1 INTEGER, DATA2 INTEGER, CONSTRAINT FK_Child2 FOREIGN KEY(Id1) REFERENCES Parent2(Id1))");
+
+		exportedKeys = meta.getExportedKeys(null, null, "Parent2");
+
+        assertTrue(exportedKeys.next());
+        assertEquals("Parent2", exportedKeys.getString("PKTABLE_NAME"));
+        assertEquals("Id1", exportedKeys.getString("PKCOLUMN_NAME"));
+        assertEquals("", exportedKeys.getString("PK_NAME"));
+        assertEquals("FK_Child2", exportedKeys.getString("FK_NAME"));
+        assertEquals("Child2", exportedKeys.getString("FKTABLE_NAME"));
+        assertEquals("Id1", exportedKeys.getString("FKCOLUMN_NAME"));
+
+        assertFalse(exportedKeys.next());
+
+        exportedKeys.close();
+    }
+
+    @Test
+    public void getImportedKeysColsForNamedKeys() throws SQLException {
+
+    	ResultSet importedKeys;
+
+    	// 1. Check for named primary keys
+    	// SQL is deliberately in uppercase, to make sure case-sensitivity is maintained
+        stat.executeUpdate("CREATE TABLE PARENT1 (ID1 INTEGER, DATA1 INTEGER, CONSTRAINT PK_PARENT PRIMARY KEY (ID1))");
+        stat.executeUpdate("CREATE TABLE CHILD1 (ID1 INTEGER, DATA2 INTEGER, FOREIGN KEY(ID1) REFERENCES PARENT1(ID1))");
+
+		importedKeys = meta.getImportedKeys(null, null, "CHILD1");
+
+        assertTrue(importedKeys.next());
+        assertEquals("PARENT1", importedKeys.getString("PKTABLE_NAME"));
+        assertEquals("ID1", importedKeys.getString("PKCOLUMN_NAME"));
+        assertEquals("PK_PARENT", importedKeys.getString("PK_NAME"));
+        assertEquals("", importedKeys.getString("FK_NAME"));
+        assertEquals("CHILD1", importedKeys.getString("FKTABLE_NAME"));
+        assertEquals("ID1", importedKeys.getString("FKCOLUMN_NAME"));
+
+        assertFalse(importedKeys.next());
+
+        importedKeys.close();
+        
+    	// 2. Check for named foreign keys
+    	// SQL is deliberately in mixed case, to make sure case-sensitivity is maintained
+        stat.executeUpdate("CREATE TABLE Parent2 (Id1 INTEGER, DATA1 INTEGER, PRIMARY KEY (Id1))");
+        stat.executeUpdate("CREATE TABLE Child2 (Id1 INTEGER, DATA2 INTEGER, "
+        		+ "CONSTRAINT FK_Child2 FOREIGN KEY(Id1) REFERENCES Parent2(Id1))");
+
+		importedKeys = meta.getImportedKeys(null, null, "Child2");
+
+        assertTrue(importedKeys.next());
+        assertEquals("Parent2", importedKeys.getString("PKTABLE_NAME"));
+        assertEquals("Id1", importedKeys.getString("PKCOLUMN_NAME"));
+        assertEquals("", importedKeys.getString("PK_NAME"));
+        assertEquals("FK_Child2", importedKeys.getString("FK_NAME"));
+        assertEquals("Child2", importedKeys.getString("FKTABLE_NAME"));
+        assertEquals("Id1", importedKeys.getString("FKCOLUMN_NAME"));
+
+        assertFalse(importedKeys.next());
+
+        importedKeys.close();
+    }
+    
+    @Test
+    public void getImportedKeysColsForMixedCaseDefinition() throws SQLException {
+
+    	ResultSet importedKeys;
+
+    	// SQL is deliberately in mixed-case, to make sure case-sensitivity is maintained
+        stat.executeUpdate("CREATE TABLE PARENT1 (ID1 INTEGER, DATA1 INTEGER, CONSTRAINT PK_PARENT PRIMARY KEY (ID1))");
+        stat.executeUpdate("CREATE TABLE CHILD1 (ID1 INTEGER, DATA2 INTEGER, "
+        		+ "CONSTRAINT FK_Parent1 FOREIGN KEY(ID1) REFERENCES Parent1(Id1))");
+
+		importedKeys = meta.getImportedKeys(null, null, "CHILD1");
+
+        assertTrue(importedKeys.next());
+        assertEquals("Parent1", importedKeys.getString("PKTABLE_NAME"));
+        assertEquals("Id1", importedKeys.getString("PKCOLUMN_NAME"));
+        assertEquals("PK_PARENT", importedKeys.getString("PK_NAME"));
+        assertEquals("FK_Parent1", importedKeys.getString("FK_NAME"));
+        assertEquals("CHILD1", importedKeys.getString("FKTABLE_NAME"));
+        assertEquals("ID1", importedKeys.getString("FKCOLUMN_NAME"));
+
+        assertFalse(importedKeys.next());
+
+        importedKeys.close();    
+    }
+    
+    @Test
+    public void getImportedKeysColsForMultipleImports() throws SQLException {
+
+    	ResultSet importedKeys;
+
+    	stat.executeUpdate("CREATE TABLE PARENT1 (ID1 INTEGER, DATA1 INTEGER, CONSTRAINT PK_PARENT1 PRIMARY KEY (ID1))");
+    	stat.executeUpdate("CREATE TABLE PARENT2 (ID2 INTEGER, DATA2 INTEGER, CONSTRAINT PK_PARENT2 PRIMARY KEY (ID2))");
+    	stat.executeUpdate("CREATE TABLE CHILD1 (ID1 INTEGER, ID2 INTEGER, "
+    			+ "CONSTRAINT FK_PARENT1 FOREIGN KEY(ID1) REFERENCES PARENT1(ID1), "
+    			+ "CONSTRAINT FK_PARENT2 FOREIGN KEY(ID2) REFERENCES PARENT2(ID2))");
+
+		importedKeys = meta.getImportedKeys(null, null, "CHILD1");
+
+        assertTrue(importedKeys.next());
+        assertEquals("PARENT1", importedKeys.getString("PKTABLE_NAME"));
+        assertEquals("ID1", importedKeys.getString("PKCOLUMN_NAME"));
+        assertEquals("PK_PARENT1", importedKeys.getString("PK_NAME"));
+        assertEquals("FK_PARENT1", importedKeys.getString("FK_NAME"));
+        assertEquals("CHILD1", importedKeys.getString("FKTABLE_NAME"));
+        assertEquals("ID1", importedKeys.getString("FKCOLUMN_NAME"));
+        
+        assertTrue(importedKeys.next());
+        assertEquals("PARENT2", importedKeys.getString("PKTABLE_NAME"));
+        assertEquals("ID2", importedKeys.getString("PKCOLUMN_NAME"));
+        assertEquals("PK_PARENT2", importedKeys.getString("PK_NAME"));
+        assertEquals("FK_PARENT2", importedKeys.getString("FK_NAME"));
+        assertEquals("CHILD1", importedKeys.getString("FKTABLE_NAME"));
+        assertEquals("ID2", importedKeys.getString("FKCOLUMN_NAME"));
+        
+        assertFalse(importedKeys.next());
+
+        importedKeys.close();    
+        
+        // Unnamed foreign keys and unnamed primary keys
+    	stat.executeUpdate("CREATE TABLE PARENT3 (ID3 INTEGER, DATA3 INTEGER, PRIMARY KEY (ID3))");
+    	stat.executeUpdate("CREATE TABLE PARENT4 (ID4 INTEGER, DATA4 INTEGER, CONSTRAINT PK_PARENT4 PRIMARY KEY (ID4))");
+    	stat.executeUpdate("CREATE TABLE CHILD2 (ID3 INTEGER, ID4 INTEGER, "
+    			+ "FOREIGN KEY(ID3) REFERENCES PARENT3(ID3), "
+    			+ "CONSTRAINT FK_PARENT4 FOREIGN KEY(ID4) REFERENCES PARENT4(ID4))");
+
+		importedKeys = meta.getImportedKeys(null, null, "CHILD2");
+
+        assertTrue(importedKeys.next());
+        assertEquals("PARENT3", importedKeys.getString("PKTABLE_NAME"));
+        assertEquals("ID3", importedKeys.getString("PKCOLUMN_NAME"));
+        assertEquals("", importedKeys.getString("PK_NAME"));
+        assertEquals("", importedKeys.getString("FK_NAME"));
+        assertEquals("CHILD2", importedKeys.getString("FKTABLE_NAME"));
+        assertEquals("ID3", importedKeys.getString("FKCOLUMN_NAME"));
+        
+        assertTrue(importedKeys.next());
+        assertEquals("PARENT4", importedKeys.getString("PKTABLE_NAME"));
+        assertEquals("ID4", importedKeys.getString("PKCOLUMN_NAME"));
+        assertEquals("PK_PARENT4", importedKeys.getString("PK_NAME"));
+        assertEquals("FK_PARENT4", importedKeys.getString("FK_NAME"));
+        assertEquals("CHILD2", importedKeys.getString("FKTABLE_NAME"));
+        assertEquals("ID4", importedKeys.getString("FKCOLUMN_NAME"));
+        
+        assertFalse(importedKeys.next());
+
+        importedKeys.close();   
+    }
+    
+    @Test
+    public void getExportedKeysColsForMultipleImports() throws SQLException {
+
+    	ResultSet exportedKeys;
+
+    	stat.executeUpdate("CREATE TABLE PARENT1 (ID1 INTEGER, ID2 INTEGER, CONSTRAINT PK_PARENT1 PRIMARY KEY (ID1))");
+    	stat.executeUpdate("CREATE TABLE CHILD1 (ID1 INTEGER, CONSTRAINT FK_PARENT1 FOREIGN KEY(ID1) REFERENCES PARENT1(ID1))");
+    	stat.executeUpdate("CREATE TABLE CHILD2 (ID2 INTEGER, CONSTRAINT FK_PARENT2 FOREIGN KEY(ID2) REFERENCES PARENT1(ID2))");
+
+		exportedKeys = meta.getExportedKeys(null, null, "PARENT1");
+
+        assertTrue(exportedKeys.next());
+        assertEquals("PARENT1", exportedKeys.getString("PKTABLE_NAME"));
+        assertEquals("ID1", exportedKeys.getString("PKCOLUMN_NAME"));
+        assertEquals("PK_PARENT1", exportedKeys.getString("PK_NAME"));
+        assertEquals("FK_PARENT1", exportedKeys.getString("FK_NAME"));
+        assertEquals("CHILD1", exportedKeys.getString("FKTABLE_NAME"));
+        assertEquals("ID1", exportedKeys.getString("FKCOLUMN_NAME"));
+        
+        assertTrue(exportedKeys.next());
+        assertEquals("PARENT1", exportedKeys.getString("PKTABLE_NAME"));
+        assertEquals("ID2", exportedKeys.getString("PKCOLUMN_NAME"));
+        assertEquals("", exportedKeys.getString("PK_NAME"));
+        assertEquals("FK_PARENT2", exportedKeys.getString("FK_NAME"));
+        assertEquals("CHILD2", exportedKeys.getString("FKTABLE_NAME"));
+        assertEquals("ID2", exportedKeys.getString("FKCOLUMN_NAME"));
+        
+        assertFalse(exportedKeys.next());
+
+        exportedKeys.close();    
+    }
+    
     @Test
     public void columnOrderOfgetTables() throws SQLException {
         ResultSet rs = meta.getTables(null, null, null, null);
@@ -770,8 +972,8 @@ public class DBMetaDataTest
         stat.executeUpdate("create table REFERRING (ID integer, RID integer, constraint fk\\r\\n foreign\\tkey\\r\\n(RID) references REFERRED(id))");
 
         exportedKeys = meta.getExportedKeys(null, null, "referred");
-        assertEquals("referred", exportedKeys.getString("PKTABLE_NAME"));
-        assertEquals("referring", exportedKeys.getString("FKTABLE_NAME"));
+        assertEquals("REFERRED", exportedKeys.getString("PKTABLE_NAME"));
+        assertEquals("REFERRING", exportedKeys.getString("FKTABLE_NAME"));
         assertEquals("fk", exportedKeys.getString("FK_NAME"));
         exportedKeys.close();
     }