Skip to content

Commit

Permalink
Regression fix, continue to do extra metadata lookup call to fetch SP…
Browse files Browse the repository at this point in the history
… param name info
  • Loading branch information
tkyc committed Aug 28, 2023
1 parent 63fec25 commit d9a47c7
Showing 1 changed file with 77 additions and 84 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1401,7 +1401,6 @@ private String stripLeadingAtSign(String columnName) {
* @return the index
*/
private int findColumn(String columnName, CallableStatementGetterSetterMethod method) throws SQLServerException {
int matchPos = -1;

if (!isCursorable(executeMethod)) {
// handle `@name` as well as `name`, since `@name` is what's returned
Expand All @@ -1415,7 +1414,7 @@ private int findColumn(String columnName, CallableStatementGetterSetterMethod me
}
}

if (method == CallableStatementGetterSetterMethod.isSetterMethod && matchPos <= 0) {
if (method == CallableStatementGetterSetterMethod.isSetterMethod) {
for (int i = 0; i < inOutParam.length; i++) {
// if it is not already registered as output param or the parameter is not an input parameter, then
// set the param name and return index.
Expand All @@ -1426,101 +1425,95 @@ private int findColumn(String columnName, CallableStatementGetterSetterMethod me
}
}
}
}

MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_parameterNamedIndex"));
Object[] msgArgs = {columnName};
SQLServerException.makeFromDriverError(connection, this, form.format(msgArgs), SQLSTATE_07009, true);

} else {
// stored procedures with cursorable methods are not called directly so we have to get the metadata
if (parameterNames == null) {
try (SQLServerStatement s = (SQLServerStatement) connection.createStatement()) {
// Note we are concatenating the information from the passed in sql, not any arguments provided by the
// user
// if the user can execute the sql, any fragments of it is potentially executed via the meta data call
// through injection
// is not a security issue.
ThreePartName threePartName = ThreePartName.parse(procedureName);
StringBuilder metaQuery = new StringBuilder("exec sp_sproc_columns ");
if (null != threePartName.getDatabasePart()) {
metaQuery.append("@procedure_qualifier=");
metaQuery.append(threePartName.getDatabasePart());
metaQuery.append(", ");
}
if (null != threePartName.getOwnerPart()) {
metaQuery.append("@procedure_owner=");
metaQuery.append(threePartName.getOwnerPart());
metaQuery.append(", ");
}
if (null != threePartName.getProcedurePart()) {
// we should always have a procedure name part
metaQuery.append("@procedure_name=");
metaQuery.append(threePartName.getProcedurePart());
metaQuery.append(" , @ODBCVer=3, @fUsePattern=0");
} else {
// This should rarely happen, this will only happen if we can't find the stored procedure name
// invalidly formatted call syntax.
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_parameterNotDefinedForProcedure"));
Object[] msgArgs = {columnName, ""};
SQLServerException.makeFromDriverError(connection, this, form.format(msgArgs), SQLSTATE_07009,
false);
}
// Stored procedures with cursorable methods are not called directly, so we have to get the metadata
if (parameterNames == null) {
try (SQLServerStatement s = (SQLServerStatement) connection.createStatement()) {
// Note we are concatenating the information from the passed in sql, not any arguments provided by the
// user
// if the user can execute the sql, any fragments of it is potentially executed via the meta data call
// through injection
// is not a security issue.
ThreePartName threePartName = ThreePartName.parse(procedureName);
StringBuilder metaQuery = new StringBuilder("exec sp_sproc_columns ");
if (null != threePartName.getDatabasePart()) {
metaQuery.append("@procedure_qualifier=");
metaQuery.append(threePartName.getDatabasePart());
metaQuery.append(", ");
}
if (null != threePartName.getOwnerPart()) {
metaQuery.append("@procedure_owner=");
metaQuery.append(threePartName.getOwnerPart());
metaQuery.append(", ");
}
if (null != threePartName.getProcedurePart()) {
// we should always have a procedure name part
metaQuery.append("@procedure_name=");
metaQuery.append(threePartName.getProcedurePart());
metaQuery.append(" , @ODBCVer=3, @fUsePattern=0");
} else {
// This should rarely happen, this will only happen if we can't find the stored procedure name
// invalidly formatted call syntax.
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_parameterNotDefinedForProcedure"));
Object[] msgArgs = {columnName, ""};
SQLServerException.makeFromDriverError(connection, this, form.format(msgArgs), SQLSTATE_07009,
false);
}

try (ResultSet rs = s.executeQueryInternal(metaQuery.toString())) {
parameterNames = new HashMap<>();
insensitiveParameterNames = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
int columnIndex = 0;
while (rs.next()) {
String p = rs.getString(4).trim();
parameterNames.put(p, columnIndex);
insensitiveParameterNames.put(p, columnIndex++);
}
try (ResultSet rs = s.executeQueryInternal(metaQuery.toString())) {
parameterNames = new HashMap<>();
insensitiveParameterNames = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
int columnIndex = 0;
while (rs.next()) {
String p = rs.getString(4).trim();
parameterNames.put(p, columnIndex);
insensitiveParameterNames.put(p, columnIndex++);
}
} catch (SQLException e) {
SQLServerException.makeFromDriverError(connection, this, e.toString(), null, false);
}
} catch (SQLException e) {
SQLServerException.makeFromDriverError(connection, this, e.toString(), null, false);
}
}


// If the server didn't return anything (eg. the param names for the sp_sproc_columns), user might not
// have required permissions to view all the parameterNames. And, there's also the case depending on the permissions,
// @RETURN_VALUE may or may not be present. So, the parameterNames list might have an additional +1 parameter.
if (null != parameterNames && parameterNames.size() <= 1) {
return map.computeIfAbsent(columnName, ifAbsent -> ai.incrementAndGet());
}

// handle `@name` as well as `name`, since `@name` is what's returned
// by DatabaseMetaData#getProcedureColumns
String columnNameWithSign = columnName.startsWith("@") ? columnName : "@" + columnName;
// If the server didn't return anything (eg. the param names for the sp_sproc_columns), user might not
// have required permissions to view all the parameterNames. And, there's also the case depending on the permissions,
// @RETURN_VALUE may or may not be present. So, the parameterNames list might have an additional +1 parameter.
if (null != parameterNames && parameterNames.size() <= 1) {
return map.computeIfAbsent(columnName, ifAbsent -> ai.incrementAndGet());
}

// 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:
// handle `@name` as well as `name`, since `@name` is what's returned
// by DatabaseMetaData#getProcedureColumns
String columnNameWithSign = columnName.startsWith("@") ? columnName : "@" + columnName;

// 1. Search using case-sensitive non-locale specific (binary) compare first.
// 2. Search using case-insensitive, non-locale specific (binary) compare last.
matchPos = (parameterNames != null) ? parameterNames.get(columnNameWithSign) : -1;
if (matchPos == -1) {
matchPos = insensitiveParameterNames.get(columnNameWithSign);
}
// 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:

if (matchPos == -1) {
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_parameterNotDefinedForProcedure"));
Object[] msgArgs = {columnName, procedureName};
SQLServerException.makeFromDriverError(connection, this, form.format(msgArgs), SQLSTATE_07009, false);
}
// 1. Search using case-sensitive non-locale specific (binary) compare first.
// 2. Search using case-insensitive, non-locale specific (binary) compare last.
int matchPos = (parameterNames != null) ? parameterNames.get(columnNameWithSign) : -1;
if (matchPos == -1) {
matchPos = insensitiveParameterNames.get(columnNameWithSign);
}

// @RETURN_VALUE is always in the list. If the user uses return value ?=call(@p1) syntax then
// @p1 is index 2 otherwise its index 1.
if (bReturnValueSyntax) // 3.2717
return matchPos + 1;
else
return matchPos;
if (matchPos == -1) {
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_parameterNotDefinedForProcedure"));
Object[] msgArgs = {columnName, procedureName};
SQLServerException.makeFromDriverError(connection, this, form.format(msgArgs), SQLSTATE_07009, false);
}

return matchPos;
// @RETURN_VALUE is always in the list. If the user uses return value ?=call(@p1) syntax then
// @p1 is index 2 otherwise its index 1.
if (bReturnValueSyntax) // 3.2717
return matchPos + 1;
else
return matchPos;

}

@Override
Expand Down

0 comments on commit d9a47c7

Please sign in to comment.