question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Statement batch doesn't produce the correct number of update counts

See original GitHub issue

Bug Report

Versions

  • Driver: 0.9.0.M1
  • Database: Microsoft SQL Server 2019 (RTM-CU8-GDR) (KB4583459) - 15.0.4083.2 (X64) Nov 2 2020 18:35:09 Copyright © 2019 Microsoft Corporation Express Edition (64-bit) on Linux (Ubuntu 18.04.5 LTS) <X64>
  • Java: openjdk version “11.0.9.1” 2020-11-04
  • OS: Microsoft Windows [Version 10.0.19042.867]

Current Behavior

This code:

System.out.println((
    Flux.from(connectionFactory.create())
        .flatMap(c -> c
            .createStatement("DECLARE @t TABLE(i INT);\n"
                + "INSERT INTO @t VALUES (1),(2),(3);\n"
                + "SELECT * FROM @t;\n")
            .execute())
        .flatMap(it -> {
            System.out.println("IT: " + it);
            return it.getRowsUpdated();
        })
        .collectList()
        .block()
));

Prints:

IT: io.r2dbc.mssql.MssqlResult@3df5a869
[6]

Expected behavior/code

I would expect the same behaviour as in JDBC. Here’s a loop that collects both update counts and result sets:

try (Statement s = connection.createStatement()) {
    boolean b = s.execute("DECLARE @t TABLE(i INT);\n"
        + "INSERT INTO @t VALUES (1),(2),(3);\n"
        + "SELECT * FROM @t;\n");
    while (true) {
        int c = -1;

        if (b) {
            try (ResultSet rs = s.getResultSet()) {
                while (rs.next())
                    System.out.println("rs: " + rs.getInt(1));
            }
        }
        else
            c = s.getUpdateCount();

        System.out.println(b + ": " + c);

        if (!b && c == -1)
            break;
        else
            b = s.getMoreResults();
    }
}

Printing:

false: 3
rs: 1
rs: 2
rs: 3
true: -1
false: -1

The JDBC version actually checks whether the current result is an update count or a result set (this isn’t currently possible with R2DBC, see https://github.com/r2dbc/r2dbc-spi/issues/27). But even if we embed this knowledge into the reactive version, I’m not getting the right results:

AtomicInteger i = new AtomicInteger(0);
System.out.println((
    Flux.from(connectionFactory.create())
        .flatMap(c -> c
            .createStatement("DECLARE @t TABLE(i INT);\n"
                + "INSERT INTO @t VALUES (1),(2),(3);\n"
                + "SELECT * FROM @t;\n")
            .execute())
        .flatMap(it -> {
            System.out.println("IT: " + it);
            if (i.getAndIncrement() == 0)
                return it.getRowsUpdated();
            else
                return it.map((r, m) -> r.get(0));
        })
        .collectList()
        .block()
));

This still prints the wrong thing

Possible Solution

As a workaround, it seems I can use a formal batch, not the statement batch syntax (though, behind the scenes, I would imagine these should be the same thing):

System.out.println((
    Flux.from(connectionFactory.create())
        .flatMap(c -> c.createBatch()
            .add("DECLARE @t TABLE(i INT);")
            .add("INSERT INTO @t VALUES (1),(2),(3);\n")
            .add("SELECT * FROM @t;\n")
            .execute())
        .flatMap(it -> {
            System.out.println("IT: " + it);
            return it.getRowsUpdated();
        })
        .collectList()
        .block()
));

Producing:

IT: io.r2dbc.mssql.MssqlResult@134650ed
IT: io.r2dbc.mssql.MssqlResult@2463bd0a
[3, 3]

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:5 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
mp911decommented, Mar 31, 2021

The problem with variable identification is somewhat that we can’t know whether something is a variable because a lot of things in the SQL server universe start with @ (DECLARE @, SELECT @@…). I think that’s a general bug in the RPC result handling. Feel free to create another ticket to align Batch vs. non-batch result behavior. We can use this one to fix the Result behavior observed from Batch.

0reactions
mp911decommented, Apr 1, 2021

Confirming that Batch works as expected while createStatement(…) doesn’t split up the Result:

Batch

Request: SQLBatch [sql="DECLARE @t TABLE(i INT); INSERT INTO @t VALUES (1),(2),(3); SELECT * FROM @t;"]

// first Result
Response: DoneToken [done=false, hasCount=true, rowCount=3, hasMore=true, currentCommand=0]

// second Result
Response: ColumnMetadataToken [columns=[Column [name='i", type=MutableTypeInformation [maxLength=4, lengthStrategy=BYTELENTYPE, precision=10, displaySize=11, scale=0, flags=9, serverType=int, userType=0, udtTypeName="null", collation=null, charset=null], table=null]]]
Response: io.r2dbc.mssql.message.token.RowToken@1c741110
Response: io.r2dbc.mssql.message.token.RowToken@6701ff63
Response: io.r2dbc.mssql.message.token.RowToken@3d40a7c2
Response: DoneToken [done=true, hasCount=true, rowCount=3, hasMore=false, currentCommand=0]

createStatement(…).execute()

Request: RPCRequest [procName='null', procId=10, optionFlags=io.r2dbc.mssql.message.token.RpcRequest$OptionFlags@1136b469, statusFlags=0, parameterDescriptors=[RpcString [name='null', value=DECLARE @t TABLE(i INT);INSERT INTO @t VALUES (1),(2),(3);SELECT * FROM @t;], RpcString [name='null', value=]]]

// Single Result object
Response: DoneInProcToken [done=false, hasCount=true, rowCount=3, hasMore=true, currentCommand=0]
Response: ColumnMetadataToken [columns=[Column [name='i", type=MutableTypeInformation [maxLength=4, lengthStrategy=BYTELENTYPE, precision=10, displaySize=11, scale=0, flags=9, serverType=int, userType=0, udtTypeName="null", collation=null, charset=null], table=null]]]
Response: io.r2dbc.mssql.message.token.RowToken@742ddf3a
Response: io.r2dbc.mssql.message.token.RowToken@59c66b51
Response: io.r2dbc.mssql.message.token.RowToken@e7bd10e
Response: DoneInProcToken [done=false, hasCount=true, rowCount=3, hasMore=true, currentCommand=0]
Response: ReturnStatus [status=0]
Response: DoneProcToken [done=true, hasCount=false, rowCount=0, hasMore=false, currentCommand=0]
Read more comments on GitHub >

github_iconTop Results From Across the Web

ORACLE JDBC Batch execution doesn't return actual count of ...
For a callable statement batch, the server always returns the value 1 as the update count, irrespective of the number rows affected by...
Read more >
Problem in getting update records count while executeBatch()
Case 1: I added these statements as addBatch() & at the end i execute method " executeBatch()". It returned the array of integer...
Read more >
Working with SQL Server ROWCOUNT - SQLShack
SET ROWCOUNT is a system object that enforces the SQL Server Engine to stop processing the query after the specified number of rows...
Read more >
MySQL 8.0 Reference Manual :: 13.2.17 UPDATE Statement
UPDATE is a DML statement that modifies rows in a table. An UPDATE statement can start with a WITH clause to define common...
Read more >
Documentation: 15: UPDATE - PostgreSQL
The count is the number of rows updated, including matched rows whose values did not change. Note that the number may be less...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found