From 93070514af439954021f90d69cd44b20cf4625e0 Mon Sep 17 00:00:00 2001 From: Terry Chow <32403408+tkyc@users.noreply.github.com> Date: Wed, 19 Jun 2024 16:18:26 -0700 Subject: [PATCH] Fixed CallableStatement default value regression (#2452) * Fixed default value regression for cstmts * Adjusted regex * Test update * Updated test p2 * Updated regex --- .../jdbc/SQLServerPreparedStatement.java | 2 +- .../sqlserver/jdbc/TestResource.java | 1 + .../CallableStatementTest.java | 45 +++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index a25aa2cbd..b92798f6d 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -139,7 +139,7 @@ private void setPreparedStatementHandle(int handle) { * Regex for JDBC 'call' escape syntax */ private static final Pattern callEscapePattern = Pattern - .compile("^\\s*(?i)\\{(\\s*\\??\\s*=?\\s*)call (.+)\\s*\\(?\\?*,?\\)?\\s*}\\s*$"); + .compile("^\\s*(?i)\\{(\\s*\\??\\s*=?\\s*)call [^\\(\\)]+\\s*(\\(\\s*\\?\\s*(,\\s*\\?\\s*)*\\))?\\s*}"); /** * Regex for 'exec' escape syntax diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java b/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java index b98171c23..ae34ef0c0 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java @@ -42,6 +42,7 @@ protected Object[][] getContents() { {"R_givenValueType", "The given value of type"}, {"R_lengthTruncated", " The inserted length is truncated or not correct!"}, {"R_timeValueTruncated", " The time value is truncated or not correct!"}, + {"R_nullPointerExceptionFromResultSet", "Cannot invoke \"java.sql.ResultSet.next()\" because \"rs\" is null"}, {"R_invalidErrorMessage", "Invalid Error Message: "}, {"R_kerberosNativeGSSFailure", "No valid credentials provided (Mechanism level: Failed to find any Kerberos tgt)"}, diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableStatementTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableStatementTest.java index e4bebf9ac..2b37534b8 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableStatementTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableStatementTest.java @@ -55,6 +55,8 @@ public class CallableStatementTest extends AbstractTest { .escapeIdentifier(RandomUtil.getIdentifier("CallableStatementTest_setNull_SP")); private static String inputParamsProcedureName = AbstractSQLGenerator .escapeIdentifier(RandomUtil.getIdentifier("CallableStatementTest_inputParams_SP")); + private static String conditionalSproc = AbstractSQLGenerator + .escapeIdentifier(RandomUtil.getIdentifier("CallableStatementTest_conditionalSproc")); private static String getObjectLocalDateTimeProcedureName = AbstractSQLGenerator .escapeIdentifier(RandomUtil.getIdentifier("CallableStatementTest_getObjectLocalDateTime_SP")); private static String getObjectOffsetDateTimeProcedureName = AbstractSQLGenerator @@ -97,6 +99,7 @@ public static void setupTest() throws Exception { TestUtils.dropProcedureIfExists(zeroParamSproc, stmt); TestUtils.dropProcedureIfExists(outOfOrderSproc, stmt); TestUtils.dropProcedureIfExists(byParamNameSproc, stmt); + TestUtils.dropProcedureIfExists(conditionalSproc, stmt); TestUtils.dropFunctionIfExists(userDefinedFunction, stmt); TestUtils.dropUserDefinedTypeIfExists(manyParamUserDefinedType, stmt); TestUtils.dropProcedureIfExists(manyParamProc, stmt); @@ -115,6 +118,7 @@ public static void setupTest() throws Exception { createProcedureZeroParams(); createOutOfOrderSproc(); createByParamNameSproc(); + createConditionalProcedure(); createUserDefinedFunction(); } } @@ -1150,6 +1154,38 @@ public void testExecDocumentedSystemStoredProceduresIndexedParameters() throws S } } + @Test + public void testCallableStatementDefaultValues() throws SQLException { + String call0 = "{call " + conditionalSproc + " (?, ?, 1)}"; + String call1 = "{call " + conditionalSproc + " (?, ?, 2)}"; + int expectedValue = 5; // The sproc should return this value + + try (CallableStatement cstmt = connection.prepareCall(call0)) { + cstmt.setInt(1, 1); + cstmt.setInt(2, 2); + cstmt.execute(); + ResultSet rs = cstmt.getResultSet(); + rs.next(); + fail(TestResource.getResource("R_expectedFailPassed")); + + } catch (Exception e) { + String msg = e.getMessage(); + assertTrue(TestResource + .getResource("R_nullPointerExceptionFromResultSet").equalsIgnoreCase(msg) + || msg == null); + } + + try (CallableStatement cstmt = connection.prepareCall(call1)) { + cstmt.setInt(1, 1); + cstmt.setInt(2, 2); + cstmt.execute(); + ResultSet rs = cstmt.getResultSet(); + rs.next(); + + assertEquals(Integer.toString(expectedValue), rs.getString(1)); + } + } + @Test @Tag(Constants.reqExternalSetup) @Tag(Constants.xAzureSQLDB) @@ -1257,6 +1293,7 @@ public static void cleanup() throws SQLException { TestUtils.dropProcedureIfExists(outOfOrderSproc, stmt); TestUtils.dropProcedureIfExists(byParamNameSproc, stmt); TestUtils.dropProcedureIfExists(currentTimeProc, stmt); + TestUtils.dropProcedureIfExists(conditionalSproc, stmt); TestUtils.dropFunctionIfExists(userDefinedFunction, stmt); } } @@ -1317,6 +1354,14 @@ private static void createProcedureCurrentTime() throws SQLException { } } + private static void createConditionalProcedure() throws SQLException { + String sql = "CREATE PROCEDURE " + conditionalSproc + " @param0 INT, @param1 INT, @maybe bigint = 2 " + + "AS BEGIN IF @maybe >= 2 BEGIN SELECT 5 END END"; + try (Statement stmt = connection.createStatement()) { + stmt.execute(sql); + } + } + private static void createTableManyParams() throws SQLException { String type = manyParamUserDefinedType; String sql = "CREATE TABLE" + manyParamsTable + " (c1 " + type + " null, " + "c2 " + type + " null, " + "c3 "