commit 78bf5f8933646595eaff20667e42dc18f8700465
Author: Heikki Linnakangas <heikki.linnakangas@iki.fi>
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 <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#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 \
