From 485843556a73fb42b60f0867cd88b51842395eb5 Mon Sep 17 00:00:00 2001 From: Shawn Sun Date: Fri, 8 Sep 2017 17:15:09 -0700 Subject: [PATCH 01/15] works now, need comments and clean --- .../jdbc/SQLServerDatabaseMetaData.java | 86 ++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java index 06fbab527..5f904418f 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java @@ -848,7 +848,91 @@ public java.sql.ResultSet getClientInfoProperties() throws SQLException { arguments[3] = table; // fktable_name arguments[4] = schema; arguments[5] = cat; - return getResultSetWithProvidedColumnNames(cat, CallableHandles.SP_FKEYS, arguments, pkfkColumnNames); +// return getResultSetWithProvidedColumnNames(cat, CallableHandles.SP_FKEYS, arguments, pkfkColumnNames); + + + return getResultSetForForeignKeyInformation(cat, schema, table); + } + + String fkeys_results_column_definition = "PKTABLE_QUALIFIER sysname, PKTABLE_OWNER sysname, PKTABLE_NAME sysname, PKCOLUMN_NAME sysname, FKTABLE_QUALIFIER sysname, FKTABLE_OWNER sysname, FKTABLE_NAME sysname, FKCOLUMN_NAME sysname, KEY_SEQ smallint, UPDATE_RULE smallint, DELETE_RULE smallint, FK_NAME sysname, PK_NAME sysname, DEFERRABILITY smallint"; + String foreign_keys_combined_column_definition = "name sysname, delete_referential_action_desc nvarchar(60), update_referential_action_desc nvarchar(60)," + + fkeys_results_column_definition; + + private ResultSet getResultSetForForeignKeyInformation(String cat, + String schema, + String table) throws SQLServerException { + String fkeys_results = "#fkeys_results"; + String foreign_keys_combined = "#foreign_keys_combined_results"; + String sys_foreign_keys = "sys.foreign_keys"; + SQLServerStatement stmt = null; + + stmt = (SQLServerStatement) connection.createStatement(); + + /** + * create a temp table that has the same definition as the result of sp_fkeys: + * + * create table #fkeys_results ( + * PKTABLE_QUALIFIER sysname, + * PKTABLE_OWNER sysname, + * PKTABLE_NAME sysname, + * PKCOLUMN_NAME sysname, + * FKTABLE_QUALIFIER sysname, + * FKTABLE_OWNER sysname, + * FKTABLE_NAME sysname, + * FKCOLUMN_NAME sysname, + * KEY_SEQ smallint, + * UPDATE_RULE smallint, + * DELETE_RULE smallint, + * FK_NAME sysname, + * PK_NAME sysname, + * DEFERRABILITY smallint + * ); + * + */ + stmt.execute("create table " + fkeys_results + " (" + fkeys_results_column_definition + ")"); + + //insert the results of sp_fkeys to the temp table #fkeys_results + stmt.execute("insert into " + fkeys_results + + " exec sp_fkeys @fktable_name=" + table + ", @fktable_owner=" + schema + ", @fktable_qualifier=" + cat); + + /** + * create a temp table that has 3 columns from sys.foreign_keys and the rest of columns are the same as #fkeys_results: + * + * create table #fkeys_results ( + * name sysname, + * delete_referential_action_desc nvarchar(60), + * update_referential_action_desc nvarchar(60), + * ...... + * ...... + * ...... + * ); + * + */ + stmt.execute("create table " + foreign_keys_combined + " (" + foreign_keys_combined_column_definition + ")"); + + + // right join the content of sys.foreign_keys and the content of #fkeys_results base on foreign key name and save the result to a new temp + // table #foreign_keys_combined_results + stmt.execute("insert into " + foreign_keys_combined + + " select " + sys_foreign_keys + ".name, " + sys_foreign_keys + ".delete_referential_action_desc, " + sys_foreign_keys + ".update_referential_action_desc," + + fkeys_results + ".PKTABLE_QUALIFIER," + fkeys_results + ".PKTABLE_OWNER," + fkeys_results + ".PKTABLE_NAME," + fkeys_results + ".PKCOLUMN_NAME," + + fkeys_results + ".FKTABLE_QUALIFIER," + fkeys_results + ".FKTABLE_OWNER," + fkeys_results + ".FKTABLE_NAME," + fkeys_results + ".FKCOLUMN_NAME," + + fkeys_results + ".KEY_SEQ," + fkeys_results + ".UPDATE_RULE," + fkeys_results + ".DELETE_RULE," + fkeys_results + ".FK_NAME," + fkeys_results + ".PK_NAME," + + fkeys_results + ".DEFERRABILITY from " + sys_foreign_keys + + " right join " + fkeys_results + " on " + sys_foreign_keys + ".name=" + fkeys_results + ".FK_NAME"); + + stmt.execute("update " + foreign_keys_combined + " set DELETE_RULE=3 where delete_referential_action_desc='NO_ACTION';" + "update " + + foreign_keys_combined + " set DELETE_RULE=0 where delete_referential_action_desc='Cascade';" + "update " + foreign_keys_combined + + " set DELETE_RULE=2 where delete_referential_action_desc='SET_NULL';" + "update " + foreign_keys_combined + + " set DELETE_RULE=4 where delete_referential_action_desc='SET_DEFAULT';" + "update " + foreign_keys_combined + + " set UPDATE_RULE=3 where update_referential_action_desc='NO_ACTION';" + "update " + foreign_keys_combined + + " set UPDATE_RULE=0 where update_referential_action_desc='Cascade';" + "update " + foreign_keys_combined + + " set UPDATE_RULE=2 where update_referential_action_desc='SET_NULL';" + "update " + foreign_keys_combined + + " set UPDATE_RULE=4 where update_referential_action_desc='SET_DEFAULT';"); + + return stmt.executeQuery( + "select PKTABLE_QUALIFIER,PKTABLE_OWNER,PKTABLE_NAME,PKCOLUMN_NAME,FKTABLE_QUALIFIER,FKTABLE_OWNER,FKTABLE_NAME,FKCOLUMN_NAME,KEY_SEQ,UPDATE_RULE,DELETE_RULE,FK_NAME,PK_NAME,DEFERRABILITY from " + + foreign_keys_combined + " order by FKTABLE_QUALIFIER, FKTABLE_OWNER, FKTABLE_NAME, KEY_SEQ"); } private final static String[] getIndexInfoColumnNames = {/* 1 */ TABLE_CAT, /* 2 */ TABLE_SCHEM, /* 3 */ TABLE_NAME, /* 4 */ NON_UNIQUE, From 71ff104a1bfd5b48b7a43e55e835628e7e0efda7 Mon Sep 17 00:00:00 2001 From: Shawn Sun Date: Fri, 8 Sep 2017 17:35:04 -0700 Subject: [PATCH 02/15] added comments --- .../jdbc/SQLServerDatabaseMetaData.java | 59 ++++++++++++++----- 1 file changed, 43 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java index 5f904418f..aa4922319 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java @@ -857,17 +857,28 @@ public java.sql.ResultSet getClientInfoProperties() throws SQLException { String fkeys_results_column_definition = "PKTABLE_QUALIFIER sysname, PKTABLE_OWNER sysname, PKTABLE_NAME sysname, PKCOLUMN_NAME sysname, FKTABLE_QUALIFIER sysname, FKTABLE_OWNER sysname, FKTABLE_NAME sysname, FKCOLUMN_NAME sysname, KEY_SEQ smallint, UPDATE_RULE smallint, DELETE_RULE smallint, FK_NAME sysname, PK_NAME sysname, DEFERRABILITY smallint"; String foreign_keys_combined_column_definition = "name sysname, delete_referential_action_desc nvarchar(60), update_referential_action_desc nvarchar(60)," + fkeys_results_column_definition; - + + /** + * The original sp_fkeys stored procedure does not give the required values from JDBC specification. This method creates 2 temporary tables and + * uses join and other operations on them to give the correct values. + * + * @param cat + * @param schema + * @param table + * @return + * @throws SQLServerException + */ private ResultSet getResultSetForForeignKeyInformation(String cat, String schema, String table) throws SQLServerException { String fkeys_results = "#fkeys_results"; String foreign_keys_combined = "#foreign_keys_combined_results"; String sys_foreign_keys = "sys.foreign_keys"; - SQLServerStatement stmt = null; + + SQLServerStatement stmt = null; //cannot close this statement, otherwise the returned resultset would be closed too. stmt = (SQLServerStatement) connection.createStatement(); - + /** * create a temp table that has the same definition as the result of sp_fkeys: * @@ -891,14 +902,16 @@ private ResultSet getResultSetForForeignKeyInformation(String cat, */ stmt.execute("create table " + fkeys_results + " (" + fkeys_results_column_definition + ")"); - //insert the results of sp_fkeys to the temp table #fkeys_results + /** + * insert the results of sp_fkeys to the temp table #fkeys_results + */ stmt.execute("insert into " + fkeys_results + " exec sp_fkeys @fktable_name=" + table + ", @fktable_owner=" + schema + ", @fktable_qualifier=" + cat); /** - * create a temp table that has 3 columns from sys.foreign_keys and the rest of columns are the same as #fkeys_results: + * create another temp table that has 3 columns from sys.foreign_keys and the rest of columns are the same as #fkeys_results: * - * create table #fkeys_results ( + * create table #foreign_keys_combined_results ( * name sysname, * delete_referential_action_desc nvarchar(60), * update_referential_action_desc nvarchar(60), @@ -911,8 +924,10 @@ private ResultSet getResultSetForForeignKeyInformation(String cat, stmt.execute("create table " + foreign_keys_combined + " (" + foreign_keys_combined_column_definition + ")"); - // right join the content of sys.foreign_keys and the content of #fkeys_results base on foreign key name and save the result to a new temp - // table #foreign_keys_combined_results + /** + * right join the content of sys.foreign_keys and the content of #fkeys_results base on foreign key name and save the result to the new temp + * table #foreign_keys_combined_results + */ stmt.execute("insert into " + foreign_keys_combined + " select " + sys_foreign_keys + ".name, " + sys_foreign_keys + ".delete_referential_action_desc, " + sys_foreign_keys + ".update_referential_action_desc," + fkeys_results + ".PKTABLE_QUALIFIER," + fkeys_results + ".PKTABLE_OWNER," + fkeys_results + ".PKTABLE_NAME," + fkeys_results + ".PKCOLUMN_NAME," @@ -921,15 +936,27 @@ private ResultSet getResultSetForForeignKeyInformation(String cat, + fkeys_results + ".DEFERRABILITY from " + sys_foreign_keys + " right join " + fkeys_results + " on " + sys_foreign_keys + ".name=" + fkeys_results + ".FK_NAME"); - stmt.execute("update " + foreign_keys_combined + " set DELETE_RULE=3 where delete_referential_action_desc='NO_ACTION';" + "update " - + foreign_keys_combined + " set DELETE_RULE=0 where delete_referential_action_desc='Cascade';" + "update " + foreign_keys_combined - + " set DELETE_RULE=2 where delete_referential_action_desc='SET_NULL';" + "update " + foreign_keys_combined - + " set DELETE_RULE=4 where delete_referential_action_desc='SET_DEFAULT';" + "update " + foreign_keys_combined - + " set UPDATE_RULE=3 where update_referential_action_desc='NO_ACTION';" + "update " + foreign_keys_combined - + " set UPDATE_RULE=0 where update_referential_action_desc='Cascade';" + "update " + foreign_keys_combined - + " set UPDATE_RULE=2 where update_referential_action_desc='SET_NULL';" + "update " + foreign_keys_combined - + " set UPDATE_RULE=4 where update_referential_action_desc='SET_DEFAULT';"); + /** + * the DELETE_RULE value and UPDATE_RULE value returned from sp_fkeys are not the same as required by JDBC spec. therefore, we need to update + * those values to JDBC required values base on delete_referential_action_desc and update_referential_action_desc returned from sys.foreign_keys + * No Action: 3 + * Cascade: 0 + * Set Null: 2 + * Set Default: 4 + */ + stmt.execute("update " + foreign_keys_combined + " set DELETE_RULE=3 where delete_referential_action_desc='NO_ACTION';" + + "update " + foreign_keys_combined + " set DELETE_RULE=0 where delete_referential_action_desc='Cascade';" + + "update " + foreign_keys_combined + " set DELETE_RULE=2 where delete_referential_action_desc='SET_NULL';" + + "update " + foreign_keys_combined + " set DELETE_RULE=4 where delete_referential_action_desc='SET_DEFAULT';" + + "update " + foreign_keys_combined + " set UPDATE_RULE=3 where update_referential_action_desc='NO_ACTION';" + + "update " + foreign_keys_combined + " set UPDATE_RULE=0 where update_referential_action_desc='Cascade';" + + "update " + foreign_keys_combined + " set UPDATE_RULE=2 where update_referential_action_desc='SET_NULL';" + + "update " + foreign_keys_combined + " set UPDATE_RULE=4 where update_referential_action_desc='SET_DEFAULT';"); + /** + * now, the #foreign_keys_combined_results table has the correct values for DELETE_RULE and UPDATE_RULE. Then we can return the result of the + * table with the same definition of the resultset return by sp_fkeys (same column definition and same order). + */ return stmt.executeQuery( "select PKTABLE_QUALIFIER,PKTABLE_OWNER,PKTABLE_NAME,PKCOLUMN_NAME,FKTABLE_QUALIFIER,FKTABLE_OWNER,FKTABLE_NAME,FKCOLUMN_NAME,KEY_SEQ,UPDATE_RULE,DELETE_RULE,FK_NAME,PK_NAME,DEFERRABILITY from " + foreign_keys_combined + " order by FKTABLE_QUALIFIER, FKTABLE_OWNER, FKTABLE_NAME, KEY_SEQ"); From ebb352ead21ada8ae6bec5a194f56cb11113b834 Mon Sep 17 00:00:00 2001 From: Shawn Sun Date: Fri, 8 Sep 2017 17:48:37 -0700 Subject: [PATCH 03/15] add support for "" --- .../microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java index aa4922319..a75e950e2 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java @@ -905,8 +905,9 @@ private ResultSet getResultSetForForeignKeyInformation(String cat, /** * insert the results of sp_fkeys to the temp table #fkeys_results */ - stmt.execute("insert into " + fkeys_results - + " exec sp_fkeys @fktable_name=" + table + ", @fktable_owner=" + schema + ", @fktable_qualifier=" + cat); + stmt.execute("insert into " + fkeys_results + " exec sp_fkeys @fktable_name=" + table + + (null == schema || schema.trim().length() != 0 ? ", @fktable_owner=" + schema : "") + + (null == cat || cat.trim().length() != 0 ? ", @fktable_qualifier=" + cat : "")); /** * create another temp table that has 3 columns from sys.foreign_keys and the rest of columns are the same as #fkeys_results: From 25408d79bf33ec0db86b97aa5097e3890fee9324 Mon Sep 17 00:00:00 2001 From: Shawn Sun Date: Mon, 11 Sep 2017 10:43:06 -0700 Subject: [PATCH 04/15] fix getExportedKeys() --- .../jdbc/SQLServerDatabaseMetaData.java | 65 ++++++++++--------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java index a75e950e2..e8ab73601 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java @@ -817,7 +817,12 @@ public java.sql.ResultSet getClientInfoProperties() throws SQLException { arguments[3] = null; // fktable_name arguments[4] = null; arguments[5] = null; - return getResultSetWithProvidedColumnNames(cat, CallableHandles.SP_FKEYS, arguments, pkfkColumnNames); + + String sp_fkeys_Query = " exec sp_fkeys @pktable_name=" + table + + (null == schema || schema.trim().length() != 0 ? ", @pktable_owner=" + schema : "") + + (null == cat || cat.trim().length() != 0 ? ", @pktable_qualifier=" + cat : ""); + + return getResultSetForForeignKeyInformation(sp_fkeys_Query); } /* L0 */ public String getExtraNameCharacters() throws SQLServerException { @@ -848,10 +853,12 @@ public java.sql.ResultSet getClientInfoProperties() throws SQLException { arguments[3] = table; // fktable_name arguments[4] = schema; arguments[5] = cat; -// return getResultSetWithProvidedColumnNames(cat, CallableHandles.SP_FKEYS, arguments, pkfkColumnNames); - - return getResultSetForForeignKeyInformation(cat, schema, table); + String sp_fkeys_Query = " exec sp_fkeys @fktable_name=" + table + + (null == schema || schema.trim().length() != 0 ? ", @fktable_owner=" + schema : "") + + (null == cat || cat.trim().length() != 0 ? ", @fktable_qualifier=" + cat : ""); + + return getResultSetForForeignKeyInformation(sp_fkeys_Query); } String fkeys_results_column_definition = "PKTABLE_QUALIFIER sysname, PKTABLE_OWNER sysname, PKTABLE_NAME sysname, PKCOLUMN_NAME sysname, FKTABLE_QUALIFIER sysname, FKTABLE_OWNER sysname, FKTABLE_NAME sysname, FKCOLUMN_NAME sysname, KEY_SEQ smallint, UPDATE_RULE smallint, DELETE_RULE smallint, FK_NAME sysname, PK_NAME sysname, DEFERRABILITY smallint"; @@ -862,17 +869,13 @@ public java.sql.ResultSet getClientInfoProperties() throws SQLException { * The original sp_fkeys stored procedure does not give the required values from JDBC specification. This method creates 2 temporary tables and * uses join and other operations on them to give the correct values. * - * @param cat - * @param schema - * @param table + * @param sp_fkeys_Query * @return * @throws SQLServerException */ - private ResultSet getResultSetForForeignKeyInformation(String cat, - String schema, - String table) throws SQLServerException { - String fkeys_results = "#fkeys_results"; - String foreign_keys_combined = "#foreign_keys_combined_results"; + private ResultSet getResultSetForForeignKeyInformation(String sp_fkeys_Query) throws SQLServerException { + String fkeys_results_tableName = "#fkeys_results"; + String foreign_keys_combined_tableName = "#foreign_keys_combined_results"; String sys_foreign_keys = "sys.foreign_keys"; SQLServerStatement stmt = null; //cannot close this statement, otherwise the returned resultset would be closed too. @@ -900,14 +903,12 @@ private ResultSet getResultSetForForeignKeyInformation(String cat, * ); * */ - stmt.execute("create table " + fkeys_results + " (" + fkeys_results_column_definition + ")"); + stmt.execute("create table " + fkeys_results_tableName + " (" + fkeys_results_column_definition + ")"); /** * insert the results of sp_fkeys to the temp table #fkeys_results */ - stmt.execute("insert into " + fkeys_results + " exec sp_fkeys @fktable_name=" + table - + (null == schema || schema.trim().length() != 0 ? ", @fktable_owner=" + schema : "") - + (null == cat || cat.trim().length() != 0 ? ", @fktable_qualifier=" + cat : "")); + stmt.execute("insert into " + fkeys_results_tableName + sp_fkeys_Query); /** * create another temp table that has 3 columns from sys.foreign_keys and the rest of columns are the same as #fkeys_results: @@ -922,20 +923,20 @@ private ResultSet getResultSetForForeignKeyInformation(String cat, * ); * */ - stmt.execute("create table " + foreign_keys_combined + " (" + foreign_keys_combined_column_definition + ")"); + stmt.execute("create table " + foreign_keys_combined_tableName + " (" + foreign_keys_combined_column_definition + ")"); /** * right join the content of sys.foreign_keys and the content of #fkeys_results base on foreign key name and save the result to the new temp * table #foreign_keys_combined_results */ - stmt.execute("insert into " + foreign_keys_combined + stmt.execute("insert into " + foreign_keys_combined_tableName + " select " + sys_foreign_keys + ".name, " + sys_foreign_keys + ".delete_referential_action_desc, " + sys_foreign_keys + ".update_referential_action_desc," - + fkeys_results + ".PKTABLE_QUALIFIER," + fkeys_results + ".PKTABLE_OWNER," + fkeys_results + ".PKTABLE_NAME," + fkeys_results + ".PKCOLUMN_NAME," - + fkeys_results + ".FKTABLE_QUALIFIER," + fkeys_results + ".FKTABLE_OWNER," + fkeys_results + ".FKTABLE_NAME," + fkeys_results + ".FKCOLUMN_NAME," - + fkeys_results + ".KEY_SEQ," + fkeys_results + ".UPDATE_RULE," + fkeys_results + ".DELETE_RULE," + fkeys_results + ".FK_NAME," + fkeys_results + ".PK_NAME," - + fkeys_results + ".DEFERRABILITY from " + sys_foreign_keys - + " right join " + fkeys_results + " on " + sys_foreign_keys + ".name=" + fkeys_results + ".FK_NAME"); + + fkeys_results_tableName + ".PKTABLE_QUALIFIER," + fkeys_results_tableName + ".PKTABLE_OWNER," + fkeys_results_tableName + ".PKTABLE_NAME," + fkeys_results_tableName + ".PKCOLUMN_NAME," + + fkeys_results_tableName + ".FKTABLE_QUALIFIER," + fkeys_results_tableName + ".FKTABLE_OWNER," + fkeys_results_tableName + ".FKTABLE_NAME," + fkeys_results_tableName + ".FKCOLUMN_NAME," + + fkeys_results_tableName + ".KEY_SEQ," + fkeys_results_tableName + ".UPDATE_RULE," + fkeys_results_tableName + ".DELETE_RULE," + fkeys_results_tableName + ".FK_NAME," + fkeys_results_tableName + ".PK_NAME," + + fkeys_results_tableName + ".DEFERRABILITY from " + sys_foreign_keys + + " right join " + fkeys_results_tableName + " on " + sys_foreign_keys + ".name=" + fkeys_results_tableName + ".FK_NAME"); /** * the DELETE_RULE value and UPDATE_RULE value returned from sp_fkeys are not the same as required by JDBC spec. therefore, we need to update @@ -945,14 +946,14 @@ private ResultSet getResultSetForForeignKeyInformation(String cat, * Set Null: 2 * Set Default: 4 */ - stmt.execute("update " + foreign_keys_combined + " set DELETE_RULE=3 where delete_referential_action_desc='NO_ACTION';" - + "update " + foreign_keys_combined + " set DELETE_RULE=0 where delete_referential_action_desc='Cascade';" - + "update " + foreign_keys_combined + " set DELETE_RULE=2 where delete_referential_action_desc='SET_NULL';" - + "update " + foreign_keys_combined + " set DELETE_RULE=4 where delete_referential_action_desc='SET_DEFAULT';" - + "update " + foreign_keys_combined + " set UPDATE_RULE=3 where update_referential_action_desc='NO_ACTION';" - + "update " + foreign_keys_combined + " set UPDATE_RULE=0 where update_referential_action_desc='Cascade';" - + "update " + foreign_keys_combined + " set UPDATE_RULE=2 where update_referential_action_desc='SET_NULL';" - + "update " + foreign_keys_combined + " set UPDATE_RULE=4 where update_referential_action_desc='SET_DEFAULT';"); + stmt.execute("update " + foreign_keys_combined_tableName + " set DELETE_RULE=3 where delete_referential_action_desc='NO_ACTION';" + + "update " + foreign_keys_combined_tableName + " set DELETE_RULE=0 where delete_referential_action_desc='Cascade';" + + "update " + foreign_keys_combined_tableName + " set DELETE_RULE=2 where delete_referential_action_desc='SET_NULL';" + + "update " + foreign_keys_combined_tableName + " set DELETE_RULE=4 where delete_referential_action_desc='SET_DEFAULT';" + + "update " + foreign_keys_combined_tableName + " set UPDATE_RULE=3 where update_referential_action_desc='NO_ACTION';" + + "update " + foreign_keys_combined_tableName + " set UPDATE_RULE=0 where update_referential_action_desc='Cascade';" + + "update " + foreign_keys_combined_tableName + " set UPDATE_RULE=2 where update_referential_action_desc='SET_NULL';" + + "update " + foreign_keys_combined_tableName + " set UPDATE_RULE=4 where update_referential_action_desc='SET_DEFAULT';"); /** * now, the #foreign_keys_combined_results table has the correct values for DELETE_RULE and UPDATE_RULE. Then we can return the result of the @@ -960,7 +961,7 @@ private ResultSet getResultSetForForeignKeyInformation(String cat, */ return stmt.executeQuery( "select PKTABLE_QUALIFIER,PKTABLE_OWNER,PKTABLE_NAME,PKCOLUMN_NAME,FKTABLE_QUALIFIER,FKTABLE_OWNER,FKTABLE_NAME,FKCOLUMN_NAME,KEY_SEQ,UPDATE_RULE,DELETE_RULE,FK_NAME,PK_NAME,DEFERRABILITY from " - + foreign_keys_combined + " order by FKTABLE_QUALIFIER, FKTABLE_OWNER, FKTABLE_NAME, KEY_SEQ"); + + foreign_keys_combined_tableName + " order by FKTABLE_QUALIFIER, FKTABLE_OWNER, FKTABLE_NAME, KEY_SEQ"); } private final static String[] getIndexInfoColumnNames = {/* 1 */ TABLE_CAT, /* 2 */ TABLE_SCHEM, /* 3 */ TABLE_NAME, /* 4 */ NON_UNIQUE, From 3b8955fcc9a39a85c76919fa6430384df6459754 Mon Sep 17 00:00:00 2001 From: Shawn Sun Date: Mon, 11 Sep 2017 11:27:54 -0700 Subject: [PATCH 05/15] add switchCatalogs() from the current implementation --- .../jdbc/SQLServerDatabaseMetaData.java | 179 +++++++++--------- 1 file changed, 93 insertions(+), 86 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java index e8ab73601..7159fe949 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java @@ -822,7 +822,7 @@ public java.sql.ResultSet getClientInfoProperties() throws SQLException { + (null == schema || schema.trim().length() != 0 ? ", @pktable_owner=" + schema : "") + (null == cat || cat.trim().length() != 0 ? ", @pktable_qualifier=" + cat : ""); - return getResultSetForForeignKeyInformation(sp_fkeys_Query); + return getResultSetForForeignKeyInformation(sp_fkeys_Query, cat); } /* L0 */ public String getExtraNameCharacters() throws SQLServerException { @@ -858,7 +858,7 @@ public java.sql.ResultSet getClientInfoProperties() throws SQLException { + (null == schema || schema.trim().length() != 0 ? ", @fktable_owner=" + schema : "") + (null == cat || cat.trim().length() != 0 ? ", @fktable_qualifier=" + cat : ""); - return getResultSetForForeignKeyInformation(sp_fkeys_Query); + return getResultSetForForeignKeyInformation(sp_fkeys_Query, cat); } String fkeys_results_column_definition = "PKTABLE_QUALIFIER sysname, PKTABLE_OWNER sysname, PKTABLE_NAME sysname, PKCOLUMN_NAME sysname, FKTABLE_QUALIFIER sysname, FKTABLE_OWNER sysname, FKTABLE_NAME sysname, FKCOLUMN_NAME sysname, KEY_SEQ smallint, UPDATE_RULE smallint, DELETE_RULE smallint, FK_NAME sysname, PK_NAME sysname, DEFERRABILITY smallint"; @@ -873,95 +873,102 @@ public java.sql.ResultSet getClientInfoProperties() throws SQLException { * @return * @throws SQLServerException */ - private ResultSet getResultSetForForeignKeyInformation(String sp_fkeys_Query) throws SQLServerException { + private ResultSet getResultSetForForeignKeyInformation(String sp_fkeys_Query, String cat) throws SQLServerException { String fkeys_results_tableName = "#fkeys_results"; String foreign_keys_combined_tableName = "#foreign_keys_combined_results"; String sys_foreign_keys = "sys.foreign_keys"; - SQLServerStatement stmt = null; //cannot close this statement, otherwise the returned resultset would be closed too. - - stmt = (SQLServerStatement) connection.createStatement(); - - /** - * create a temp table that has the same definition as the result of sp_fkeys: - * - * create table #fkeys_results ( - * PKTABLE_QUALIFIER sysname, - * PKTABLE_OWNER sysname, - * PKTABLE_NAME sysname, - * PKCOLUMN_NAME sysname, - * FKTABLE_QUALIFIER sysname, - * FKTABLE_OWNER sysname, - * FKTABLE_NAME sysname, - * FKCOLUMN_NAME sysname, - * KEY_SEQ smallint, - * UPDATE_RULE smallint, - * DELETE_RULE smallint, - * FK_NAME sysname, - * PK_NAME sysname, - * DEFERRABILITY smallint - * ); - * - */ - stmt.execute("create table " + fkeys_results_tableName + " (" + fkeys_results_column_definition + ")"); - - /** - * insert the results of sp_fkeys to the temp table #fkeys_results - */ - stmt.execute("insert into " + fkeys_results_tableName + sp_fkeys_Query); - - /** - * create another temp table that has 3 columns from sys.foreign_keys and the rest of columns are the same as #fkeys_results: - * - * create table #foreign_keys_combined_results ( - * name sysname, - * delete_referential_action_desc nvarchar(60), - * update_referential_action_desc nvarchar(60), - * ...... - * ...... - * ...... - * ); - * - */ - stmt.execute("create table " + foreign_keys_combined_tableName + " (" + foreign_keys_combined_column_definition + ")"); - + String orgCat = switchCatalogs(cat); + try { + // cannot close this statement, otherwise the returned resultset would be closed too. + SQLServerStatement stmt = (SQLServerStatement) connection.createStatement(); - /** - * right join the content of sys.foreign_keys and the content of #fkeys_results base on foreign key name and save the result to the new temp - * table #foreign_keys_combined_results - */ - stmt.execute("insert into " + foreign_keys_combined_tableName - + " select " + sys_foreign_keys + ".name, " + sys_foreign_keys + ".delete_referential_action_desc, " + sys_foreign_keys + ".update_referential_action_desc," - + fkeys_results_tableName + ".PKTABLE_QUALIFIER," + fkeys_results_tableName + ".PKTABLE_OWNER," + fkeys_results_tableName + ".PKTABLE_NAME," + fkeys_results_tableName + ".PKCOLUMN_NAME," - + fkeys_results_tableName + ".FKTABLE_QUALIFIER," + fkeys_results_tableName + ".FKTABLE_OWNER," + fkeys_results_tableName + ".FKTABLE_NAME," + fkeys_results_tableName + ".FKCOLUMN_NAME," - + fkeys_results_tableName + ".KEY_SEQ," + fkeys_results_tableName + ".UPDATE_RULE," + fkeys_results_tableName + ".DELETE_RULE," + fkeys_results_tableName + ".FK_NAME," + fkeys_results_tableName + ".PK_NAME," - + fkeys_results_tableName + ".DEFERRABILITY from " + sys_foreign_keys - + " right join " + fkeys_results_tableName + " on " + sys_foreign_keys + ".name=" + fkeys_results_tableName + ".FK_NAME"); - - /** - * the DELETE_RULE value and UPDATE_RULE value returned from sp_fkeys are not the same as required by JDBC spec. therefore, we need to update - * those values to JDBC required values base on delete_referential_action_desc and update_referential_action_desc returned from sys.foreign_keys - * No Action: 3 - * Cascade: 0 - * Set Null: 2 - * Set Default: 4 - */ - stmt.execute("update " + foreign_keys_combined_tableName + " set DELETE_RULE=3 where delete_referential_action_desc='NO_ACTION';" - + "update " + foreign_keys_combined_tableName + " set DELETE_RULE=0 where delete_referential_action_desc='Cascade';" - + "update " + foreign_keys_combined_tableName + " set DELETE_RULE=2 where delete_referential_action_desc='SET_NULL';" - + "update " + foreign_keys_combined_tableName + " set DELETE_RULE=4 where delete_referential_action_desc='SET_DEFAULT';" - + "update " + foreign_keys_combined_tableName + " set UPDATE_RULE=3 where update_referential_action_desc='NO_ACTION';" - + "update " + foreign_keys_combined_tableName + " set UPDATE_RULE=0 where update_referential_action_desc='Cascade';" - + "update " + foreign_keys_combined_tableName + " set UPDATE_RULE=2 where update_referential_action_desc='SET_NULL';" - + "update " + foreign_keys_combined_tableName + " set UPDATE_RULE=4 where update_referential_action_desc='SET_DEFAULT';"); - - /** - * now, the #foreign_keys_combined_results table has the correct values for DELETE_RULE and UPDATE_RULE. Then we can return the result of the - * table with the same definition of the resultset return by sp_fkeys (same column definition and same order). - */ - return stmt.executeQuery( - "select PKTABLE_QUALIFIER,PKTABLE_OWNER,PKTABLE_NAME,PKCOLUMN_NAME,FKTABLE_QUALIFIER,FKTABLE_OWNER,FKTABLE_NAME,FKCOLUMN_NAME,KEY_SEQ,UPDATE_RULE,DELETE_RULE,FK_NAME,PK_NAME,DEFERRABILITY from " - + foreign_keys_combined_tableName + " order by FKTABLE_QUALIFIER, FKTABLE_OWNER, FKTABLE_NAME, KEY_SEQ"); + /** + * create a temp table that has the same definition as the result of sp_fkeys: + * + * create table #fkeys_results ( + * PKTABLE_QUALIFIER sysname, + * PKTABLE_OWNER sysname, + * PKTABLE_NAME sysname, + * PKCOLUMN_NAME sysname, + * FKTABLE_QUALIFIER sysname, + * FKTABLE_OWNER sysname, + * FKTABLE_NAME sysname, + * FKCOLUMN_NAME sysname, + * KEY_SEQ smallint, + * UPDATE_RULE smallint, + * DELETE_RULE smallint, + * FK_NAME sysname, + * PK_NAME sysname, + * DEFERRABILITY smallint + * ); + * + */ + stmt.execute("create table " + fkeys_results_tableName + " (" + fkeys_results_column_definition + ")"); + + /** + * insert the results of sp_fkeys to the temp table #fkeys_results + */ + stmt.execute("insert into " + fkeys_results_tableName + sp_fkeys_Query); + + /** + * create another temp table that has 3 columns from sys.foreign_keys and the rest of columns are the same as #fkeys_results: + * + * create table #foreign_keys_combined_results ( + * name sysname, + * delete_referential_action_desc nvarchar(60), + * update_referential_action_desc nvarchar(60), + * ...... + * ...... + * ...... + * ); + * + */ + stmt.execute("create table " + foreign_keys_combined_tableName + " (" + foreign_keys_combined_column_definition + ")"); + + + /** + * right join the content of sys.foreign_keys and the content of #fkeys_results base on foreign key name and save the result to the new temp + * table #foreign_keys_combined_results + */ + stmt.execute("insert into " + foreign_keys_combined_tableName + + " select " + sys_foreign_keys + ".name, " + sys_foreign_keys + ".delete_referential_action_desc, " + sys_foreign_keys + ".update_referential_action_desc," + + fkeys_results_tableName + ".PKTABLE_QUALIFIER," + fkeys_results_tableName + ".PKTABLE_OWNER," + fkeys_results_tableName + ".PKTABLE_NAME," + fkeys_results_tableName + ".PKCOLUMN_NAME," + + fkeys_results_tableName + ".FKTABLE_QUALIFIER," + fkeys_results_tableName + ".FKTABLE_OWNER," + fkeys_results_tableName + ".FKTABLE_NAME," + fkeys_results_tableName + ".FKCOLUMN_NAME," + + fkeys_results_tableName + ".KEY_SEQ," + fkeys_results_tableName + ".UPDATE_RULE," + fkeys_results_tableName + ".DELETE_RULE," + fkeys_results_tableName + ".FK_NAME," + fkeys_results_tableName + ".PK_NAME," + + fkeys_results_tableName + ".DEFERRABILITY from " + sys_foreign_keys + + " right join " + fkeys_results_tableName + " on " + sys_foreign_keys + ".name=" + fkeys_results_tableName + ".FK_NAME"); + + /** + * the DELETE_RULE value and UPDATE_RULE value returned from sp_fkeys are not the same as required by JDBC spec. therefore, we need to update + * those values to JDBC required values base on delete_referential_action_desc and update_referential_action_desc returned from sys.foreign_keys + * No Action: 3 + * Cascade: 0 + * Set Null: 2 + * Set Default: 4 + */ + stmt.execute("update " + foreign_keys_combined_tableName + " set DELETE_RULE=3 where delete_referential_action_desc='NO_ACTION';" + + "update " + foreign_keys_combined_tableName + " set DELETE_RULE=0 where delete_referential_action_desc='Cascade';" + + "update " + foreign_keys_combined_tableName + " set DELETE_RULE=2 where delete_referential_action_desc='SET_NULL';" + + "update " + foreign_keys_combined_tableName + " set DELETE_RULE=4 where delete_referential_action_desc='SET_DEFAULT';" + + "update " + foreign_keys_combined_tableName + " set UPDATE_RULE=3 where update_referential_action_desc='NO_ACTION';" + + "update " + foreign_keys_combined_tableName + " set UPDATE_RULE=0 where update_referential_action_desc='Cascade';" + + "update " + foreign_keys_combined_tableName + " set UPDATE_RULE=2 where update_referential_action_desc='SET_NULL';" + + "update " + foreign_keys_combined_tableName + " set UPDATE_RULE=4 where update_referential_action_desc='SET_DEFAULT';"); + + /** + * now, the #foreign_keys_combined_results table has the correct values for DELETE_RULE and UPDATE_RULE. Then we can return the result of the + * table with the same definition of the resultset return by sp_fkeys (same column definition and same order). + */ + return stmt.executeQuery( + "select PKTABLE_QUALIFIER,PKTABLE_OWNER,PKTABLE_NAME,PKCOLUMN_NAME,FKTABLE_QUALIFIER,FKTABLE_OWNER,FKTABLE_NAME,FKCOLUMN_NAME,KEY_SEQ,UPDATE_RULE,DELETE_RULE,FK_NAME,PK_NAME,DEFERRABILITY from " + + foreign_keys_combined_tableName + " order by FKTABLE_QUALIFIER, FKTABLE_OWNER, FKTABLE_NAME, KEY_SEQ"); + } + finally { + if (null != orgCat) { + connection.setCatalog(orgCat); + } + } } private final static String[] getIndexInfoColumnNames = {/* 1 */ TABLE_CAT, /* 2 */ TABLE_SCHEM, /* 3 */ TABLE_NAME, /* 4 */ NON_UNIQUE, From bf24e104da69555e66b168a0b12ee887c0fa5c8f Mon Sep 17 00:00:00 2001 From: Shawn Sun Date: Mon, 11 Sep 2017 12:18:25 -0700 Subject: [PATCH 06/15] fix getCrossReference() --- .../jdbc/SQLServerDatabaseMetaData.java | 56 +++++-------------- 1 file changed, 14 insertions(+), 42 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java index 7159fe949..dc4c4e899 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java @@ -17,6 +17,7 @@ import java.text.MessageFormat; import java.util.EnumMap; import java.util.Properties; +import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; @@ -729,10 +730,6 @@ public java.sql.ResultSet getClientInfoProperties() throws SQLException { return rs; } - private final static String[] pkfkColumnNames = {/* 1 */ PKTABLE_CAT, /* 2 */ PKTABLE_SCHEM, /* 3 */ PKTABLE_NAME, /* 4 */ PKCOLUMN_NAME, - /* 5 */ FKTABLE_CAT, /* 6 */ FKTABLE_SCHEM, /* 7 */ FKTABLE_NAME, /* 8 */ FKCOLUMN_NAME, /* 9 */ KEY_SEQ, /* 10 */ UPDATE_RULE, - /* 11 */ DELETE_RULE, /* 12 */ FK_NAME, /* 13 */ PK_NAME, /* 14 */ DEFERRABILITY}; - /* L0 */ public java.sql.ResultSet getCrossReference(String cat1, String schem1, String tab1, @@ -743,19 +740,15 @@ public java.sql.ResultSet getClientInfoProperties() throws SQLException { loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString()); } checkClosed(); - /* - * sp_fkeys [ @pktable_name = ] 'pktable_name' [ , [ @pktable_owner = ] 'pktable_owner' ] [ , [ @pktable_qualifier = ] 'pktable_qualifier' ] { - * , [ @fktable_name = ] 'fktable_name' } [ , [ @fktable_owner = ] 'fktable_owner' ] [ , [ @fktable_qualifier = ] 'fktable_qualifier' ] - */ - String[] arguments = new String[6]; - arguments[0] = tab1; // pktable_name - arguments[1] = schem1; - arguments[2] = cat1; - arguments[3] = tab2; - arguments[4] = schem2; - arguments[5] = cat2; - return getResultSetWithProvidedColumnNames(null, CallableHandles.SP_FKEYS, arguments, pkfkColumnNames); + String sp_fkeys_Query = " exec sp_fkeys @pktable_name=" + tab1 + + (null == schem1 || schem1.trim().length() != 0 ? ", @pktable_owner=" + schem1 : "") + + (null == cat1 || cat1.trim().length() != 0 ? ", @pktable_qualifier=" + cat1 : "" ) + + ", @fktable_name=" + tab2 + + (null == schem2 || schem2.trim().length() != 0 ? ", @fktable_owner=" + schem2 : "") + + (null == cat2 || cat2.trim().length() != 0 ? ", @fktable_qualifier=" + cat2 : ""); + + return getResultSetForForeignKeyInformation(sp_fkeys_Query, null); } /* L0 */ public String getDatabaseProductName() throws SQLServerException { @@ -806,17 +799,6 @@ public java.sql.ResultSet getClientInfoProperties() throws SQLException { loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString()); } checkClosed(); - /* - * sp_fkeys [ @pktable_name = ] 'pktable_name' [ , [ @pktable_owner = ] 'pktable_owner' ] [ , [ @pktable_qualifier = ] 'pktable_qualifier' ] { - * , [ @fktable_name = ] 'fktable_name' } [ , [ @fktable_owner = ] 'fktable_owner' ] [ , [ @fktable_qualifier = ] 'fktable_qualifier' ] - */ - String[] arguments = new String[6]; - arguments[0] = table; // pktable_name - arguments[1] = schema; - arguments[2] = cat; - arguments[3] = null; // fktable_name - arguments[4] = null; - arguments[5] = null; String sp_fkeys_Query = " exec sp_fkeys @pktable_name=" + table + (null == schema || schema.trim().length() != 0 ? ", @pktable_owner=" + schema : "") @@ -842,17 +824,6 @@ public java.sql.ResultSet getClientInfoProperties() throws SQLException { loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString()); } checkClosed(); - /* - * sp_fkeys [ @pktable_name = ] 'pktable_name' [ , [ @pktable_owner = ] 'pktable_owner' ] [ , [ @pktable_qualifier = ] 'pktable_qualifier' ] { - * , [ @fktable_name = ] 'fktable_name' } [ , [ @fktable_owner = ] 'fktable_owner' ] [ , [ @fktable_qualifier = ] 'fktable_qualifier' ] - */ - String[] arguments = new String[6]; - arguments[0] = null; // pktable_name - arguments[1] = null; - arguments[2] = null; - arguments[3] = table; // fktable_name - arguments[4] = schema; - arguments[5] = cat; String sp_fkeys_Query = " exec sp_fkeys @fktable_name=" + table + (null == schema || schema.trim().length() != 0 ? ", @fktable_owner=" + schema : "") @@ -861,8 +832,8 @@ public java.sql.ResultSet getClientInfoProperties() throws SQLException { return getResultSetForForeignKeyInformation(sp_fkeys_Query, cat); } - String fkeys_results_column_definition = "PKTABLE_QUALIFIER sysname, PKTABLE_OWNER sysname, PKTABLE_NAME sysname, PKCOLUMN_NAME sysname, FKTABLE_QUALIFIER sysname, FKTABLE_OWNER sysname, FKTABLE_NAME sysname, FKCOLUMN_NAME sysname, KEY_SEQ smallint, UPDATE_RULE smallint, DELETE_RULE smallint, FK_NAME sysname, PK_NAME sysname, DEFERRABILITY smallint"; - String foreign_keys_combined_column_definition = "name sysname, delete_referential_action_desc nvarchar(60), update_referential_action_desc nvarchar(60)," + private String fkeys_results_column_definition = "PKTABLE_QUALIFIER sysname, PKTABLE_OWNER sysname, PKTABLE_NAME sysname, PKCOLUMN_NAME sysname, FKTABLE_QUALIFIER sysname, FKTABLE_OWNER sysname, FKTABLE_NAME sysname, FKCOLUMN_NAME sysname, KEY_SEQ smallint, UPDATE_RULE smallint, DELETE_RULE smallint, FK_NAME sysname, PK_NAME sysname, DEFERRABILITY smallint"; + private String foreign_keys_combined_column_definition = "name sysname, delete_referential_action_desc nvarchar(60), update_referential_action_desc nvarchar(60)," + fkeys_results_column_definition; /** @@ -874,8 +845,9 @@ public java.sql.ResultSet getClientInfoProperties() throws SQLException { * @throws SQLServerException */ private ResultSet getResultSetForForeignKeyInformation(String sp_fkeys_Query, String cat) throws SQLServerException { - String fkeys_results_tableName = "#fkeys_results"; - String foreign_keys_combined_tableName = "#foreign_keys_combined_results"; + UUID uuid = UUID.randomUUID(); + String fkeys_results_tableName = "[#fkeys_results" + uuid + "]"; + String foreign_keys_combined_tableName = "[#foreign_keys_combined_results" + uuid + "]"; String sys_foreign_keys = "sys.foreign_keys"; String orgCat = switchCatalogs(cat); From c35365a4264ca8f80ecc6a8db9f25b3568363c44 Mon Sep 17 00:00:00 2001 From: Shawn Sun Date: Mon, 11 Sep 2017 13:46:40 -0700 Subject: [PATCH 07/15] adding tests --- .../DatabaseMetaDataForeignKeyTest.java | 134 ++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataForeignKeyTest.java diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataForeignKeyTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataForeignKeyTest.java new file mode 100644 index 000000000..11493945d --- /dev/null +++ b/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataForeignKeyTest.java @@ -0,0 +1,134 @@ +/* + * Microsoft JDBC Driver for SQL Server + * + * Copyright(c) Microsoft Corporation All rights reserved. + * + * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. + */ +package com.microsoft.sqlserver.jdbc.databasemetadata; + +import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.sql.DriverManager; +import java.sql.SQLException; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.platform.runner.JUnitPlatform; +import org.junit.runner.RunWith; + +import com.microsoft.sqlserver.jdbc.SQLServerConnection; +import com.microsoft.sqlserver.jdbc.SQLServerDatabaseMetaData; +import com.microsoft.sqlserver.jdbc.SQLServerException; +import com.microsoft.sqlserver.jdbc.SQLServerResultSet; +import com.microsoft.sqlserver.jdbc.SQLServerStatement; +import com.microsoft.sqlserver.testframework.AbstractTest; +import com.microsoft.sqlserver.testframework.Utils; + +/** + * Test class for testing DatabaseMetaData with foreign keys. + */ +@RunWith(JUnitPlatform.class) +public class DatabaseMetaDataForeignKeyTest extends AbstractTest { + private static SQLServerConnection conn = null; + private static SQLServerStatement stmt = null; + + private static String table1 = "DatabaseMetaDataForeignKeyTest_table_1"; + private static String table2 = "DatabaseMetaDataForeignKeyTest_table_2"; + private static String table3 = "DatabaseMetaDataForeignKeyTest_table_3"; + private static String table4 = "DatabaseMetaDataForeignKeyTest_table_4"; + private static String table5 = "DatabaseMetaDataForeignKeyTest_table_5"; + + private static String schema = null; + private static String catalog = null; + + @BeforeAll + private static void setupVariation() throws SQLException { + conn = (SQLServerConnection) DriverManager.getConnection(connectionString); + SQLServerStatement stmt = (SQLServerStatement) conn.createStatement(); + + catalog = conn.getCatalog(); + schema = conn.getSchema(); + + connection.createStatement().executeUpdate("if object_id('" + table1 + "','U') is not null drop table " + table1); + + connection.createStatement().executeUpdate("if object_id('" + table2 + "','U') is not null drop table " + table2); + stmt.execute("Create table " + table2 + " (c21 int NOT NULL PRIMARY KEY)"); + + connection.createStatement().executeUpdate("if object_id('" + table3 + "','U') is not null drop table " + table3); + stmt.execute("Create table " + table3 + " (c31 int NOT NULL PRIMARY KEY)"); + + connection.createStatement().executeUpdate("if object_id('" + table4 + "','U') is not null drop table " + table4); + stmt.execute("Create table " + table4 + " (c41 int NOT NULL PRIMARY KEY)"); + + connection.createStatement().executeUpdate("if object_id('" + table5 + "','U') is not null drop table " + table5); + stmt.execute("Create table " + table5 + " (c51 int NOT NULL PRIMARY KEY)"); + + connection.createStatement().executeUpdate("if object_id('" + table1 + "','U') is not null drop table " + table1); + stmt.execute("Create table " + table1 + " (c11 int primary key," + + " c12 int FOREIGN KEY REFERENCES " + table2 + "(c21) ON DELETE no action ON UPDATE set default," + + " c13 int FOREIGN KEY REFERENCES " + table3 + "(c31) ON DELETE cascade ON UPDATE set null," + + " c14 int FOREIGN KEY REFERENCES " + table4 + "(c41) ON DELETE set null ON UPDATE cascade," + + " c15 int FOREIGN KEY REFERENCES " + table5 + "(c51) ON DELETE set default ON UPDATE no action," + + ")"); + } + + @AfterAll + private static void terminateVariation() throws SQLException { + conn = (SQLServerConnection) DriverManager.getConnection(connectionString); + stmt = (SQLServerStatement) conn.createStatement(); + + Utils.dropTableIfExists(table1, stmt); + Utils.dropTableIfExists(table2, stmt); + Utils.dropTableIfExists(table3, stmt); + Utils.dropTableIfExists(table4, stmt); + Utils.dropTableIfExists(table5, stmt); + } + + @Test + public void testGetImportedKeys() throws SQLServerException { + SQLServerDatabaseMetaData dmd = (SQLServerDatabaseMetaData) connection.getMetaData(); + + SQLServerResultSet rs1 = (SQLServerResultSet) dmd.getImportedKeys(null, null, table1); + validateResults(rs1); + +// SQLServerResultSet rs2 = (SQLServerResultSet) dmd.getImportedKeys("", "", table1); +// validateResults(rs2); + + SQLServerResultSet rs3 = (SQLServerResultSet) dmd.getImportedKeys("test", "dbo", table1); + validateResults(rs3); + + } + + private void validateResults(SQLServerResultSet rs) throws SQLServerException { + int expectedRowCount = 4; + int rowCount = 0; + + rs.next(); + assertEquals(4, rs.getInt("UPDATE_RULE")); + assertEquals(3, rs.getInt("DELETE_RULE")); + rowCount++; + + rs.next(); + assertEquals(2, rs.getInt("UPDATE_RULE")); + assertEquals(0, rs.getInt("DELETE_RULE")); + rowCount++; + + rs.next(); + assertEquals(0, rs.getInt("UPDATE_RULE")); + assertEquals(2, rs.getInt("DELETE_RULE")); + rowCount++; + + rs.next(); + assertEquals(3, rs.getInt("UPDATE_RULE")); + assertEquals(4, rs.getInt("DELETE_RULE")); + rowCount++; + + if(expectedRowCount != rowCount) { + assertEquals(expectedRowCount, rowCount, "number of foreign key entries is incorrect."); + } + } + +} From 5dbb223ed994c160c403734e751054b52171903d Mon Sep 17 00:00:00 2001 From: Shawn Sun Date: Mon, 11 Sep 2017 15:01:41 -0700 Subject: [PATCH 08/15] fix scenario when catalog is "" and add some tests for it --- .../jdbc/SQLServerDatabaseMetaData.java | 23 +++--- .../DatabaseMetaDataForeignKeyTest.java | 74 +++++++++++++++++-- 2 files changed, 79 insertions(+), 18 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java index dc4c4e899..4e7f1a5f2 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java @@ -742,11 +742,11 @@ public java.sql.ResultSet getClientInfoProperties() throws SQLException { checkClosed(); String sp_fkeys_Query = " exec sp_fkeys @pktable_name=" + tab1 - + (null == schem1 || schem1.trim().length() != 0 ? ", @pktable_owner=" + schem1 : "") - + (null == cat1 || cat1.trim().length() != 0 ? ", @pktable_qualifier=" + cat1 : "" ) + + (null == schem1 ? ", @pktable_owner=null" : ", @pktable_owner='" + schem1 + "'") + + (null == cat1 ? ", @pktable_qualifier=null" : ", @pktable_qualifier='" + cat1 + "'") + ", @fktable_name=" + tab2 - + (null == schem2 || schem2.trim().length() != 0 ? ", @fktable_owner=" + schem2 : "") - + (null == cat2 || cat2.trim().length() != 0 ? ", @fktable_qualifier=" + cat2 : ""); + + (null == schem2 ? ", @fktable_owner=null" : ", @fktable_owner='" + schem2 + "'") + + (null == cat2 ? ", @fktable_qualifier=null" : ", @fktable_qualifier='" + cat2 + "'"); return getResultSetForForeignKeyInformation(sp_fkeys_Query, null); } @@ -801,8 +801,8 @@ public java.sql.ResultSet getClientInfoProperties() throws SQLException { checkClosed(); String sp_fkeys_Query = " exec sp_fkeys @pktable_name=" + table - + (null == schema || schema.trim().length() != 0 ? ", @pktable_owner=" + schema : "") - + (null == cat || cat.trim().length() != 0 ? ", @pktable_qualifier=" + cat : ""); + + (null == schema ? ", @pktable_owner=null" : ", @pktable_owner='" + schema + "'") + + (null == cat ? ", @pktable_qualifier=null" : ", @pktable_qualifier='" + cat + "'"); return getResultSetForForeignKeyInformation(sp_fkeys_Query, cat); } @@ -826,8 +826,8 @@ public java.sql.ResultSet getClientInfoProperties() throws SQLException { checkClosed(); String sp_fkeys_Query = " exec sp_fkeys @fktable_name=" + table - + (null == schema || schema.trim().length() != 0 ? ", @fktable_owner=" + schema : "") - + (null == cat || cat.trim().length() != 0 ? ", @fktable_qualifier=" + cat : ""); + + (null == schema ? ", @fktable_owner=null" : ", @fktable_owner='" + schema + "'") + + (null == cat ? ", @fktable_qualifier=null" : ", @fktable_qualifier='" + cat + "'"); return getResultSetForForeignKeyInformation(sp_fkeys_Query, cat); } @@ -849,8 +849,11 @@ private ResultSet getResultSetForForeignKeyInformation(String sp_fkeys_Query, St String fkeys_results_tableName = "[#fkeys_results" + uuid + "]"; String foreign_keys_combined_tableName = "[#foreign_keys_combined_results" + uuid + "]"; String sys_foreign_keys = "sys.foreign_keys"; - - String orgCat = switchCatalogs(cat); + + String orgCat = null; + if (null != cat && cat.trim().length() != 0) { + orgCat = switchCatalogs(cat); + } try { // cannot close this statement, otherwise the returned resultset would be closed too. SQLServerStatement stmt = (SQLServerStatement) connection.createStatement(); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataForeignKeyTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataForeignKeyTest.java index 11493945d..27e157d5d 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataForeignKeyTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataForeignKeyTest.java @@ -7,8 +7,8 @@ */ package com.microsoft.sqlserver.jdbc.databasemetadata; -import static org.junit.Assert.fail; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; import java.sql.DriverManager; import java.sql.SQLException; @@ -43,6 +43,8 @@ public class DatabaseMetaDataForeignKeyTest extends AbstractTest { private static String schema = null; private static String catalog = null; + + private static final String EXPECTED_ERROR_MESSAGE = "The database name component of the object qualifier must be the name of the current database."; @BeforeAll private static void setupVariation() throws SQLException { @@ -87,22 +89,34 @@ private static void terminateVariation() throws SQLException { Utils.dropTableIfExists(table5, stmt); } + /** + * test getImportedKeys() methods + * + * @throws SQLServerException + */ @Test public void testGetImportedKeys() throws SQLServerException { SQLServerDatabaseMetaData dmd = (SQLServerDatabaseMetaData) connection.getMetaData(); SQLServerResultSet rs1 = (SQLServerResultSet) dmd.getImportedKeys(null, null, table1); - validateResults(rs1); + validateGetImportedKeysResults(rs1); -// SQLServerResultSet rs2 = (SQLServerResultSet) dmd.getImportedKeys("", "", table1); -// validateResults(rs2); + SQLServerResultSet rs2 = (SQLServerResultSet) dmd.getImportedKeys(catalog, schema, table1); + validateGetImportedKeysResults(rs2); - SQLServerResultSet rs3 = (SQLServerResultSet) dmd.getImportedKeys("test", "dbo", table1); - validateResults(rs3); + SQLServerResultSet rs3 = (SQLServerResultSet) dmd.getImportedKeys(catalog, "", table1); + validateGetImportedKeysResults(rs3); + try { + dmd.getImportedKeys("", schema, table1); + fail("Exception is not thrown."); + } + catch (SQLServerException e) { + assertEquals(EXPECTED_ERROR_MESSAGE, e.getMessage()); + } } - - private void validateResults(SQLServerResultSet rs) throws SQLServerException { + + private void validateGetImportedKeysResults(SQLServerResultSet rs) throws SQLServerException { int expectedRowCount = 4; int rowCount = 0; @@ -130,5 +144,49 @@ private void validateResults(SQLServerResultSet rs) throws SQLServerException { assertEquals(expectedRowCount, rowCount, "number of foreign key entries is incorrect."); } } + + /** + * test getExportedKeys() methods + * + * @throws SQLServerException + */ + @Test + public void testGetExportedKeys() throws SQLServerException { + String[] tableNames = {table2, table3, table4, table5}; + int[][] values = { + // expected UPDATE_RULE, expected DELETE_RULE + {4, 3}, + {2, 0}, + {0, 2}, + {3, 4} + }; + + SQLServerDatabaseMetaData dmd = (SQLServerDatabaseMetaData) connection.getMetaData(); + for (int i = 0; i < tableNames.length; i++) { + String table = tableNames[i]; + SQLServerResultSet rs1 = (SQLServerResultSet) dmd.getExportedKeys(null, null, table); + rs1.next(); + assertEquals(values[i][0], rs1.getInt("UPDATE_RULE")); + assertEquals(values[i][1], rs1.getInt("DELETE_RULE")); + + SQLServerResultSet rs2 = (SQLServerResultSet) dmd.getExportedKeys(catalog, schema, table); + rs2.next(); + assertEquals(values[i][0], rs2.getInt("UPDATE_RULE")); + assertEquals(values[i][1], rs2.getInt("DELETE_RULE")); + + SQLServerResultSet rs3 = (SQLServerResultSet) dmd.getExportedKeys(catalog, "", table); + rs3.next(); + assertEquals(values[i][0], rs3.getInt("UPDATE_RULE")); + assertEquals(values[i][1], rs3.getInt("DELETE_RULE")); + + try { + dmd.getExportedKeys("", schema, table); + fail("Exception is not thrown."); + } + catch (SQLServerException e) { + assertEquals(EXPECTED_ERROR_MESSAGE, e.getMessage()); + } + } + } } From e7182ed0a73bce44cd4e39fdb4fe29f30f984ca7 Mon Sep 17 00:00:00 2001 From: Shawn Sun Date: Mon, 11 Sep 2017 15:27:00 -0700 Subject: [PATCH 09/15] add tests for getCrossReference() --- .../DatabaseMetaDataForeignKeyTest.java | 56 +++++++++++++++++-- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataForeignKeyTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataForeignKeyTest.java index 27e157d5d..9ce309fec 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataForeignKeyTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataForeignKeyTest.java @@ -164,24 +164,70 @@ public void testGetExportedKeys() throws SQLServerException { SQLServerDatabaseMetaData dmd = (SQLServerDatabaseMetaData) connection.getMetaData(); for (int i = 0; i < tableNames.length; i++) { - String table = tableNames[i]; - SQLServerResultSet rs1 = (SQLServerResultSet) dmd.getExportedKeys(null, null, table); + String pkTable = tableNames[i]; + SQLServerResultSet rs1 = (SQLServerResultSet) dmd.getExportedKeys(null, null, pkTable); rs1.next(); assertEquals(values[i][0], rs1.getInt("UPDATE_RULE")); assertEquals(values[i][1], rs1.getInt("DELETE_RULE")); - SQLServerResultSet rs2 = (SQLServerResultSet) dmd.getExportedKeys(catalog, schema, table); + SQLServerResultSet rs2 = (SQLServerResultSet) dmd.getExportedKeys(catalog, schema, pkTable); rs2.next(); assertEquals(values[i][0], rs2.getInt("UPDATE_RULE")); assertEquals(values[i][1], rs2.getInt("DELETE_RULE")); - SQLServerResultSet rs3 = (SQLServerResultSet) dmd.getExportedKeys(catalog, "", table); + SQLServerResultSet rs3 = (SQLServerResultSet) dmd.getExportedKeys(catalog, "", pkTable); rs3.next(); assertEquals(values[i][0], rs3.getInt("UPDATE_RULE")); assertEquals(values[i][1], rs3.getInt("DELETE_RULE")); try { - dmd.getExportedKeys("", schema, table); + dmd.getExportedKeys("", schema, pkTable); + fail("Exception is not thrown."); + } + catch (SQLServerException e) { + assertEquals(EXPECTED_ERROR_MESSAGE, e.getMessage()); + } + } + } + + /** + * test getCrossReference() methods + * + * @throws SQLServerException + */ + @Test + public void testGetCrossReference() throws SQLServerException { + String fkTable = table1; + String[] tableNames = {table2, table3, table4, table5}; + int[][] values = { + // expected UPDATE_RULE, expected DELETE_RULE + {4, 3}, + {2, 0}, + {0, 2}, + {3, 4} + }; + + SQLServerDatabaseMetaData dmd = (SQLServerDatabaseMetaData) connection.getMetaData(); + + for (int i = 0; i < tableNames.length; i++) { + String pkTable = tableNames[i]; + SQLServerResultSet rs1 = (SQLServerResultSet) dmd.getCrossReference(null, null, pkTable, null, null, fkTable); + rs1.next(); + assertEquals(values[i][0], rs1.getInt("UPDATE_RULE")); + assertEquals(values[i][1], rs1.getInt("DELETE_RULE")); + + SQLServerResultSet rs2 = (SQLServerResultSet) dmd.getCrossReference(catalog, schema, pkTable, catalog, schema, fkTable); + rs2.next(); + assertEquals(values[i][0], rs2.getInt("UPDATE_RULE")); + assertEquals(values[i][1], rs2.getInt("DELETE_RULE")); + + SQLServerResultSet rs3 = (SQLServerResultSet) dmd.getCrossReference(catalog, "", pkTable, catalog, "", fkTable); + rs3.next(); + assertEquals(values[i][0], rs3.getInt("UPDATE_RULE")); + assertEquals(values[i][1], rs3.getInt("DELETE_RULE")); + + try { + dmd.getCrossReference("", schema, pkTable, "", schema, fkTable); fail("Exception is not thrown."); } catch (SQLServerException e) { From d6668e0f30bb47b57284d4fb9fa959af4716ac43 Mon Sep 17 00:00:00 2001 From: Shawn Sun Date: Mon, 11 Sep 2017 15:47:38 -0700 Subject: [PATCH 10/15] use batch instead of each statement.execute to reduce performance impact --- .../jdbc/SQLServerDatabaseMetaData.java | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java index 4e7f1a5f2..87816b049 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java @@ -8,6 +8,7 @@ package com.microsoft.sqlserver.jdbc; +import java.sql.BatchUpdateException; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.DriverPropertyInfo; @@ -879,12 +880,12 @@ private ResultSet getResultSetForForeignKeyInformation(String sp_fkeys_Query, St * ); * */ - stmt.execute("create table " + fkeys_results_tableName + " (" + fkeys_results_column_definition + ")"); + stmt.addBatch("create table " + fkeys_results_tableName + " (" + fkeys_results_column_definition + ")"); /** * insert the results of sp_fkeys to the temp table #fkeys_results */ - stmt.execute("insert into " + fkeys_results_tableName + sp_fkeys_Query); + stmt.addBatch("insert into " + fkeys_results_tableName + sp_fkeys_Query); /** * create another temp table that has 3 columns from sys.foreign_keys and the rest of columns are the same as #fkeys_results: @@ -899,14 +900,14 @@ private ResultSet getResultSetForForeignKeyInformation(String sp_fkeys_Query, St * ); * */ - stmt.execute("create table " + foreign_keys_combined_tableName + " (" + foreign_keys_combined_column_definition + ")"); + stmt.addBatch("create table " + foreign_keys_combined_tableName + " (" + foreign_keys_combined_column_definition + ")"); /** * right join the content of sys.foreign_keys and the content of #fkeys_results base on foreign key name and save the result to the new temp * table #foreign_keys_combined_results */ - stmt.execute("insert into " + foreign_keys_combined_tableName + stmt.addBatch("insert into " + foreign_keys_combined_tableName + " select " + sys_foreign_keys + ".name, " + sys_foreign_keys + ".delete_referential_action_desc, " + sys_foreign_keys + ".update_referential_action_desc," + fkeys_results_tableName + ".PKTABLE_QUALIFIER," + fkeys_results_tableName + ".PKTABLE_OWNER," + fkeys_results_tableName + ".PKTABLE_NAME," + fkeys_results_tableName + ".PKCOLUMN_NAME," + fkeys_results_tableName + ".FKTABLE_QUALIFIER," + fkeys_results_tableName + ".FKTABLE_OWNER," + fkeys_results_tableName + ".FKTABLE_NAME," + fkeys_results_tableName + ".FKCOLUMN_NAME," @@ -922,7 +923,7 @@ private ResultSet getResultSetForForeignKeyInformation(String sp_fkeys_Query, St * Set Null: 2 * Set Default: 4 */ - stmt.execute("update " + foreign_keys_combined_tableName + " set DELETE_RULE=3 where delete_referential_action_desc='NO_ACTION';" + stmt.addBatch("update " + foreign_keys_combined_tableName + " set DELETE_RULE=3 where delete_referential_action_desc='NO_ACTION';" + "update " + foreign_keys_combined_tableName + " set DELETE_RULE=0 where delete_referential_action_desc='Cascade';" + "update " + foreign_keys_combined_tableName + " set DELETE_RULE=2 where delete_referential_action_desc='SET_NULL';" + "update " + foreign_keys_combined_tableName + " set DELETE_RULE=4 where delete_referential_action_desc='SET_DEFAULT';" @@ -930,10 +931,17 @@ private ResultSet getResultSetForForeignKeyInformation(String sp_fkeys_Query, St + "update " + foreign_keys_combined_tableName + " set UPDATE_RULE=0 where update_referential_action_desc='Cascade';" + "update " + foreign_keys_combined_tableName + " set UPDATE_RULE=2 where update_referential_action_desc='SET_NULL';" + "update " + foreign_keys_combined_tableName + " set UPDATE_RULE=4 where update_referential_action_desc='SET_DEFAULT';"); - + + try { + stmt.executeBatch(); + } + catch (BatchUpdateException e) { + throw new SQLServerException(e.getMessage(), e.getSQLState(), e.getErrorCode(), null); + } + /** - * now, the #foreign_keys_combined_results table has the correct values for DELETE_RULE and UPDATE_RULE. Then we can return the result of the - * table with the same definition of the resultset return by sp_fkeys (same column definition and same order). + * now, the #foreign_keys_combined_results table has the correct values for DELETE_RULE and UPDATE_RULE. Then we can return the result of + * the table with the same definition of the resultset return by sp_fkeys (same column definition and same order). */ return stmt.executeQuery( "select PKTABLE_QUALIFIER,PKTABLE_OWNER,PKTABLE_NAME,PKCOLUMN_NAME,FKTABLE_QUALIFIER,FKTABLE_OWNER,FKTABLE_NAME,FKCOLUMN_NAME,KEY_SEQ,UPDATE_RULE,DELETE_RULE,FK_NAME,PK_NAME,DEFERRABILITY from " From 1d800f93231fb903fdf9bb34a7df5c7899bf1d69 Mon Sep 17 00:00:00 2001 From: Shawn Sun Date: Mon, 11 Sep 2017 16:29:13 -0700 Subject: [PATCH 11/15] change column names as JDBC required --- .../sqlserver/jdbc/SQLServerDatabaseMetaData.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java index 87816b049..9ee344d0c 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java @@ -742,10 +742,10 @@ public java.sql.ResultSet getClientInfoProperties() throws SQLException { } checkClosed(); - String sp_fkeys_Query = " exec sp_fkeys @pktable_name=" + tab1 + String sp_fkeys_Query = " exec sp_fkeys @pktable_name=[" + tab1 + "]" + (null == schem1 ? ", @pktable_owner=null" : ", @pktable_owner='" + schem1 + "'") + (null == cat1 ? ", @pktable_qualifier=null" : ", @pktable_qualifier='" + cat1 + "'") - + ", @fktable_name=" + tab2 + + ", @fktable_name=[" + tab2 + "]" + (null == schem2 ? ", @fktable_owner=null" : ", @fktable_owner='" + schem2 + "'") + (null == cat2 ? ", @fktable_qualifier=null" : ", @fktable_qualifier='" + cat2 + "'"); @@ -801,7 +801,7 @@ public java.sql.ResultSet getClientInfoProperties() throws SQLException { } checkClosed(); - String sp_fkeys_Query = " exec sp_fkeys @pktable_name=" + table + String sp_fkeys_Query = " exec sp_fkeys @pktable_name=[" + table + "]" + (null == schema ? ", @pktable_owner=null" : ", @pktable_owner='" + schema + "'") + (null == cat ? ", @pktable_qualifier=null" : ", @pktable_qualifier='" + cat + "'"); @@ -826,7 +826,7 @@ public java.sql.ResultSet getClientInfoProperties() throws SQLException { } checkClosed(); - String sp_fkeys_Query = " exec sp_fkeys @fktable_name=" + table + String sp_fkeys_Query = " exec sp_fkeys @fktable_name=[" + table + "]" + (null == schema ? ", @fktable_owner=null" : ", @fktable_owner='" + schema + "'") + (null == cat ? ", @fktable_qualifier=null" : ", @fktable_qualifier='" + cat + "'"); @@ -944,7 +944,7 @@ private ResultSet getResultSetForForeignKeyInformation(String sp_fkeys_Query, St * the table with the same definition of the resultset return by sp_fkeys (same column definition and same order). */ return stmt.executeQuery( - "select PKTABLE_QUALIFIER,PKTABLE_OWNER,PKTABLE_NAME,PKCOLUMN_NAME,FKTABLE_QUALIFIER,FKTABLE_OWNER,FKTABLE_NAME,FKCOLUMN_NAME,KEY_SEQ,UPDATE_RULE,DELETE_RULE,FK_NAME,PK_NAME,DEFERRABILITY from " + "select PKTABLE_QUALIFIER as 'PKTABLE_CAT',PKTABLE_OWNER as 'PKTABLE_SCHEM',PKTABLE_NAME,PKCOLUMN_NAME,FKTABLE_QUALIFIER as 'FKTABLE_CAT',FKTABLE_OWNER as 'FKTABLE_SCHEM',FKTABLE_NAME,FKCOLUMN_NAME,KEY_SEQ,UPDATE_RULE,DELETE_RULE,FK_NAME,PK_NAME,DEFERRABILITY from " + foreign_keys_combined_tableName + " order by FKTABLE_QUALIFIER, FKTABLE_OWNER, FKTABLE_NAME, KEY_SEQ"); } finally { From 2a39ec03d82427c1543e128686abc68096481320 Mon Sep 17 00:00:00 2001 From: Shawn Sun Date: Tue, 12 Sep 2017 09:35:09 -0700 Subject: [PATCH 12/15] replace [ with ' --- .../sqlserver/jdbc/SQLServerDatabaseMetaData.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java index 9ee344d0c..e5d1126e7 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java @@ -742,10 +742,10 @@ public java.sql.ResultSet getClientInfoProperties() throws SQLException { } checkClosed(); - String sp_fkeys_Query = " exec sp_fkeys @pktable_name=[" + tab1 + "]" + String sp_fkeys_Query = " exec sp_fkeys @pktable_name='" + tab1 + "'" + (null == schem1 ? ", @pktable_owner=null" : ", @pktable_owner='" + schem1 + "'") + (null == cat1 ? ", @pktable_qualifier=null" : ", @pktable_qualifier='" + cat1 + "'") - + ", @fktable_name=[" + tab2 + "]" + + ", @fktable_name='" + tab2 + "'" + (null == schem2 ? ", @fktable_owner=null" : ", @fktable_owner='" + schem2 + "'") + (null == cat2 ? ", @fktable_qualifier=null" : ", @fktable_qualifier='" + cat2 + "'"); @@ -801,7 +801,7 @@ public java.sql.ResultSet getClientInfoProperties() throws SQLException { } checkClosed(); - String sp_fkeys_Query = " exec sp_fkeys @pktable_name=[" + table + "]" + String sp_fkeys_Query = " exec sp_fkeys @pktable_name='" + table + "'" + (null == schema ? ", @pktable_owner=null" : ", @pktable_owner='" + schema + "'") + (null == cat ? ", @pktable_qualifier=null" : ", @pktable_qualifier='" + cat + "'"); @@ -826,7 +826,7 @@ public java.sql.ResultSet getClientInfoProperties() throws SQLException { } checkClosed(); - String sp_fkeys_Query = " exec sp_fkeys @fktable_name=[" + table + "]" + String sp_fkeys_Query = " exec sp_fkeys @fktable_name='" + table + "'" + (null == schema ? ", @fktable_owner=null" : ", @fktable_owner='" + schema + "'") + (null == cat ? ", @fktable_qualifier=null" : ", @fktable_qualifier='" + cat + "'"); From 78808abc60a8dc36d1555afa094cca023414cab3 Mon Sep 17 00:00:00 2001 From: Shawn Sun Date: Tue, 12 Sep 2017 11:41:56 -0700 Subject: [PATCH 13/15] use PreparedStatement to populate temp table #fkeys_results --- .../jdbc/SQLServerDatabaseMetaData.java | 269 ++++++++++-------- .../DatabaseMetaDataForeignKeyTest.java | 11 +- 2 files changed, 165 insertions(+), 115 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java index e5d1126e7..414b9445b 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java @@ -731,6 +731,10 @@ public java.sql.ResultSet getClientInfoProperties() throws SQLException { return rs; } + private final static String[] pkfkColumnNames = {/* 1 */ PKTABLE_CAT, /* 2 */ PKTABLE_SCHEM, /* 3 */ PKTABLE_NAME, /* 4 */ PKCOLUMN_NAME, + /* 5 */ FKTABLE_CAT, /* 6 */ FKTABLE_SCHEM, /* 7 */ FKTABLE_NAME, /* 8 */ FKCOLUMN_NAME, /* 9 */ KEY_SEQ, /* 10 */ UPDATE_RULE, + /* 11 */ DELETE_RULE, /* 12 */ FK_NAME, /* 13 */ PK_NAME, /* 14 */ DEFERRABILITY}; + /* L0 */ public java.sql.ResultSet getCrossReference(String cat1, String schem1, String tab1, @@ -742,14 +746,21 @@ public java.sql.ResultSet getClientInfoProperties() throws SQLException { } checkClosed(); - String sp_fkeys_Query = " exec sp_fkeys @pktable_name='" + tab1 + "'" - + (null == schem1 ? ", @pktable_owner=null" : ", @pktable_owner='" + schem1 + "'") - + (null == cat1 ? ", @pktable_qualifier=null" : ", @pktable_qualifier='" + cat1 + "'") - + ", @fktable_name='" + tab2 + "'" - + (null == schem2 ? ", @fktable_owner=null" : ", @fktable_owner='" + schem2 + "'") - + (null == cat2 ? ", @fktable_qualifier=null" : ", @fktable_qualifier='" + cat2 + "'"); + /* + * sp_fkeys [ @pktable_name = ] 'pktable_name' [ , [ @pktable_owner = ] 'pktable_owner' ] [ , [ @pktable_qualifier = ] 'pktable_qualifier' ] { + * , [ @fktable_name = ] 'fktable_name' } [ , [ @fktable_owner = ] 'fktable_owner' ] [ , [ @fktable_qualifier = ] 'fktable_qualifier' ] + */ + String[] arguments = new String[6]; + arguments[0] = tab1; // pktable_name + arguments[1] = schem1; + arguments[2] = cat1; + arguments[3] = tab2; + arguments[4] = schem2; + arguments[5] = cat2; + + SQLServerResultSet fkeysRS = getResultSetWithProvidedColumnNames(null, CallableHandles.SP_FKEYS, arguments, pkfkColumnNames); - return getResultSetForForeignKeyInformation(sp_fkeys_Query, null); + return getResultSetForForeignKeyInformation(fkeysRS, null); } /* L0 */ public String getDatabaseProductName() throws SQLServerException { @@ -801,11 +812,21 @@ public java.sql.ResultSet getClientInfoProperties() throws SQLException { } checkClosed(); - String sp_fkeys_Query = " exec sp_fkeys @pktable_name='" + table + "'" - + (null == schema ? ", @pktable_owner=null" : ", @pktable_owner='" + schema + "'") - + (null == cat ? ", @pktable_qualifier=null" : ", @pktable_qualifier='" + cat + "'"); + /* + * sp_fkeys [ @pktable_name = ] 'pktable_name' [ , [ @pktable_owner = ] 'pktable_owner' ] [ , [ @pktable_qualifier = ] 'pktable_qualifier' ] { + * , [ @fktable_name = ] 'fktable_name' } [ , [ @fktable_owner = ] 'fktable_owner' ] [ , [ @fktable_qualifier = ] 'fktable_qualifier' ] + */ + String[] arguments = new String[6]; + arguments[0] = table; // pktable_name + arguments[1] = schema; + arguments[2] = cat; + arguments[3] = null; // fktable_name + arguments[4] = null; + arguments[5] = null; + + SQLServerResultSet fkeysRS = getResultSetWithProvidedColumnNames(cat, CallableHandles.SP_FKEYS, arguments, pkfkColumnNames); - return getResultSetForForeignKeyInformation(sp_fkeys_Query, cat); + return getResultSetForForeignKeyInformation(fkeysRS, cat); } /* L0 */ public String getExtraNameCharacters() throws SQLServerException { @@ -826,11 +847,21 @@ public java.sql.ResultSet getClientInfoProperties() throws SQLException { } checkClosed(); - String sp_fkeys_Query = " exec sp_fkeys @fktable_name='" + table + "'" - + (null == schema ? ", @fktable_owner=null" : ", @fktable_owner='" + schema + "'") - + (null == cat ? ", @fktable_qualifier=null" : ", @fktable_qualifier='" + cat + "'"); + /* + * sp_fkeys [ @pktable_name = ] 'pktable_name' [ , [ @pktable_owner = ] 'pktable_owner' ] [ , [ @pktable_qualifier = ] 'pktable_qualifier' ] { + * , [ @fktable_name = ] 'fktable_name' } [ , [ @fktable_owner = ] 'fktable_owner' ] [ , [ @fktable_qualifier = ] 'fktable_qualifier' ] + */ + String[] arguments = new String[6]; + arguments[0] = null; // pktable_name + arguments[1] = null; + arguments[2] = null; + arguments[3] = table; // fktable_name + arguments[4] = schema; + arguments[5] = cat; - return getResultSetForForeignKeyInformation(sp_fkeys_Query, cat); + SQLServerResultSet fkeysRS = getResultSetWithProvidedColumnNames(cat, CallableHandles.SP_FKEYS, arguments, pkfkColumnNames); + + return getResultSetForForeignKeyInformation(fkeysRS, cat); } private String fkeys_results_column_definition = "PKTABLE_QUALIFIER sysname, PKTABLE_OWNER sysname, PKTABLE_NAME sysname, PKCOLUMN_NAME sysname, FKTABLE_QUALIFIER sysname, FKTABLE_OWNER sysname, FKTABLE_NAME sysname, FKCOLUMN_NAME sysname, KEY_SEQ smallint, UPDATE_RULE smallint, DELETE_RULE smallint, FK_NAME sysname, PK_NAME sysname, DEFERRABILITY smallint"; @@ -845,113 +876,129 @@ public java.sql.ResultSet getClientInfoProperties() throws SQLException { * @return * @throws SQLServerException */ - private ResultSet getResultSetForForeignKeyInformation(String sp_fkeys_Query, String cat) throws SQLServerException { + private ResultSet getResultSetForForeignKeyInformation(SQLServerResultSet fkeysRS, String cat) throws SQLServerException { UUID uuid = UUID.randomUUID(); String fkeys_results_tableName = "[#fkeys_results" + uuid + "]"; String foreign_keys_combined_tableName = "[#foreign_keys_combined_results" + uuid + "]"; String sys_foreign_keys = "sys.foreign_keys"; - String orgCat = null; - if (null != cat && cat.trim().length() != 0) { - orgCat = switchCatalogs(cat); - } + // cannot close this statement, otherwise the returned resultset would be closed too. + SQLServerStatement stmt = (SQLServerStatement) connection.createStatement(); + + /** + * create a temp table that has the same definition as the result of sp_fkeys: + * + * create table #fkeys_results ( + * PKTABLE_QUALIFIER sysname, + * PKTABLE_OWNER sysname, + * PKTABLE_NAME sysname, + * PKCOLUMN_NAME sysname, + * FKTABLE_QUALIFIER sysname, + * FKTABLE_OWNER sysname, + * FKTABLE_NAME sysname, + * FKCOLUMN_NAME sysname, + * KEY_SEQ smallint, + * UPDATE_RULE smallint, + * DELETE_RULE smallint, + * FK_NAME sysname, + * PK_NAME sysname, + * DEFERRABILITY smallint + * ); + * + */ + stmt.execute("create table " + fkeys_results_tableName + " (" + fkeys_results_column_definition + ")"); + + /** + * insert the results of sp_fkeys to the temp table #fkeys_results + */ + SQLServerPreparedStatement ps = (SQLServerPreparedStatement) connection + .prepareCall("insert into " + fkeys_results_tableName + "values(?,?,?,?,?,?,?,?,?,?,?,?,?,?)"); try { - // cannot close this statement, otherwise the returned resultset would be closed too. - SQLServerStatement stmt = (SQLServerStatement) connection.createStatement(); - - /** - * create a temp table that has the same definition as the result of sp_fkeys: - * - * create table #fkeys_results ( - * PKTABLE_QUALIFIER sysname, - * PKTABLE_OWNER sysname, - * PKTABLE_NAME sysname, - * PKCOLUMN_NAME sysname, - * FKTABLE_QUALIFIER sysname, - * FKTABLE_OWNER sysname, - * FKTABLE_NAME sysname, - * FKCOLUMN_NAME sysname, - * KEY_SEQ smallint, - * UPDATE_RULE smallint, - * DELETE_RULE smallint, - * FK_NAME sysname, - * PK_NAME sysname, - * DEFERRABILITY smallint - * ); - * - */ - stmt.addBatch("create table " + fkeys_results_tableName + " (" + fkeys_results_column_definition + ")"); - - /** - * insert the results of sp_fkeys to the temp table #fkeys_results - */ - stmt.addBatch("insert into " + fkeys_results_tableName + sp_fkeys_Query); - - /** - * create another temp table that has 3 columns from sys.foreign_keys and the rest of columns are the same as #fkeys_results: - * - * create table #foreign_keys_combined_results ( - * name sysname, - * delete_referential_action_desc nvarchar(60), - * update_referential_action_desc nvarchar(60), - * ...... - * ...... - * ...... - * ); - * - */ - stmt.addBatch("create table " + foreign_keys_combined_tableName + " (" + foreign_keys_combined_column_definition + ")"); - - - /** - * right join the content of sys.foreign_keys and the content of #fkeys_results base on foreign key name and save the result to the new temp - * table #foreign_keys_combined_results - */ - stmt.addBatch("insert into " + foreign_keys_combined_tableName - + " select " + sys_foreign_keys + ".name, " + sys_foreign_keys + ".delete_referential_action_desc, " + sys_foreign_keys + ".update_referential_action_desc," - + fkeys_results_tableName + ".PKTABLE_QUALIFIER," + fkeys_results_tableName + ".PKTABLE_OWNER," + fkeys_results_tableName + ".PKTABLE_NAME," + fkeys_results_tableName + ".PKCOLUMN_NAME," - + fkeys_results_tableName + ".FKTABLE_QUALIFIER," + fkeys_results_tableName + ".FKTABLE_OWNER," + fkeys_results_tableName + ".FKTABLE_NAME," + fkeys_results_tableName + ".FKCOLUMN_NAME," - + fkeys_results_tableName + ".KEY_SEQ," + fkeys_results_tableName + ".UPDATE_RULE," + fkeys_results_tableName + ".DELETE_RULE," + fkeys_results_tableName + ".FK_NAME," + fkeys_results_tableName + ".PK_NAME," - + fkeys_results_tableName + ".DEFERRABILITY from " + sys_foreign_keys - + " right join " + fkeys_results_tableName + " on " + sys_foreign_keys + ".name=" + fkeys_results_tableName + ".FK_NAME"); - - /** - * the DELETE_RULE value and UPDATE_RULE value returned from sp_fkeys are not the same as required by JDBC spec. therefore, we need to update - * those values to JDBC required values base on delete_referential_action_desc and update_referential_action_desc returned from sys.foreign_keys - * No Action: 3 - * Cascade: 0 - * Set Null: 2 - * Set Default: 4 - */ - stmt.addBatch("update " + foreign_keys_combined_tableName + " set DELETE_RULE=3 where delete_referential_action_desc='NO_ACTION';" - + "update " + foreign_keys_combined_tableName + " set DELETE_RULE=0 where delete_referential_action_desc='Cascade';" - + "update " + foreign_keys_combined_tableName + " set DELETE_RULE=2 where delete_referential_action_desc='SET_NULL';" - + "update " + foreign_keys_combined_tableName + " set DELETE_RULE=4 where delete_referential_action_desc='SET_DEFAULT';" - + "update " + foreign_keys_combined_tableName + " set UPDATE_RULE=3 where update_referential_action_desc='NO_ACTION';" - + "update " + foreign_keys_combined_tableName + " set UPDATE_RULE=0 where update_referential_action_desc='Cascade';" - + "update " + foreign_keys_combined_tableName + " set UPDATE_RULE=2 where update_referential_action_desc='SET_NULL';" - + "update " + foreign_keys_combined_tableName + " set UPDATE_RULE=4 where update_referential_action_desc='SET_DEFAULT';"); - - try { - stmt.executeBatch(); - } - catch (BatchUpdateException e) { - throw new SQLServerException(e.getMessage(), e.getSQLState(), e.getErrorCode(), null); + while (fkeysRS.next()) { + ps.setString(1, fkeysRS.getString(1)); + ps.setString(2, fkeysRS.getString(2)); + ps.setString(3, fkeysRS.getString(3)); + ps.setString(4, fkeysRS.getString(4)); + ps.setString(5, fkeysRS.getString(5)); + ps.setString(6, fkeysRS.getString(6)); + ps.setString(7, fkeysRS.getString(7)); + ps.setString(8, fkeysRS.getString(8)); + ps.setString(9, fkeysRS.getString(9)); + ps.setString(10, fkeysRS.getString(10)); + ps.setString(11, fkeysRS.getString(11)); + ps.setString(12, fkeysRS.getString(12)); + ps.setString(13, fkeysRS.getString(13)); + ps.setString(14, fkeysRS.getString(14)); + ps.execute(); } - - /** - * now, the #foreign_keys_combined_results table has the correct values for DELETE_RULE and UPDATE_RULE. Then we can return the result of - * the table with the same definition of the resultset return by sp_fkeys (same column definition and same order). - */ - return stmt.executeQuery( - "select PKTABLE_QUALIFIER as 'PKTABLE_CAT',PKTABLE_OWNER as 'PKTABLE_SCHEM',PKTABLE_NAME,PKCOLUMN_NAME,FKTABLE_QUALIFIER as 'FKTABLE_CAT',FKTABLE_OWNER as 'FKTABLE_SCHEM',FKTABLE_NAME,FKCOLUMN_NAME,KEY_SEQ,UPDATE_RULE,DELETE_RULE,FK_NAME,PK_NAME,DEFERRABILITY from " - + foreign_keys_combined_tableName + " order by FKTABLE_QUALIFIER, FKTABLE_OWNER, FKTABLE_NAME, KEY_SEQ"); } finally { - if (null != orgCat) { - connection.setCatalog(orgCat); + if (null != ps) { + ps.close(); } + if (null != fkeysRS) { + fkeysRS.close(); + } + } + + /** + * create another temp table that has 3 columns from sys.foreign_keys and the rest of columns are the same as #fkeys_results: + * + * create table #foreign_keys_combined_results ( + * name sysname, + * delete_referential_action_desc nvarchar(60), + * update_referential_action_desc nvarchar(60), + * ...... + * ...... + * ...... + * ); + * + */ + stmt.addBatch("create table " + foreign_keys_combined_tableName + " (" + foreign_keys_combined_column_definition + ")"); + + /** + * right join the content of sys.foreign_keys and the content of #fkeys_results base on foreign key name and save the result to the new temp + * table #foreign_keys_combined_results + */ + stmt.addBatch("insert into " + foreign_keys_combined_tableName + + " select " + sys_foreign_keys + ".name, " + sys_foreign_keys + ".delete_referential_action_desc, " + sys_foreign_keys + ".update_referential_action_desc," + + fkeys_results_tableName + ".PKTABLE_QUALIFIER," + fkeys_results_tableName + ".PKTABLE_OWNER," + fkeys_results_tableName + ".PKTABLE_NAME," + fkeys_results_tableName + ".PKCOLUMN_NAME," + + fkeys_results_tableName + ".FKTABLE_QUALIFIER," + fkeys_results_tableName + ".FKTABLE_OWNER," + fkeys_results_tableName + ".FKTABLE_NAME," + fkeys_results_tableName + ".FKCOLUMN_NAME," + + fkeys_results_tableName + ".KEY_SEQ," + fkeys_results_tableName + ".UPDATE_RULE," + fkeys_results_tableName + ".DELETE_RULE," + fkeys_results_tableName + ".FK_NAME," + fkeys_results_tableName + ".PK_NAME," + + fkeys_results_tableName + ".DEFERRABILITY from " + sys_foreign_keys + + " right join " + fkeys_results_tableName + " on " + sys_foreign_keys + ".name=" + fkeys_results_tableName + ".FK_NAME"); + + /** + * the DELETE_RULE value and UPDATE_RULE value returned from sp_fkeys are not the same as required by JDBC spec. therefore, we need to update + * those values to JDBC required values base on delete_referential_action_desc and update_referential_action_desc returned from sys.foreign_keys + * No Action: 3 + * Cascade: 0 + * Set Null: 2 + * Set Default: 4 + */ + stmt.addBatch("update " + foreign_keys_combined_tableName + " set DELETE_RULE=3 where delete_referential_action_desc='NO_ACTION';" + + "update " + foreign_keys_combined_tableName + " set DELETE_RULE=0 where delete_referential_action_desc='Cascade';" + + "update " + foreign_keys_combined_tableName + " set DELETE_RULE=2 where delete_referential_action_desc='SET_NULL';" + + "update " + foreign_keys_combined_tableName + " set DELETE_RULE=4 where delete_referential_action_desc='SET_DEFAULT';" + + "update " + foreign_keys_combined_tableName + " set UPDATE_RULE=3 where update_referential_action_desc='NO_ACTION';" + + "update " + foreign_keys_combined_tableName + " set UPDATE_RULE=0 where update_referential_action_desc='Cascade';" + + "update " + foreign_keys_combined_tableName + " set UPDATE_RULE=2 where update_referential_action_desc='SET_NULL';" + + "update " + foreign_keys_combined_tableName + " set UPDATE_RULE=4 where update_referential_action_desc='SET_DEFAULT';"); + + try { + stmt.executeBatch(); + } + catch (BatchUpdateException e) { + throw new SQLServerException(e.getMessage(), e.getSQLState(), e.getErrorCode(), null); } + + /** + * now, the #foreign_keys_combined_results table has the correct values for DELETE_RULE and UPDATE_RULE. Then we can return the result of + * the table with the same definition of the resultset return by sp_fkeys (same column definition and same order). + */ + return stmt.executeQuery( + "select PKTABLE_QUALIFIER as 'PKTABLE_CAT',PKTABLE_OWNER as 'PKTABLE_SCHEM',PKTABLE_NAME,PKCOLUMN_NAME,FKTABLE_QUALIFIER as 'FKTABLE_CAT',FKTABLE_OWNER as 'FKTABLE_SCHEM',FKTABLE_NAME,FKCOLUMN_NAME,KEY_SEQ,UPDATE_RULE,DELETE_RULE,FK_NAME,PK_NAME,DEFERRABILITY from " + + foreign_keys_combined_tableName + " order by FKTABLE_QUALIFIER, FKTABLE_OWNER, FKTABLE_NAME, KEY_SEQ"); } private final static String[] getIndexInfoColumnNames = {/* 1 */ TABLE_CAT, /* 2 */ TABLE_SCHEM, /* 3 */ TABLE_NAME, /* 4 */ NON_UNIQUE, diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataForeignKeyTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataForeignKeyTest.java index 9ce309fec..b498a6afb 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataForeignKeyTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataForeignKeyTest.java @@ -8,6 +8,7 @@ package com.microsoft.sqlserver.jdbc.databasemetadata; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.sql.DriverManager; @@ -44,8 +45,10 @@ public class DatabaseMetaDataForeignKeyTest extends AbstractTest { private static String schema = null; private static String catalog = null; - private static final String EXPECTED_ERROR_MESSAGE = "The database name component of the object qualifier must be the name of the current database."; + private static final String EXPECTED_ERROR_MESSAGE = "An object or column name is missing or empty."; + private static final String EXPECTED_ERROR_MESSAGE2 = "The database name component of the object qualifier must be the name of the current database."; + @BeforeAll private static void setupVariation() throws SQLException { conn = (SQLServerConnection) DriverManager.getConnection(connectionString); @@ -112,7 +115,7 @@ public void testGetImportedKeys() throws SQLServerException { fail("Exception is not thrown."); } catch (SQLServerException e) { - assertEquals(EXPECTED_ERROR_MESSAGE, e.getMessage()); + assertTrue(e.getMessage().startsWith(EXPECTED_ERROR_MESSAGE)); } } @@ -185,7 +188,7 @@ public void testGetExportedKeys() throws SQLServerException { fail("Exception is not thrown."); } catch (SQLServerException e) { - assertEquals(EXPECTED_ERROR_MESSAGE, e.getMessage()); + assertTrue(e.getMessage().startsWith(EXPECTED_ERROR_MESSAGE)); } } } @@ -231,7 +234,7 @@ public void testGetCrossReference() throws SQLServerException { fail("Exception is not thrown."); } catch (SQLServerException e) { - assertEquals(EXPECTED_ERROR_MESSAGE, e.getMessage()); + assertEquals(EXPECTED_ERROR_MESSAGE2, e.getMessage()); } } } From 7a143b9b4a5d89d8540e8aede523f59aa667915c Mon Sep 17 00:00:00 2001 From: Shawn Sun Date: Tue, 12 Sep 2017 11:54:35 -0700 Subject: [PATCH 14/15] change String to Int for some columns --- .../sqlserver/jdbc/SQLServerDatabaseMetaData.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java index 414b9445b..66ac57794 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java @@ -923,12 +923,12 @@ private ResultSet getResultSetForForeignKeyInformation(SQLServerResultSet fkeysR ps.setString(6, fkeysRS.getString(6)); ps.setString(7, fkeysRS.getString(7)); ps.setString(8, fkeysRS.getString(8)); - ps.setString(9, fkeysRS.getString(9)); - ps.setString(10, fkeysRS.getString(10)); - ps.setString(11, fkeysRS.getString(11)); + ps.setInt(9, fkeysRS.getInt(9)); + ps.setInt(10, fkeysRS.getInt(10)); + ps.setInt(11, fkeysRS.getInt(11)); ps.setString(12, fkeysRS.getString(12)); ps.setString(13, fkeysRS.getString(13)); - ps.setString(14, fkeysRS.getString(14)); + ps.setInt(14, fkeysRS.getInt(14)); ps.execute(); } } From d1eb934bbf5b0c2c0de233e605ebd647c9c35f91 Mon Sep 17 00:00:00 2001 From: Shawn Sun Date: Tue, 12 Sep 2017 13:39:45 -0700 Subject: [PATCH 15/15] refactor a bit --- .../sqlserver/jdbc/SQLServerDatabaseMetaData.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java index 66ac57794..516a4d590 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java @@ -864,10 +864,6 @@ public java.sql.ResultSet getClientInfoProperties() throws SQLException { return getResultSetForForeignKeyInformation(fkeysRS, cat); } - private String fkeys_results_column_definition = "PKTABLE_QUALIFIER sysname, PKTABLE_OWNER sysname, PKTABLE_NAME sysname, PKCOLUMN_NAME sysname, FKTABLE_QUALIFIER sysname, FKTABLE_OWNER sysname, FKTABLE_NAME sysname, FKCOLUMN_NAME sysname, KEY_SEQ smallint, UPDATE_RULE smallint, DELETE_RULE smallint, FK_NAME sysname, PK_NAME sysname, DEFERRABILITY smallint"; - private String foreign_keys_combined_column_definition = "name sysname, delete_referential_action_desc nvarchar(60), update_referential_action_desc nvarchar(60)," - + fkeys_results_column_definition; - /** * The original sp_fkeys stored procedure does not give the required values from JDBC specification. This method creates 2 temporary tables and * uses join and other operations on them to give the correct values. @@ -882,6 +878,10 @@ private ResultSet getResultSetForForeignKeyInformation(SQLServerResultSet fkeysR String foreign_keys_combined_tableName = "[#foreign_keys_combined_results" + uuid + "]"; String sys_foreign_keys = "sys.foreign_keys"; + String fkeys_results_column_definition = "PKTABLE_QUALIFIER sysname, PKTABLE_OWNER sysname, PKTABLE_NAME sysname, PKCOLUMN_NAME sysname, FKTABLE_QUALIFIER sysname, FKTABLE_OWNER sysname, FKTABLE_NAME sysname, FKCOLUMN_NAME sysname, KEY_SEQ smallint, UPDATE_RULE smallint, DELETE_RULE smallint, FK_NAME sysname, PK_NAME sysname, DEFERRABILITY smallint"; + String foreign_keys_combined_column_definition = "name sysname, delete_referential_action_desc nvarchar(60), update_referential_action_desc nvarchar(60)," + + fkeys_results_column_definition; + // cannot close this statement, otherwise the returned resultset would be closed too. SQLServerStatement stmt = (SQLServerStatement) connection.createStatement();