Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Forbid mixing named and positional parameters in SQL statements. #787

Merged
merged 2 commits into from Mar 29, 2017

Conversation

qualidafial
Copy link
Member

Addresses #500.

In a nutshell: if a named parameter didn't match up with e.g. the method parameter name, JDBI would attempt to match on a positional param. This could have very unexpected results if e.g. the method arguments aren't in the same order as the query.

I'm rather surprised how many of our existing tests relied on this. 馃槵

This PR adds the following restrictions:

  • A SQL statement must use either named parameters, or positional parameters. Using both in the same statement will throw an exception.
  • A statement with positional parameters will only used position-bound arguments.
  • A statement with named parameters will only use name-bound arguments.

You can still bind both positional and named, but the statement will only use one category of bindings or the other.

Copy link
Member

@arteam arteam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Hopefully, our users don't have much queries with mixed parameters. But it's probably a good thing to prevent such cases early.

int index = i;
Argument a = params.findForPosition(i)
.orElseThrow(() -> {
String msg = String.format("Unable to execute, no positional parameter bound at position %s",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would just use "Unable to execute, no positional parameter bound at position " + index. The index is the last place, so it doesn't seem useful to use String.format.

a.apply(i + 1, statement, this.context);
} catch (SQLException e) {
throw new UnableToExecuteStatementException(
String.format("Exception while binding positional param at (0 based) position %d",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we could use a string concatenation here instead of String.format

Argument a = params.findForName(param)
.orElseThrow(() -> {
String msg = String.format("Unable to execute, no named parameter matches \"%s\".", param);
return new UnableToExecuteStatementException(msg, context);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we could use a more terse form:

() -> new UnableToExecuteStatementException(
                            String.format("Unable to execute, no named parameter matches \"%s\".", param),
                            context)

try {
a.apply(i + 1, statement, this.context);
} catch (SQLException e) {
throw new UnableToCreateStatementException(String.format("Exception while binding '%s'",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: I think we need to decide with with quotes we wrap the parameter in error messages (single or double quotes).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like single quotes better, because then I can be lazy and not escape them

return new UnableToExecuteStatementException(msg, context);
});
try {
a.apply(i + 1, statement, this.context);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this. seems unnecessary.

return new UnableToExecuteStatementException(msg, context);
});
try {
a.apply(i + 1, statement, this.context);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this. superfluous, we could use just context.

}

private void bindPositional(Binding params, PreparedStatement statement) {
int paramCount = stmt.getParams().size();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it doesn't make much sense to extract stmt.getParams().size(); to a variable. It's not used outside of the for loop condition.

int paramCount = stmt.getParams().size();
for (int i = 0; i < paramCount; i++) {
int index = i;
Argument a = params.findForPosition(i)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we could avoid declaring the argument variable and just invoke apply after orElseThrow.

@brianm
Copy link
Member

brianm commented Mar 29, 2017

Is this lined up for 2.x or 3.x We MUST NOT do this in 2.x

@qualidafial
Copy link
Member Author

3.0 only

@qualidafial qualidafial merged commit 1d48581 into master Mar 29, 2017
@qualidafial qualidafial deleted the no-mixey-named-positional-params branch March 29, 2017 23:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

Successfully merging this pull request may close these issues.

None yet

4 participants