From 5bbc9706357fab5ee70c467fb4e2ea05c825092c Mon Sep 17 00:00:00 2001 From: v-xiangs Date: Wed, 26 Apr 2017 15:05:41 -0700 Subject: [PATCH 1/4] throw exception if no metadata is retrieved for stored procedure --- .../sqlserver/jdbc/SQLServerParameterMetaData.java | 11 +++++++++++ .../microsoft/sqlserver/jdbc/SQLServerResource.java | 1 + 2 files changed, 12 insertions(+) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParameterMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParameterMetaData.java index 79d4afa57..6a6dc2474 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParameterMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParameterMetaData.java @@ -581,6 +581,17 @@ private void checkClosed() throws SQLServerException { rsProcedureMeta = s.executeQueryInternal("exec sp_sproc_columns_100 " + sProc + " @ODBCVer=3"); else rsProcedureMeta = s.executeQueryInternal("exec sp_sproc_columns " + sProc + " @ODBCVer=3"); + + // if rsProcedureMeta has no next row, it means the stored procedure is not found + if (!rsProcedureMeta.next()) { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_StoredProcedureNotFound")); + Object[] msgArgs = {st.procedureName}; + SQLServerException.makeFromDriverError(con, rsProcedureMeta, form.format(msgArgs), null, false); + } + else { + rsProcedureMeta.beforeFirst(); + } + // Sixth is DATA_TYPE rsProcedureMeta.getColumn(6).setFilter(new DataTypeFilter()); if (con.isKatmaiOrLater()) { diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java index 980c30bbf..67766de09 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java @@ -381,5 +381,6 @@ protected Object[][] getContents() { {"R_serverPreparedStatementDiscardThreshold", "The serverPreparedStatementDiscardThreshold {0} is not valid."}, {"R_kerberosLoginFailedForUsername", "Cannot login with Kerberos principal {0}, check your credentials. {1}"}, {"R_kerberosLoginFailed", "Kerberos Login failed: {0} due to {1} ({2})"}, + {"R_StoredProcedureNotFound", "Could not find stored procedure ''{0}''."}, }; } From 3e3531cccdbe3f387cb80c6e329e2508e1601e35 Mon Sep 17 00:00:00 2001 From: v-xiangs Date: Wed, 26 Apr 2017 15:32:08 -0700 Subject: [PATCH 2/4] added tests --- .../ParameterMetaDataTest.java | 38 ++++++++++++++++--- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/parametermetadata/ParameterMetaDataTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/parametermetadata/ParameterMetaDataTest.java index c4e8bd0d9..371e1185c 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/parametermetadata/ParameterMetaDataTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/parametermetadata/ParameterMetaDataTest.java @@ -16,11 +16,13 @@ import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Statement; +import java.util.UUID; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; +import com.microsoft.sqlserver.jdbc.SQLServerCallableStatement; import com.microsoft.sqlserver.jdbc.SQLServerException; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.Utils; @@ -29,7 +31,7 @@ @RunWith(JUnitPlatform.class) public class ParameterMetaDataTest extends AbstractTest { private static final String tableName = "[" + RandomUtil.getIdentifier("StatementParam") + "]"; - + /** * Test ParameterMetaData#isWrapperFor and ParameterMetaData#unwrap. * @@ -37,19 +39,19 @@ public class ParameterMetaDataTest extends AbstractTest { */ @Test public void testParameterMetaDataWrapper() throws SQLException { - try (Connection con = DriverManager.getConnection(connectionString); - Statement stmt = con.createStatement()) { + try (Connection con = DriverManager.getConnection(connectionString); Statement stmt = con.createStatement()) { stmt.executeUpdate("create table " + tableName + " (col1 int identity(1,1) primary key)"); try { String query = "SELECT * from " + tableName + " where col1 = ?"; - + try (PreparedStatement pstmt = con.prepareStatement(query)) { ParameterMetaData parameterMetaData = pstmt.getParameterMetaData(); assertTrue(parameterMetaData.isWrapperFor(ParameterMetaData.class)); assertSame(parameterMetaData, parameterMetaData.unwrap(ParameterMetaData.class)); } - } finally { + } + finally { Utils.dropTableIfExists(tableName, stmt); } @@ -73,4 +75,30 @@ public void testSQLServerExceptionNotWrapped() throws SQLException { "SQLServerException should not be wrapped by another SQLServerException."); } } + + /** + * Test exception when invalid stored procedure name is used. + * + * @throws Exception + */ + @Test + public void testExceptionWithInvalidStoredProcedureName() throws Exception { + String randomProcedureName = UUID.randomUUID().toString(); + final String sql = "{call [" + randomProcedureName + "] (?)}"; + + try (Connection con = DriverManager.getConnection(connectionString); + SQLServerCallableStatement Cstmt = (SQLServerCallableStatement) con.prepareCall(sql);) { + Cstmt.getParameterMetaData(); + + throw new Exception("Expected Exception for invalied stored procedure name is not thrown."); + } + catch (Exception e) { + if (e instanceof SQLServerException) { + assertTrue(e.getMessage().contains("Could not find stored procedure")); + } + else { + throw e; + } + } + } } From 6a8993667ef4830e6e57929f75ec4ed9c8a56c0c Mon Sep 17 00:00:00 2001 From: v-xiangs Date: Wed, 26 Apr 2017 16:27:05 -0700 Subject: [PATCH 3/4] make it works for TVP only --- .../jdbc/SQLServerParameterMetaData.java | 13 +++++---- .../jdbc/SQLServerPreparedStatement.java | 7 +++++ .../ParameterMetaDataTest.java | 28 ------------------- 3 files changed, 14 insertions(+), 34 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParameterMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParameterMetaData.java index 6a6dc2474..c0e26d646 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParameterMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerParameterMetaData.java @@ -41,6 +41,8 @@ public final class SQLServerParameterMetaData implements ParameterMetaData { /* Used for callable statement meta data */ private Statement stmtCall; private SQLServerResultSet rsProcedureMeta; + + protected boolean procedureIsFound = false; static final private java.util.logging.Logger logger = java.util.logging.Logger .getLogger("com.microsoft.sqlserver.jdbc.internals.SQLServerParameterMetaData"); @@ -582,15 +584,14 @@ private void checkClosed() throws SQLServerException { else rsProcedureMeta = s.executeQueryInternal("exec sp_sproc_columns " + sProc + " @ODBCVer=3"); - // if rsProcedureMeta has no next row, it means the stored procedure is not found - if (!rsProcedureMeta.next()) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_StoredProcedureNotFound")); - Object[] msgArgs = {st.procedureName}; - SQLServerException.makeFromDriverError(con, rsProcedureMeta, form.format(msgArgs), null, false); + // if rsProcedureMeta has next row, it means the stored procedure is found + if (rsProcedureMeta.next()) { + procedureIsFound = true; } else { - rsProcedureMeta.beforeFirst(); + procedureIsFound = false; } + rsProcedureMeta.beforeFirst(); // Sixth is DATA_TYPE rsProcedureMeta.getColumn(6).setFilter(new DataTypeFilter()); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index b1dff7c4d..ba7f093ac 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -2199,6 +2199,13 @@ String getTVPNameIfNull(int n, if(null != this.procedureName) { SQLServerParameterMetaData pmd = (SQLServerParameterMetaData) this.getParameterMetaData(); pmd.isTVP = true; + + if (!pmd.procedureIsFound) { + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_StoredProcedureNotFound")); + Object[] msgArgs = {this.procedureName}; + SQLServerException.makeFromDriverError(connection, pmd, form.format(msgArgs), null, false); + } + try { String tvpNameWithoutSchema = pmd.getParameterTypeName(n); String tvpSchema = pmd.getTVPSchemaFromStoredProcedure(n); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/parametermetadata/ParameterMetaDataTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/parametermetadata/ParameterMetaDataTest.java index 371e1185c..bfc421571 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/parametermetadata/ParameterMetaDataTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/parametermetadata/ParameterMetaDataTest.java @@ -16,13 +16,11 @@ import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Statement; -import java.util.UUID; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; -import com.microsoft.sqlserver.jdbc.SQLServerCallableStatement; import com.microsoft.sqlserver.jdbc.SQLServerException; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.Utils; @@ -75,30 +73,4 @@ public void testSQLServerExceptionNotWrapped() throws SQLException { "SQLServerException should not be wrapped by another SQLServerException."); } } - - /** - * Test exception when invalid stored procedure name is used. - * - * @throws Exception - */ - @Test - public void testExceptionWithInvalidStoredProcedureName() throws Exception { - String randomProcedureName = UUID.randomUUID().toString(); - final String sql = "{call [" + randomProcedureName + "] (?)}"; - - try (Connection con = DriverManager.getConnection(connectionString); - SQLServerCallableStatement Cstmt = (SQLServerCallableStatement) con.prepareCall(sql);) { - Cstmt.getParameterMetaData(); - - throw new Exception("Expected Exception for invalied stored procedure name is not thrown."); - } - catch (Exception e) { - if (e instanceof SQLServerException) { - assertTrue(e.getMessage().contains("Could not find stored procedure")); - } - else { - throw e; - } - } - } } From 0ff590042bdf95559427d26be7bd7f9338f09608 Mon Sep 17 00:00:00 2001 From: v-xiangs Date: Wed, 26 Apr 2017 16:44:34 -0700 Subject: [PATCH 4/4] added test in TVP test suite --- .../sqlserver/jdbc/tvp/TVPIssuesTest.java | 54 +++++++++++++++++-- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/tvp/TVPIssuesTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/tvp/TVPIssuesTest.java index 6da95f6c3..78294fc80 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/tvp/TVPIssuesTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/tvp/TVPIssuesTest.java @@ -8,6 +8,7 @@ package com.microsoft.sqlserver.jdbc.tvp; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.sql.Connection; @@ -22,6 +23,8 @@ import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; +import com.microsoft.sqlserver.jdbc.SQLServerCallableStatement; +import com.microsoft.sqlserver.jdbc.SQLServerException; import com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement; import com.microsoft.sqlserver.jdbc.SQLServerStatement; import com.microsoft.sqlserver.testframework.AbstractTest; @@ -32,9 +35,10 @@ public class TVPIssuesTest extends AbstractTest { static Connection connection = null; static Statement stmt = null; - private static String tvpName = "tryTVP_RS_varcharMax_4001_Issue"; - private static String srcTable = "tryTVP_RS_varcharMax_4001_Issue_src"; - private static String desTable = "tryTVP_RS_varcharMax_4001_Issue_dest"; + private static String tvpName = "TVPIssuesTest_TVP"; + private static String procedureName = "TVPIssuesTest_SP"; + private static String srcTable = "TVPIssuesTest_src"; + private static String desTable = "TVPIssuesTest_dest"; @Test public void tryTVP_RS_varcharMax_4001_Issue() throws Exception { @@ -52,6 +56,34 @@ public void tryTVP_RS_varcharMax_4001_Issue() throws Exception { testDestinationTable(); } + /** + * Test exception when invalid stored procedure name is used. + * + * @throws Exception + */ + @Test + public void testExceptionWithInvalidStoredProcedureName() throws Exception { + SQLServerStatement st = (SQLServerStatement) connection.createStatement(); + ResultSet rs = st.executeQuery("select * from " + srcTable); + + dropProcedure(); + + final String sql = "{call " + procedureName + "(?)}"; + SQLServerCallableStatement Cstmt = (SQLServerCallableStatement) connection.prepareCall(sql); + try { + Cstmt.setObject(1, rs); + throw new Exception("Expected Exception for invalied stored procedure name is not thrown."); + } + catch (Exception e) { + if (e instanceof SQLServerException) { + assertTrue(e.getMessage().contains("Could not find stored procedure"), "Invalid Error Message."); + } + else { + throw e; + } + } + } + private void testDestinationTable() throws SQLException, IOException { ResultSet rs = connection.createStatement().executeQuery("select * from " + desTable); while (rs.next()) { @@ -83,6 +115,8 @@ public static void beforeAll() throws SQLException { connection = DriverManager.getConnection(connectionString); stmt = connection.createStatement(); + dropProcedure(); + stmt.executeUpdate("IF EXISTS (SELECT * FROM sys.types WHERE is_table_type = 1 AND name = '" + tvpName + "') " + " drop type " + tvpName); Utils.dropTableIfExists(srcTable, stmt); Utils.dropTableIfExists(desTable, stmt); @@ -96,11 +130,25 @@ public static void beforeAll() throws SQLException { String TVPCreateCmd = "CREATE TYPE " + tvpName + " as table (c1 varchar(max) null)"; stmt.executeUpdate(TVPCreateCmd); + createPreocedure(); + populateSourceTable(); } + private static void dropProcedure() throws SQLException { + Utils.dropProcedureIfExists(procedureName, stmt); + } + + private static void createPreocedure() throws SQLException { + String sql = "CREATE PROCEDURE " + procedureName + " @InputData " + tvpName + " READONLY " + " AS " + " BEGIN " + " INSERT INTO " + desTable + + " SELECT * FROM @InputData" + " END"; + + stmt.execute(sql); + } + @AfterAll public static void terminateVariation() throws SQLException { + dropProcedure(); stmt.executeUpdate("IF EXISTS (SELECT * FROM sys.types WHERE is_table_type = 1 AND name = '" + tvpName + "') " + " drop type " + tvpName); Utils.dropTableIfExists(srcTable, stmt); Utils.dropTableIfExists(desTable, stmt);