Hi all,
We've been stuck on the stored procedures request [0] for quite a while.
This proposal explains a possible approach so we can prepare a pull request
for R2DBC SPI 1.0 M9.
We'd like to get feedback on this issue from you.
The idea of this proposal is to enable (low-level) consumption of response
items that a database server sends in return to executing a stored
procedure (callable function?) beyond rows, a single row count,
and a single error.
Stored procedures allow returning multiple result sets, errors, and
out-parameters (feel free to extend this list if it is not complete).
I'm proposing to expose low-level response segments for consumption
for a versatile and extensible mechanism to consume query results beyond
aggregated row/update count consumption.
We would extend the Result type to accept a mapping function for response
segments. As of now, we've identified the following possible segments:
* OutParameter (Out-parameter descriptor)
* Row (Exists already)
* Notice (Info/Error notification)
* RowCount (Encapsulates the affected rows count)
A client can consume these segments as these items are read from the stream.
On the Statement side, we need a mechanism to register out parameters for certain
databases. Some databases do not require upfront parameter registration, some do,
and in some cases, we require a specific type declaration to call the right function
if stored procedures are overloaded.
I'm proposing to use Statment.bind() with a specific Parameter wrapper type
to not introduce additional methods.
See the following code for how the code would look like that uses the proposed changes:
Connection connection = …;
Publisher<String> functionResult = connection.createStatement("CALL my_function($1, $2, $3, $4)")
.bind(0, Parameter.out(String.class))
.bind(1, Parameter.out(String.class, "VARCHAR(400)"))
.bind(2, Parameter.inOut("the-value"))
.bind(3, Parameter.inOut(String.class, "NVARCHAR(8000)"))
.execute()
.flatMap(result -> result. map(OutParameter.class::isInstance, response -> {
OutParameter param = (OutParameter) response;
return param.get(String.class).
}));
I prepared a gist that contains all proposed types and changes at [1].
This proposal is a draft to illustrate the approach. It does not only
solve consumption of out parameters but also allows a more
fine-grained consumption of items returned by a Statement execution.
All naming is in Flux(!) and subject to suggestions.
The main questions to proceed from here are:
* Did we consider all possible outcomes of a stored procedure call?
* Is Result.map(Predicate, Function) or Result.flatMap(Function) a good way
to consume results of a stored procedure execution?
* How else could we consume responses without returning null and
reduced overhead to not always return empty publishers?
* Did we consider all cases in out/inout parameter registration?
* What is your general feedback on the API?
Special thanks go to Lukas Eder who raised the topic and explained a
lot of quirks of stored procedures to us.
I would also ask for feedback on this proposal of from JDBC driver maintainers
in particular.
Cheers,
Mark