Repository: xerial/sqlite-jdbc
Issue: DatabaseMetaData getExportedKeys does not escape PK_NAME for named primary keys

DatabaseMetaData `getExportedKeys` does not escape `PK_NAME`, resulting in a SQLException for named primary keys.

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..cad9326 100644
--- a/src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java
+++ b/src/main/java/org/sqlite/jdbc3/JDBC3DatabaseMetaData.java
@@ -1434,6 +1434,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 +1442,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 {
@@ -1465,20 +1471,20 @@ public abstract class JDBC3DatabaseMetaData extends org.sqlite.core.CoreDatabase
 
                     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) ? pkColumns[0] : PKColName;
 
                         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(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(RULE_MAP.get(fk.getString(6))).append(" as ur, ")
                             .append(RULE_MAP.get(fk.getString(7))).append(" as dr, ");
@@ -1490,7 +1496,7 @@ public abstract class JDBC3DatabaseMetaData extends org.sqlite.core.CoreDatabase
                             Matcher matcher = FK_NAMED_PATTERN.matcher(rs.getString(1));
 
                             if (matcher.find()){
-                                exportedKeysQuery.append("'").append(escape(matcher.group(1).toLowerCase())).append("' as fkn");
+                                exportedKeysQuery.append("'").append(escape(matcher.group(1))).append("' as fkn");
                             }
                             else {
                                 exportedKeysQuery.append("'' as fkn");
@@ -1520,7 +1526,7 @@ public abstract class JDBC3DatabaseMetaData extends org.sqlite.core.CoreDatabase
         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,7 +1536,7 @@ 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(pkFinder.getName() != null ? quote(pkFinder.getName()) : "''").append(" as PK_NAME, ")
             .append(Integer.toString(DatabaseMetaData.importedKeyInitiallyDeferred)) // FIXME: Check for pragma foreign_keys = true ?
             .append(" as DEFERRABILITY ");
 
diff --git a/src/test/java/org/sqlite/DBMetaDataTest.java b/src/test/java/org/sqlite/DBMetaDataTest.java
index ff416d2..e613b39 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,50 @@ 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 columnOrderOfgetTables() throws SQLException {
         ResultSet rs = meta.getTables(null, null, null, null);
@@ -770,8 +807,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();
     }