Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix methods that use sp_fkeys #490

Merged
merged 15 commits into from
Sep 15, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -17,6 +18,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;

Expand Down Expand Up @@ -743,6 +745,7 @@ 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' ]
Expand All @@ -755,7 +758,9 @@ public java.sql.ResultSet getClientInfoProperties() throws SQLException {
arguments[4] = schem2;
arguments[5] = cat2;

return getResultSetWithProvidedColumnNames(null, CallableHandles.SP_FKEYS, arguments, pkfkColumnNames);
SQLServerResultSet fkeysRS = getResultSetWithProvidedColumnNames(null, CallableHandles.SP_FKEYS, arguments, pkfkColumnNames);

return getResultSetForForeignKeyInformation(fkeysRS, null);
}

/* L0 */ public String getDatabaseProductName() throws SQLServerException {
Expand Down Expand Up @@ -806,6 +811,7 @@ 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' ]
Expand All @@ -817,7 +823,10 @@ 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);

SQLServerResultSet fkeysRS = getResultSetWithProvidedColumnNames(cat, CallableHandles.SP_FKEYS, arguments, pkfkColumnNames);

return getResultSetForForeignKeyInformation(fkeysRS, cat);
}

/* L0 */ public String getExtraNameCharacters() throws SQLServerException {
Expand All @@ -837,6 +846,7 @@ 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' ]
Expand All @@ -848,7 +858,147 @@ 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);

SQLServerResultSet fkeysRS = getResultSetWithProvidedColumnNames(cat, CallableHandles.SP_FKEYS, arguments, pkfkColumnNames);

return getResultSetForForeignKeyInformation(fkeysRS, cat);
}

/**
* 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 sp_fkeys_Query
* @return
* @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 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();

/**
* 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 {
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.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.setInt(14, fkeysRS.getInt(14));
ps.execute();
}
}
finally {
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,
Expand Down
Loading