helidon-io / helidon

Java libraries for writing microservices
https://helidon.io
Apache License 2.0
3.51k stars 566 forks source link

JdbcStatement named parameters should be usable more than once in a statement #2599

Closed joev0000 closed 3 years ago

joev0000 commented 3 years ago

(or, it would be nice if named parameters in a statement didn't have to be unique)

I am trying to build a statement that uses the same value in multiple positions:

SELECT ID FROM MY_TABLE WHERE (FOO = :foo) OR (FOO > :foo AND BAR = :bar)

When executed with a map containing { foo=1, bar=2 }, this causes an exception

Caused by: io.helidon.dbclient.DbClientException: Query parameters missing in Map:
    at io.helidon.dbclient.jdbc@2.1.0/io.helidon.dbclient.jdbc.JdbcStatement.prepareNamedStatement(JdbcStatement.java:121)

There is a check in that method that makes sure the number of entries in the parameter map is the same as the number of parameter names as they appear in the query:

// SQL statement and provided parameters integrity check
if (namesOrder.size() > parameters.size()) {
    throw new DbClientException(namedStatementErrorMessage(namesOrder, parameters));
}

The debugger here tells me that the namesOrder contains ["foo", "foo", "bar"] but parameters contains {foo=1, bar=2}. and so the exception is raised.

Environment Details


Problem Description

Expected Behavior

JdbcStatement allows the use of a named parameter more than once in a statement.

Current Behavior

The exception is raised: DbClientException: Query parameters missing in Map

Caused by: io.helidon.dbclient.DbClientException: Query parameters missing in Map:
        at io.helidon.dbclient.jdbc@2.1.0/io.helidon.dbclient.jdbc.JdbcStatement.prepareNamedStatement(JdbcStatement.java:121)
        at io.helidon.dbclient.jdbc@2.1.0/io.helidon.dbclient.jdbc.JdbcStatement.lambda$build$3(JdbcStatement.java:76)
        at java.base/java.util.Optional.map(Optional.java:265)
        at io.helidon.dbclient.jdbc@2.1.0/io.helidon.dbclient.jdbc.JdbcStatement.build(JdbcStatement.java:76)
        at io.helidon.dbclient.jdbc@2.1.0/io.helidon.dbclient.jdbc.JdbcStatementQuery.lambda$doExecute$2(JdbcStatementQuery.java:95)
        ... 7 more

Steps to reproduce

Put this code in a web Service, set up a router to call it:

private void test(ServerRequest request, ServerResponse response) {
    db.execute(e -> e.createNamedQuery("test", "SELECT ID FROM MY_TABLE WHERE (FOO = :foo) OR (FOO > :foo AND BAR = :bar)
            .addParam("foo", 1)
            .addParam("bar", 2)
            .execute())
        .collectList()
        .thenAccept(l -> response.send(l.toString()))
        .exceptionallyAccept(t -> respondWithStackTrace(response, t));
}
Tomas-Kraus commented 3 years ago

Yes, this change makes sense for all namedQuery/DML methods. Maybe I made the check too much strict.

Tomas-Kraus commented 3 years ago

Please let me know whether current master build works fine for you.