From b2991f9467615086ca05d08e75fef3f9ab2f286b Mon Sep 17 00:00:00 2001 From: Terry Chow Date: Mon, 22 Jul 2024 13:06:29 -0700 Subject: [PATCH 1/8] Fixed regression with specifying arg names in call syntax --- .../jdbc/SQLServerPreparedStatement.java | 4 ++- .../CallableStatementTest.java | 28 +++++++++++++++++++ 2 files changed, 31 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 050f77cfd..24d9f7fb6 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -139,7 +139,9 @@ 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*(,\\s*\\?\\s*)*\\))?|\\(\\))\\s*}"); + .compile("(^\\s*(?i)\\{(\\s*\\??\\s*=?\\s*)call [^\\(\\)]+\\s*((\\(\\s*\\?\\s*(,\\s*\\?\\s*)" + + "*\\))?|\\(\\))\\s*}|^\\s*(?i)\\{(\\s*\\??\\s*=?\\s*)call [^\\(\\)]+\\s*((\\(\\s*(\\s*@[^\\(\\)" + + "\\s]+\\s*=\\s*\\?)\\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 " From 8145e04825ac51e0be2f26e4f3952d56094d989d Mon Sep 17 00:00:00 2001 From: Terry Chow Date: Mon, 22 Jul 2024 13:58:22 -0700 Subject: [PATCH 2/8] Corrected inefficient regex --- .../microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index 24d9f7fb6..78bfb981e 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -139,9 +139,8 @@ 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*(,\\s*\\?\\s*)" + - "*\\))?|\\(\\))\\s*}|^\\s*(?i)\\{(\\s*\\??\\s*=?\\s*)call [^\\(\\)]+\\s*((\\(\\s*(\\s*@[^\\(\\)" + - "\\s]+\\s*=\\s*\\?)\\s*(,\\s*(\\s*@[^\\(\\)\\s]+\\s*=\\s*\\?)\\s*)*\\))?|\\(\\))\\s*})"); + .compile("^\\s*(?i)\\{(\\s*\\??\\s*=?\\s*)call [^\\(\\)]+\\s*((\\(\\s*(\\?|(\\s*@[^\\(\\)\\s]+\\s*" + + "=\\s*\\?))\\s*(,\\s*(\\?|(\\s*@[^\\(\\)\\s]+\\s*=\\s*\\?))\\s*)*\\))?|\\(\\))\\s*}"); /** * Regex for 'exec' escape syntax From 3798a832412583eea591895332987ee152ecd54e Mon Sep 17 00:00:00 2001 From: Terry Chow Date: Mon, 22 Jul 2024 14:07:10 -0700 Subject: [PATCH 3/8] Corrected inefficient regex --- .../microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index 78bfb981e..769f12d31 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -139,8 +139,8 @@ 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*@[^\\(\\)\\s]+\\s*" + - "=\\s*\\?))\\s*(,\\s*(\\?|(\\s*@[^\\(\\)\\s]+\\s*=\\s*\\?))\\s*)*\\))?|\\(\\))\\s*}"); + .compile("^\\s*(?i)\\{(\\s*\\??\\s*=?\\s*)call [^\\(\\)]+\\s*((\\(\\s*(@[^\\(\\)\\s]+\\s*" + + "=\\s*)?\\?\\s*(,\\s*(@[^\\(\\)\\s]+\\s*=\\s*)?\\?\\s*)*\\))?|\\(\\))\\s*}"); /** * Regex for 'exec' escape syntax From fb69c03ff0d2d743711cb58f77270618b5399022 Mon Sep 17 00:00:00 2001 From: Terry Chow Date: Mon, 22 Jul 2024 14:15:18 -0700 Subject: [PATCH 4/8] Corrected inefficient regex p2 --- .../microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index 769f12d31..31ba098ed 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -139,8 +139,8 @@ 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]+\\s*" + - "=\\s*)?\\?\\s*(,\\s*(@[^\\(\\)\\s]+\\s*=\\s*)?\\?\\s*)*\\))?|\\(\\))\\s*}"); + .compile("^\\s*(?i)\\{(\\s*\\??\\s*=?\\s*)call [^\\(\\)]+\\s*" + + "((\\(\\s*(@.+\\s*=\\s*)?\\?\\s*(,\\s*(@.+\\s*=\\s*)?\\?\\s*)*\\))?|\\(\\))\\s*}"); /** * Regex for 'exec' escape syntax From 771a0b17893df93c8a287996e121e2b90d8e67fb Mon Sep 17 00:00:00 2001 From: Terry Chow Date: Mon, 22 Jul 2024 14:21:05 -0700 Subject: [PATCH 5/8] Corrected inefficient regex p3 --- .../microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index 31ba098ed..fca890988 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -139,8 +139,8 @@ 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*=\\s*)?\\?\\s*(,\\s*(@.+\\s*=\\s*)?\\?\\s*)*\\))?|\\(\\))\\s*}"); + .compile("^\\s*(?i)\\{(\\s*\\??\\s*=?\\s*)call [^\\(\\)]+\\s*((\\(\\s*(.+\\s*=\\s*)?\\?\\s*" + + "(,\\s*(.+\\s*=\\s*)?\\?\\s*)*\\))?|\\(\\))\\s*}"); /** * Regex for 'exec' escape syntax From a6634cde9d0c4c715da3a2736749f34e841b433d Mon Sep 17 00:00:00 2001 From: Terry Chow Date: Mon, 22 Jul 2024 14:27:21 -0700 Subject: [PATCH 6/8] Corrected inefficient regex p4 --- .../microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index fca890988..b59b5e812 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -139,8 +139,8 @@ 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*=\\s*)?\\?\\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 From 5914856263a6cbcead70cf88b1268fb59b6ece14 Mon Sep 17 00:00:00 2001 From: Terry Chow Date: Mon, 22 Jul 2024 15:01:24 -0700 Subject: [PATCH 7/8] Updated changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 From c252a8be9ea688a708713bc39da74800cba29e42 Mon Sep 17 00:00:00 2001 From: Terry Chow Date: Tue, 23 Jul 2024 10:22:13 -0700 Subject: [PATCH 8/8] Added comment --- .../microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index b59b5e812..651505aa2 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -137,6 +137,8 @@ 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*" +