From 0e97689f50f30c70dcfec89bcc89537ea622b8b5 Mon Sep 17 00:00:00 2001 From: Terry Chow <32403408+tkyc@users.noreply.github.com> Date: Tue, 23 Jul 2024 12:00:44 -0700 Subject: [PATCH] Fixed regression with specifying arg names in call syntax (#2480) * Fixed regression with specifying arg names in call syntax * Corrected inefficient regex * Corrected inefficient regex * Corrected inefficient regex p2 * Corrected inefficient regex p3 * Corrected inefficient regex p4 * Updated changelog * Added comment --- CHANGELOG.md | 3 +- .../jdbc/SQLServerPreparedStatement.java | 5 +++- .../CallableStatementTest.java | 28 +++++++++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e676970b6..3c007584a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,8 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) ## [12.8.0] Stable Release -- No changes since previous release +### Fixed issues +- Fixed regression with specifying argument names in callable statement syntax [#2480](https://github.com/microsoft/mssql-jdbc/pull/2480) ## [12.7.1] Preview Release ### Added diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index 050f77cfd..651505aa2 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -137,9 +137,12 @@ private void setPreparedStatementHandle(int handle) { /** * Regex for JDBC 'call' escape syntax + * + * Matches {[? =] call sproc ([@arg =] ?, [@arg =] ?, [@arg =] ? ...)} */ private static final Pattern callEscapePattern = Pattern - .compile("^\\s*(?i)\\{(\\s*\\??\\s*=?\\s*)call [^\\(\\)]+\\s*((\\(\\s*\\?\\s*(,\\s*\\?\\s*)*\\))?|\\(\\))\\s*}"); + .compile("^\\s*(?i)\\{(\\s*\\??\\s*=?\\s*)call [^\\(\\)]+\\s*" + + "((\\(\\s*(.+\\s*=\\s*)?\\?\\s*(,\\s*\\?\\s*)*\\))?|\\(\\))\\s*}"); /** * Regex for 'exec' escape syntax 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 3a0daf194..f2f5fbcca 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableStatementTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableStatementTest.java @@ -22,6 +22,7 @@ import java.util.TimeZone; import java.util.UUID; +import org.junit.Assert; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; @@ -57,6 +58,8 @@ public class CallableStatementTest extends AbstractTest { .escapeIdentifier(RandomUtil.getIdentifier("CallableStatementTest_inputParams_SP")); private static String conditionalSproc = AbstractSQLGenerator .escapeIdentifier(RandomUtil.getIdentifier("CallableStatementTest_conditionalSproc")); + private static String simpleRetValSproc = AbstractSQLGenerator + .escapeIdentifier(RandomUtil.getIdentifier("CallableStatementTest_simpleSproc")); private static String getObjectLocalDateTimeProcedureName = AbstractSQLGenerator .escapeIdentifier(RandomUtil.getIdentifier("CallableStatementTest_getObjectLocalDateTime_SP")); private static String getObjectOffsetDateTimeProcedureName = AbstractSQLGenerator @@ -100,6 +103,7 @@ public static void setupTest() throws Exception { TestUtils.dropProcedureIfExists(outOfOrderSproc, stmt); TestUtils.dropProcedureIfExists(byParamNameSproc, stmt); TestUtils.dropProcedureIfExists(conditionalSproc, stmt); + TestUtils.dropProcedureIfExists(simpleRetValSproc, stmt); TestUtils.dropFunctionIfExists(userDefinedFunction, stmt); TestUtils.dropUserDefinedTypeIfExists(manyParamUserDefinedType, stmt); TestUtils.dropProcedureIfExists(manyParamProc, stmt); @@ -119,6 +123,7 @@ public static void setupTest() throws Exception { createOutOfOrderSproc(); createByParamNameSproc(); createConditionalProcedure(); + createSimpleRetValSproc(); createUserDefinedFunction(); } } @@ -1197,6 +1202,21 @@ public void testCallableStatementDefaultValues() throws SQLException { } } + @Test + public void testCallableStatementSetByAnnotatedArgs() throws SQLException { + String call = "{? = call " + simpleRetValSproc + " (@Arg1 = ?)}"; + int expectedValue = 1; // The sproc should return this value + + try (CallableStatement cstmt = connection.prepareCall(call)) { + cstmt.registerOutParameter(1, Types.INTEGER); + cstmt.setInt(1, 2); + cstmt.setString(2, "foo"); + cstmt.execute(); + + Assert.assertEquals(expectedValue, cstmt.getInt(1)); + } + } + @Test @Tag(Constants.reqExternalSetup) @Tag(Constants.xAzureSQLDB) @@ -1305,6 +1325,7 @@ public static void cleanup() throws SQLException { TestUtils.dropProcedureIfExists(byParamNameSproc, stmt); TestUtils.dropProcedureIfExists(currentTimeProc, stmt); TestUtils.dropProcedureIfExists(conditionalSproc, stmt); + TestUtils.dropProcedureIfExists(simpleRetValSproc, stmt); TestUtils.dropFunctionIfExists(userDefinedFunction, stmt); } } @@ -1373,6 +1394,13 @@ private static void createConditionalProcedure() throws SQLException { } } + private static void createSimpleRetValSproc() throws SQLException { + String sql = "CREATE PROCEDURE " + simpleRetValSproc + " (@Arg1 VARCHAR(128)) AS DECLARE @ReturnCode INT RETURN 1"; + 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 "