@@ -72,10 +72,12 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
72
72
final String userSQL ;
73
73
74
74
// flag whether is exec escape syntax
75
- private boolean isExecEscapeSyntax ;
75
+ private boolean hasExecEscape ;
76
76
77
77
// flag whether is call escape syntax
78
- private boolean isCallEscapeSyntax ;
78
+ private boolean hasCallEscape ;
79
+
80
+ private boolean hasEmbeddedParam ;
79
81
80
82
/** Parameter positions in processed SQL statement text. */
81
83
final int [] userSQLParamPositions ;
@@ -135,20 +137,6 @@ private void setPreparedStatementHandle(int handle) {
135
137
*/
136
138
private boolean useBulkCopyForBatchInsert ;
137
139
138
- /**
139
- * Regex for JDBC 'call' escape syntax
140
- *
141
- * Matches {[? =] call sproc ([@arg =] ?, [@arg =] ?, [@arg =] ? ...)}
142
- */
143
- private static final Pattern callEscapePattern = Pattern
144
- .compile ("^\\ s*(?i)\\ {(\\ s*\\ ??\\ s*=?\\ s*)call [^\\ (\\ )]+\\ s*" +
145
- "((\\ (\\ s*(.+\\ s*=\\ s*)?\\ ?\\ s*(,\\ s*\\ ?\\ s*)*\\ ))?|\\ (\\ ))\\ s*}" );
146
-
147
- /**
148
- * Regex for 'exec' escape syntax
149
- */
150
- private static final Pattern execEscapePattern = Pattern .compile ("^\\ s*(?i)(?:exec|execute)\\ b" );
151
-
152
140
/**
153
141
* For caching data related to batch insert with bulkcopy
154
142
*/
@@ -303,8 +291,9 @@ private boolean resetPrepStmtHandle(boolean discardCurrentCacheItem) {
303
291
procedureName = parsedSQL .procedureName ;
304
292
bReturnValueSyntax = parsedSQL .bReturnValueSyntax ;
305
293
userSQL = parsedSQL .processedSQL ;
306
- isExecEscapeSyntax = isExecEscapeSyntax (sql );
307
- isCallEscapeSyntax = isCallEscapeSyntax (sql );
294
+ hasCallEscape = parsedSQL .callEscape ;
295
+ hasExecEscape = parsedSQL .execEscape ;
296
+ hasEmbeddedParam = parsedSQL .embeddedParam ;
308
297
userSQLParamPositions = parsedSQL .parameterPositions ;
309
298
initParams (userSQLParamPositions .length );
310
299
useBulkCopyForBatchInsert = conn .getUseBulkCopyForBatchInsert ();
@@ -464,8 +453,16 @@ private boolean buildPreparedStrings(Parameter[] params, boolean renewDefinition
464
453
465
454
preparedTypeDefinitions = newTypeDefinitions ;
466
455
467
- /* Replace the parameter marker '?' with the param numbers @p1, @p2 etc */
468
- preparedSQL = connection .replaceParameterMarkers (userSQL , userSQLParamPositions , params , bReturnValueSyntax );
456
+ if (hasEmbeddedParam && (hasExecEscape || hasCallEscape )) {
457
+ // If the CallableStatement has embedded parameter values eg. 'EXEC sp @param0=value0, ?, ?',
458
+ // retain these values and replace '?' explicitly in the sql string with the parameter values
459
+ // instead of the parameter numbers @p1, @p2, etc... like the else case
460
+ preparedSQL = connection .replaceParameterMarkers (userSQL , userSQLParamPositions , params );
461
+ } else {
462
+ /* Replace the parameter marker '?' with the param numbers @p1, @p2 etc */
463
+ preparedSQL = connection .replaceParameterMarkers (userSQL , userSQLParamPositions , params , bReturnValueSyntax );
464
+ }
465
+
469
466
if (bRequestedGeneratedKeys )
470
467
preparedSQL = preparedSQL + IDENTITY_QUERY ;
471
468
@@ -705,7 +702,7 @@ final void doExecutePreparedStatement(PrepStmtExecCmd command) throws SQLServerE
705
702
706
703
// Start the request and detach the response reader so that we can
707
704
// continue using it after we return.
708
- TDSWriter tdsWriter = command .startRequest (TDS .PKT_RPC );
705
+ TDSWriter tdsWriter = command .startRequest (hasEmbeddedParam ? TDS . PKT_QUERY : TDS .PKT_RPC );
709
706
710
707
needsPrepare = doPrepExec (tdsWriter , inOutParam , hasNewTypeDefinitions , hasExistingTypeDefinitions ,
711
708
command );
@@ -895,6 +892,24 @@ private void buildRPCExecParams(TDSWriter tdsWriter) throws SQLServerException {
895
892
tdsWriter .writeByte ((byte ) 0 ); // RPC procedure option 2
896
893
}
897
894
895
+ private void buildParamsNonRPC (TDSWriter tdsWriter ) throws SQLServerException {
896
+ if (getStatementLogger ().isLoggable (java .util .logging .Level .FINE )) {
897
+ getStatementLogger ().fine (toString () + ": calling PROC" + ", SQL:" + preparedSQL );
898
+ }
899
+
900
+ expectPrepStmtHandle = false ;
901
+ executedSqlDirectly = true ;
902
+ expectCursorOutParams = false ;
903
+ outParamIndexAdjustment = 0 ;
904
+ tdsWriter .writeString (preparedSQL );
905
+ if (connection .isAEv2 ()) {
906
+ tdsWriter .sendEnclavePackage (preparedSQL , enclaveCEKs );
907
+ }
908
+
909
+ tdsWriter .writeByte ((byte ) 0 ); // RPC procedure option 1
910
+ tdsWriter .writeByte ((byte ) 0 ); // RPC procedure option 2
911
+ }
912
+
898
913
private void buildPrepParams (TDSWriter tdsWriter ) throws SQLServerException {
899
914
if (getStatementLogger ().isLoggable (java .util .logging .Level .FINE ))
900
915
getStatementLogger ().fine (toString () + ": calling sp_prepare: PreparedHandle:"
@@ -1212,7 +1227,7 @@ private boolean doPrepExec(TDSWriter tdsWriter, Parameter[] params, boolean hasN
1212
1227
1213
1228
boolean needsPrepare = (hasNewTypeDefinitions && hasExistingTypeDefinitions ) || !hasPreparedStatementHandle ();
1214
1229
boolean isPrepareMethodSpPrepExec = connection .getPrepareMethod ().equals (PrepareMethod .PREPEXEC .toString ());
1215
- boolean callRpcDirectly = callRPCDirectly (params );
1230
+ boolean makeRPC = makeRPC (params );
1216
1231
1217
1232
// Cursors don't use statement pooling.
1218
1233
if (isCursorable (executeMethod )) {
@@ -1221,8 +1236,20 @@ private boolean doPrepExec(TDSWriter tdsWriter, Parameter[] params, boolean hasN
1221
1236
else
1222
1237
buildServerCursorExecParams (tdsWriter );
1223
1238
} else {
1224
- // if it is a parameterized stored procedure call and is not TVP, use sp_execute directly.
1225
- if (needsPrepare && callRpcDirectly ) {
1239
+ // If this is a stored procedure with
1240
+ // embedded parameters eg EXEC sp @param1=value1,?,?...
1241
+ if (hasEmbeddedParam && needsPrepare ) {
1242
+ buildParamsNonRPC (tdsWriter );
1243
+
1244
+ // Return immediately as we don't need to send the parameters over RPC
1245
+ // as the parameters should be embedded in the 'preparedSQL' string,
1246
+ // which is what is sent to the server
1247
+ return needsPrepare ;
1248
+
1249
+ }
1250
+ // If this is a stored procedure, build the RPC call to execute it directly
1251
+ // Can't be a cstmt batch call, as using RPC for batch will cause a significant performance drop
1252
+ else if (makeRPC && needsPrepare && executeMethod != EXECUTE_BATCH ) {
1226
1253
buildRPCExecParams (tdsWriter );
1227
1254
}
1228
1255
// Move overhead of needing to do prepare & unprepare to only use cases that need more than one execution.
@@ -1254,7 +1281,7 @@ else if (needsPrepare && !connection.getEnablePrepareOnFirstPreparedStatementCal
1254
1281
}
1255
1282
}
1256
1283
1257
- sendParamsByRPC (tdsWriter , params , bReturnValueSyntax , callRpcDirectly );
1284
+ sendParamsByRPC (tdsWriter , params , bReturnValueSyntax , makeRPC );
1258
1285
1259
1286
return needsPrepare ;
1260
1287
}
@@ -1266,18 +1293,15 @@ else if (needsPrepare && !connection.getEnablePrepareOnFirstPreparedStatementCal
1266
1293
* @return
1267
1294
* @throws SQLServerException
1268
1295
*/
1269
- boolean callRPCDirectly (Parameter [] params ) throws SQLServerException {
1296
+ boolean makeRPC (Parameter [] params ) throws SQLServerException {
1270
1297
int paramCount = SQLServerConnection .countParams (userSQL );
1271
1298
1272
1299
// In order to execute sprocs directly the following must be true:
1273
1300
// 1. There must be a sproc name
1274
1301
// 2. There must be parameters
1275
1302
// 3. Parameters must not be a TVP type
1276
- // 4. Compliant CALL escape syntax
1277
- // If isExecEscapeSyntax is true, EXEC escape syntax is used then use prior behaviour of
1278
- // wrapping call to execute the procedure
1279
- return (null != procedureName && paramCount != 0 && !isTVPType (params ) && isCallEscapeSyntax
1280
- && !isExecEscapeSyntax );
1303
+ // 4. Compliant CALL escape syntax or compliant EXEC escape syntax
1304
+ return (null != procedureName && paramCount != 0 && !isTVPType (params ) && (hasCallEscape || hasExecEscape ));
1281
1305
}
1282
1306
1283
1307
/**
@@ -1297,14 +1321,6 @@ private boolean isTVPType(Parameter[] params) throws SQLServerException {
1297
1321
return false ;
1298
1322
}
1299
1323
1300
- private boolean isExecEscapeSyntax (String sql ) {
1301
- return execEscapePattern .matcher (sql ).find ();
1302
- }
1303
-
1304
- private boolean isCallEscapeSyntax (String sql ) {
1305
- return callEscapePattern .matcher (sql ).find ();
1306
- }
1307
-
1308
1324
/**
1309
1325
* Executes sp_prepare to prepare a parameterized statement and sets the prepared statement handle
1310
1326
*
0 commit comments