commit 78bf5f8933646595eaff20667e42dc18f8700465 Author: Heikki Linnakangas Date: Tue Jun 24 16:38:00 2014 +0300 Fix bug with UseDeclareFetch=1 when a transaction is committed before fetch. If a server cursor is closed before the application has fetched any rows from the result set, the "base" of the result set's cached rowset was off-by-one. Also add a regression test for the same. This fixes the bug reported by Jan-Peter Seifert. diff --git a/qresult.c b/qresult.c index 1e793c5..a1c8bf7 100644 --- a/qresult.c +++ b/qresult.c @@ -700,7 +700,6 @@ void QR_on_close_cursor(QResultClass *self) { QR_set_cursor(self, NULL); - QR_set_has_valid_base(self); } /* diff --git a/test/expected/cursor-commit.out b/test/expected/cursor-commit.out new file mode 100644 index 0000000..5c2a124 --- /dev/null +++ b/test/expected/cursor-commit.out @@ -0,0 +1,6 @@ +\! "./src/cursor-commit-test" +connected +first row: 1 +row 2: 2 +row 3: 3 +disconnecting diff --git a/test/src/cursor-commit-test.c b/test/src/cursor-commit-test.c new file mode 100644 index 0000000..169974d --- /dev/null +++ b/test/src/cursor-commit-test.c @@ -0,0 +1,87 @@ +/* + * This test case tests for a bug in result set caching, with + * UseDeclareFetch=1, that was fixed. The bug occurred when a cursor was + * closed, due to transaction commit, before any rows were fetched from + * it. That set the "base" of the internal cached rowset incorrectly, + * off by one. + */ + +#include +#include +#include + +#include "common.h" + +int main(int argc, char **argv) +{ + int rc; + HSTMT hstmt = SQL_NULL_HSTMT; + SQLCHAR charval[100]; + SQLLEN len; + int row; + + test_connect(); + + /* Start a transaction */ + rc = SQLSetConnectAttr(conn, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, SQL_IS_UINTEGER); + + rc = SQLAllocHandle(SQL_HANDLE_STMT, conn, &hstmt); + if (!SQL_SUCCEEDED(rc)) + { + print_diag("failed to allocate stmt handle", SQL_HANDLE_DBC, conn); + exit(1); + } + + rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, + (SQLPOINTER) SQL_CURSOR_STATIC, SQL_IS_UINTEGER); + CHECK_STMT_RESULT(rc, "SQLSetStmtAttr failed", hstmt); + + /* + * Begin executing a query + */ + rc = SQLExecDirect(hstmt, (SQLCHAR *) "SELECT g FROM generate_series(1,3) g", SQL_NTS); + CHECK_STMT_RESULT(rc, "SQLExecDirect failed", hstmt); + + rc = SQLBindCol(hstmt, 1, SQL_C_CHAR, &charval, sizeof(charval), &len); + CHECK_STMT_RESULT(rc, "SQLBindCol failed", hstmt); + + /* Commit. This implicitly closes the cursor in the server. */ + rc = SQLEndTran(SQL_HANDLE_DBC, conn, SQL_COMMIT); + if (!SQL_SUCCEEDED(rc)) + { + print_diag("failed to commit", SQL_HANDLE_DBC, conn); + exit(1); + } + + rc = SQLFetchScroll(hstmt, SQL_FETCH_FIRST, 0); + CHECK_STMT_RESULT(rc, "SQLFetchScroll(FIRST) failed", hstmt); + + if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) + printf("first row: %s\n", charval); + + row = 1; + while (1) + { + rc = SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0); + if (rc == SQL_NO_DATA) + break; + + row++; + + if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) + printf("row %d: %s\n", row, charval); + else + { + print_diag("SQLFetchScroll failed", SQL_HANDLE_STMT, hstmt); + exit(1); + } + } + + rc = SQLFreeStmt(hstmt, SQL_CLOSE); + CHECK_STMT_RESULT(rc, "SQLFreeStmt failed", hstmt); + + /* Clean up */ + test_disconnect(); + + return 0; +} diff --git a/test/tests b/test/tests index 7449935..1d72d56 100644 --- a/test/tests +++ b/test/tests @@ -24,6 +24,7 @@ TESTBINS = src/connect-test \ src/alter-test \ src/quotes-test \ src/cursors-test \ + src/cursor-commit-test \ src/positioned-update-test \ src/catalogfunctions-test \ src/bindcol-test \