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 property maxRows incorrectly affects count(*) on table valued functions.

See original GitHub issue

Setting statement property maxRows results in faulty row count on table valued functions. Below is the output from the supplied test program. The two functions numbers_tf and numbers_itf both return 10 rows. The former is table-valued function while the latter is an inlined version instead.

maxRows(0): select count(*) from dbo.numbers_itf() => 10  OK!
maxRows(5): select count(*) from dbo.numbers_itf() => 10  OK!
maxRows(0): select count(*) from dbo.numbers_tf() => 10  OK!
maxRows(5): select count(*) from dbo.numbers_tf() => 5  FAILED!

As can be seen setting maxRows to 5 changes the count to 5 instead of the expected 10. Clearly this is a bug. MaxRows should only affect the number of rows that the result set returns.

import java.math.BigInteger;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.concurrent.ThreadLocalRandom;

public class Main {
    public static void main(String[] args) throws SQLException {
        String url = "jdbc:sqlserver://localhost:1433";
        String user = "sa";
        String password = "Test1234!";
        String database = randomName("test_");
        try (
                Connection c = DriverManager.getConnection(url, user, password);
        ) {
            try {
                executeUpdate(c, "create database " + database);
                executeUpdate(c, "use " + database);


                executeUpdate(c, "create function dbo.numbers_itf()\n" +
                        "returns table\n" +
                        "as return\n" +
                        "    select * from (values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10)) t(num)\n" +
                        "\n");

                executeUpdate(c, "create function dbo.numbers_tf()\n" +
                        "  returns @result table (\n" +
                        "    num int\n" +
                        "  )\n" +
                        "  as\n" +
                        "  begin\n" +
                        "    insert into @result\n" +
                        "    select * from (values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10)) t(num)\n" +
                        "    return\n" +
                        "  end\n" +
                        "\n");


                executeTest(c, 0, "select count(*) from dbo.numbers_itf()", 10);
                executeTest(c, 5, "select count(*) from dbo.numbers_itf()", 10);
                executeTest(c, 0, "select count(*) from dbo.numbers_tf()", 10);
                executeTest(c, 5, "select count(*) from dbo.numbers_tf()", 10);

            } finally {
                executeUpdate(c,"use master");
                executeUpdate(c, "drop database " + database);
            }
        }
    }

    private static int executeUpdate(Connection c, String sql) throws SQLException {
        try (Statement s = c.createStatement()) {
            return s.executeUpdate(sql);
        }
    }

    private static void executeTest(Connection c, int maxRows, String sql, int expected) throws SQLException {
        try (Statement s = c.createStatement()) {
            s.setMaxRows(maxRows);
            s.execute(sql);
            try (ResultSet r = s.getResultSet()) {
                r.next();
                int actual = r.getInt(1);
                System.out.format("maxRows(%d): %s => %d  %s%n", s.getMaxRows(), sql, actual, (actual != expected ? "FAILED!" : "OK!"));
            }
        }
    }

    private static String randomName(String prefix) {
        return prefix + (new BigInteger(40, ThreadLocalRandom.current())).toString(32);
    }

}

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Comments:33 (24 by maintainers)

github_iconTop GitHub Comments

2reactions
ulviicommented, Apr 12, 2018

Instead of not reading the remaining packets after reaching maxRows, we could try to read and discard them. This might result in performance degradation when setMaxRows() is used, but also make the API JDBC compliant. I am currently looking into this, will try to create a PR as well as a few benchmarks.

2reactions
tseylerdcommented, Apr 9, 2018

@ulvii Good point. It’s the way how it should work (and how other jdbc drivers work). The point about the double parsing seems like an implementation detail. Why not parse it once and use the information to both count rows and create result set?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Problem with Table-Valued Functions - Microsoft Q&A
I created a function to return a value. When I called the function, for unknown reason (to me, it is unknown), I did...
Read more >
Table Valued Function Killing My Query Performance
Returning a Table Variable will make it a multi-statement table valued function and can be bad for performance due to the fact that...
Read more >
12.5 Limits on Table Column Count and Row Size
The maximum row size for a table constrains the number (and possibly size) of columns because the total length of all columns cannot...
Read more >
SQL error messages and exceptions - Oracle Help Center
In an ALTER TABLE statement, the column ' <columnName> ' has been specified as NOT NULL and either the DEFAULT clause was not...
Read more >
Tables and Views — PostgREST 9.0.0 documentation
All views and tables in the exposed schema and accessible by the active database role for a request are available for querying.
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