Closed mp911de closed 3 years ago
Proof of concept at: https://github.com/r2dbc/r2dbc-mssql/tree/result-segment-mapping (requires https://github.com/r2dbc/r2dbc-spi/pull/215).
Would this be how an IN/OUT
parameter is bound?
.bind("@Greeting", Parameters.out(R2dbcType.VARCHAR, "in value"))
Also, is there a specified order in which the OUT
parameters and other result sets are returned? E.g. what should happen if the procedure was like this?
CREATE PROCEDURE test_proc
@TheName nvarchar(50),
@Greeting nvarchar(255) OUTPUT
AS
SET NOCOUNT ON;
SET @Greeting = CONCAT('Hello ', @TheName);
SELECT 1;
Would this be how an
IN/OUT
parameter is bound?Parameters.out(R2dbcType.VARCHAR, "in value")
Yes. The server protocol requires the registration of all parameters consisting of a type, name, and direction. IN
/IN/OUT
parameters can also hold a value.
Re Parameters.out
, this comes from the SPI and we could add there also a inOut
factory variant. I think it would serve primarily the API and not really any functionality.
Also, is there a specified order in which the OUT parameters and other result sets are returned?
Yes, there there are ordinals involved. But there's also a bit of additional complexity as the general mechanism for parametrized query execution is RPC-based using built-in stored procedures. SQL Server responds with return values in the order of their declaration and the first returned user-space value is @Greeting
so either row.get(0)
or row.get("@Greeting")
works for retrieval. There's also RowMetadata
available reflecting names, types, and index positions.
E.g. what should happen if the procedure was like this?
SQL Server responds with a row first and sends the out params at the end. It seems there's no way to know for the consumer in which order any assignments happen. From a code interpretation perspective, it even makes sense, because out params become available after the procedure has terminated, there's no way to push out params from within the procedure.
FWIW, the actual call looks like:
sp_cursoropen cursor OUTPUT, 'EXEC test_proc @P0, @Greeting OUTPUT', scrollopt, ccopt,rowcount OUTPUT, '@P0 VARCHAR(4000),@Greeting VARCHAR(4000) OUTPUT', @P0, @Greeting
Re
Parameters.out
, this comes from the SPI and we could add there also ainOut
factory variant. I think it would serve primarily the API and not really any functionality.
I was thinking the same thing. Also for clarity reasons, because a lot of RDBMS call the modifier INOUT
or IN OUT
or something similar. SQL Server is a bit special with their OUTPUT
really meaning IN OUT
and no OUT
only parameter support.
SQL Server responds with a row first and sends the out params at the end. It seems there's no way to know for the consumer in which order any assignments happen. From a code interpretation perspective, it even makes sense, because out params become available after the procedure has terminated, there's no way to push out params from within the procedure.
Makes sense. I vaguely remember having to handle this differently per dialect even in JDBC for some dialects, where the order of fetching OUT
params and other results is significant, probably even SQL Server again. Some JDBC drivers buffer the things to make them available in any order, but this isn't applicable in R2DBC. Perhaps an API note could be interesting?
Also, while I see that unifying OUT
parameters and other types of Row
simplifies the API and makes it more elegant as in my PostgreSQL example here: https://github.com/r2dbc/r2dbc-spi/issues/27#issuecomment-811955839, this example shows that it would probably be useful to be able to formally expose whether a Row
is a set of OUT
parameters or a result set. Perhaps two different Segment
subtypes possibly sharing some common API could help?
Re
Parameters.out
, this comes from the SPI and we could add there also ainOut
factory variant. I think it would serve primarily the API and not really any functionality.I was thinking the same thing. Also for clarity reasons, because a lot of RDBMS call the modifier
INOUT
orIN OUT
or something similar. SQL Server is a bit special with theirOUTPUT
really meaningIN OUT
and noOUT
only parameter support.
Can you file either a SPI ticket or submit a PR if you like? The changes are limited to the Parameters
type and basically a copy of the out
methods with a separate type implementing In
and Out
interfaces.
Also, while I see that unifying
OUT
parameters and other types ofRow
simplifies the API and makes it more elegant as in my PostgreSQL example here: r2dbc/r2dbc-spi#27 (comment), this example shows that it would probably be useful to be able to formally expose whether aRow
is a set ofOUT
parameters or a result set. Perhaps two differentSegment
subtypes possibly sharing some common API could help?
The idea to map out values to something Row
-like goes into a proper direction. Thanks for this suggestion. Feel free to leave a comment on https://github.com/r2dbc/r2dbc-spi/pull/215. Coming up with a proper name for the interfaces might be a more difficult challenge than actually implementing it. Row extends <result-thing>
for rows and <result-thing>
for anything (including out params) that can be described with metadata and retrieved by name/index sounds seems a neat arrangement. On the Segment
side, we can come up with a more fine-grained set of interfaces to represent row data and non-row data.
Can you file either a SPI ticket or submit a PR if you like? The changes are limited to the
Parameters
type and basically a copy of theout
methods with a separate type implementingIn
andOut
interfaces.
OK: https://github.com/r2dbc/r2dbc-spi/issues/216. I'll provide a PR suggestion also.
Feel free to leave a comment on r2dbc/r2dbc-spi#215.
I will
It would make sense to consume out parameters returned from a stored procedure. Since the R2DBC spec doesn't define how to consume out params, we could map these onto a
Row
. R2DBC 0.9 allows defining out/in-out parameters so we could leverageStatement.bind(…)
to register out parameter declarations. The invocation syntax would follow SQL Server defaults without having the driver to rewrite a stored procedure call.Schema setup:
SQL statement:
Java code:
See: https://docs.microsoft.com/en-us/sql/relational-databases/stored-procedures/execute-a-stored-procedure?view=sql-server-ver15