Skip to content

Commit

Permalink
Merge pull request #495 from gordthompson/allow-at-sign-prefixed-para…
Browse files Browse the repository at this point in the history
…m-names

recognize CallableStatement parameter names with leading '@'
  • Loading branch information
AfsanehR-zz authored Oct 7, 2017
2 parents e5f7c6c + e2ec02e commit b8daa42
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1451,6 +1451,15 @@ public NClob getNClob(String parameterName) throws SQLException {
if (paramNames != null)
l = paramNames.size();

// handle `@name` as well as `name`, since `@name` is what's returned
// by DatabaseMetaData#getProcedureColumns
String columnNameWithoutAtSign = null;
if (columnName.startsWith("@")) {
columnNameWithoutAtSign = columnName.substring(1, columnName.length());
} else {
columnNameWithoutAtSign = columnName;
}

// In order to be as accurate as possible when locating parameter name
// indexes, as well as be deterministic when running on various client
// locales, we search for parameter names using the following scheme:
Expand All @@ -1465,7 +1474,7 @@ public NClob getNClob(String parameterName) throws SQLException {
for (i = 0; i < l; i++) {
String sParam = paramNames.get(i);
sParam = sParam.substring(1, sParam.length());
if (sParam.equals(columnName)) {
if (sParam.equals(columnNameWithoutAtSign)) {
matchPos = i;
break;
}
Expand All @@ -1477,7 +1486,7 @@ public NClob getNClob(String parameterName) throws SQLException {
for (i = 0; i < l; i++) {
String sParam = paramNames.get(i);
sParam = sParam.substring(1, sParam.length());
if (sParam.equalsIgnoreCase(columnName)) {
if (sParam.equalsIgnoreCase(columnNameWithoutAtSign)) {
matchPos = i;
break;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package com.microsoft.sqlserver.jdbc.callablestatement;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
Expand All @@ -17,6 +20,7 @@

import com.microsoft.sqlserver.jdbc.SQLServerCallableStatement;
import com.microsoft.sqlserver.jdbc.SQLServerDataSource;
import com.microsoft.sqlserver.jdbc.SQLServerException;
import com.microsoft.sqlserver.testframework.AbstractTest;
import com.microsoft.sqlserver.testframework.Utils;

Expand All @@ -28,6 +32,7 @@ public class CallableStatementTest extends AbstractTest {
private static String tableNameGUID = "uniqueidentifier_Table";
private static String outputProcedureNameGUID = "uniqueidentifier_SP";
private static String setNullProcedureName = "CallableStatementTest_setNull_SP";
private static String inputParamsProcedureName = "CallableStatementTest_inputParams_SP";

private static Connection connection = null;
private static Statement stmt = null;
Expand All @@ -45,10 +50,12 @@ public static void setupTest() throws SQLException {
Utils.dropTableIfExists(tableNameGUID, stmt);
Utils.dropProcedureIfExists(outputProcedureNameGUID, stmt);
Utils.dropProcedureIfExists(setNullProcedureName, stmt);
Utils.dropProcedureIfExists(inputParamsProcedureName, stmt);

createGUIDTable();
createGUIDStoredProcedure();
createSetNullPreocedure();
createSetNullProcedure();
createInputParamsProcedure();
}

/**
Expand Down Expand Up @@ -133,6 +140,46 @@ public void getSetNullWithTypeVarchar() throws SQLException {
}
}


/**
* recognize parameter names with and without leading '@'
*
* @throws SQLException
*/
@Test
public void inputParamsTest() throws SQLException {
String call = "{CALL " + inputParamsProcedureName + " (?,?)}";
ResultSet rs = null;

// the historical way: no leading '@', parameter names respected (not positional)
CallableStatement cs1 = connection.prepareCall(call);
cs1.setString("p2", "bar");
cs1.setString("p1", "foo");
rs = cs1.executeQuery();
rs.next();
assertEquals("foobar", rs.getString(1));

// the "new" way: leading '@', parameter names still respected (not positional)
CallableStatement cs2 = connection.prepareCall(call);
cs2.setString("@p2", "world!");
cs2.setString("@p1", "Hello ");
rs = cs2.executeQuery();
rs.next();
assertEquals("Hello world!", rs.getString(1));

// sanity check: unrecognized parameter name
CallableStatement cs3 = connection.prepareCall(call);
try {
cs3.setString("@whatever", "junk");
fail("SQLServerException should have been thrown");
} catch (SQLServerException sse) {
if (!sse.getMessage().startsWith("Parameter @whatever was not defined")) {
fail("Unexpected content in exception message");
}
}

}

/**
* Cleanup after test
*
Expand All @@ -143,6 +190,7 @@ public static void cleanup() throws SQLException {
Utils.dropTableIfExists(tableNameGUID, stmt);
Utils.dropProcedureIfExists(outputProcedureNameGUID, stmt);
Utils.dropProcedureIfExists(setNullProcedureName, stmt);
Utils.dropProcedureIfExists(inputParamsProcedureName, stmt);

if (null != stmt) {
stmt.close();
Expand All @@ -162,7 +210,20 @@ private static void createGUIDTable() throws SQLException {
stmt.execute(sql);
}

private static void createSetNullPreocedure() throws SQLException {
private static void createSetNullProcedure() throws SQLException {
stmt.execute("create procedure " + setNullProcedureName + " (@p1 nvarchar(255), @p2 nvarchar(255) output) as select @p2=@p1 return 0");
}

private static void createInputParamsProcedure() throws SQLException {
String sql =
"CREATE PROCEDURE [dbo].[CallableStatementTest_inputParams_SP] " +
" @p1 nvarchar(max) = N'parameter1', " +
" @p2 nvarchar(max) = N'parameter2' " +
"AS " +
"BEGIN " +
" SET NOCOUNT ON; " +
" SELECT @p1 + @p2 AS result; " +
"END ";
stmt.execute(sql);
}
}

0 comments on commit b8daa42

Please sign in to comment.