/*
 * Test deleting tuples all with scrolling block cursors BOF ->
 * EOF -> BOF -> ...
 */

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#include "common.h"

#define	TOTAL	120
#define	BLOCK	4

static HSTMT	hstmte = SQL_NULL_HSTMT;

static SQLRETURN delete_loop(HSTMT hstmt)
{
	SQLRETURN	rc;
	BOOL	use_first_last = 0;
	int	delcnt = 0, delsav, loopcnt = 0;
	SQLSMALLINT	orientation = SQL_FETCH_FIRST;

	do {
		printf("\torientation=%d delete count=%d\n", orientation, delcnt);
		delsav = delcnt;
		if (use_first_last)
			orientation = (orientation == SQL_FETCH_NEXT ? SQL_FETCH_FIRST : SQL_FETCH_LAST);
		while (rc = SQLFetchScroll(hstmt, orientation, 0), SQL_SUCCEEDED(rc))
		{
			orientation = (orientation == SQL_FETCH_NEXT ? SQL_FETCH_NEXT : (orientation == SQL_FETCH_FIRST ? SQL_FETCH_NEXT : SQL_FETCH_PRIOR));
			rc = SQLSetPos(hstmt, 1, SQL_DELETE, SQL_LCK_NO_CHANGE);
			CHECK_STMT_RESULT(rc, "SQLSetPos delete failed", hstmt);
			delcnt++;
		}
		if (SQL_NO_DATA != rc)
		{
			CHECK_STMT_RESULT(rc, "SQLFetchScroll failed", hstmt);
		}
		orientation = (orientation == SQL_FETCH_NEXT ? SQL_FETCH_PRIOR : SQL_FETCH_NEXT);
		if (++loopcnt == 4)
			SQLExecDirect(hstmte, (SQLCHAR *) "savepoint miho", SQL_NTS);
	} while (delcnt != delsav);
	printf("delete all count %d\n", delcnt);

	return	SQL_SUCCESS;
}

int main(int argc, char **argv)
{
	int		rc;
	HSTMT		hstmt = SQL_NULL_HSTMT;
	int		i, j, k;
	int		count = TOTAL;
	char		query[100];
	SQLLEN		rowArraySize = BLOCK;
	SQLULEN		rowsFetched;
	SQLINTEGER	id[BLOCK];
	SQLLEN		cbLen[BLOCK];

	/****
     * Run this test with Fetch=37 when UseDeclareFecth=1.
     */
	test_connect_ext("UpdatableCursors=1;Fetch=37");

	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 = SQLAllocHandle(SQL_HANDLE_STMT, conn, &hstmte);
	if (!SQL_SUCCEEDED(rc))
	{
		print_diag("failed to allocate stmt handle2", SQL_HANDLE_DBC, conn);
		exit(1);
	}

	rc = SQLExecDirect(hstmt, (SQLCHAR *) "create temporary table tmptable(id serial primary key)", SQL_NTS);
	CHECK_STMT_RESULT(rc, "SQLExecDirect create table failed", hstmt);

	/* insert into a table */
	for (i = 0; i < count; i++)
	{
		snprintf(query, sizeof(query), "insert into tmptable values (%d)", i);
		rc = SQLExecDirect(hstmt, (SQLCHAR *) query, SQL_NTS);
		CHECK_STMT_RESULT(rc, "insert into table failed", hstmt);
	}
	rc = SQLFreeStmt(hstmt, SQL_CLOSE);
	CHECK_STMT_RESULT(rc, "SQLFreeStmt failed", hstmt);

	/*
	 * Block cursor
	 */
	rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROWS_FETCHED_PTR, (SQLPOINTER) &rowsFetched, 0);
	CHECK_STMT_RESULT(rc, "SQLSetStmtAttr ROWS_FETCHED_PTR failed", hstmt);
	rc = SQLBindCol(hstmt, 1, SQL_C_SLONG, &id, 0, cbLen);
	CHECK_STMT_RESULT(rc, "SQLBindCol failed", hstmt);
	rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER) rowArraySize, SQL_IS_UINTEGER);
	CHECK_STMT_RESULT(rc, "SQLSetStmtAttr ROW_ARRAY_SIZE failed", hstmt);
	rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CONCURRENCY, (SQLPOINTER) SQL_CONCUR_ROWVER, 0);
	CHECK_STMT_RESULT(rc, "SQLSetStmtAttr CONCURRENCY failed", hstmt);
	rc = SQLSetConnectAttr(conn, SQL_AUTOCOMMIT, (SQLPOINTER) SQL_AUTOCOMMIT_OFF, 0);

	rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE, (SQLPOINTER) SQL_CURSOR_KEYSET_DRIVEN, 0);
	CHECK_STMT_RESULT(rc, "SQLSetStmtAttr CURSOR_TYPE failed", hstmt);
	rc = SQLExecDirect(hstmt, (SQLCHAR *) "select * from tmptable", SQL_NTS);
	CHECK_STMT_RESULT(rc, "select failed", hstmt);
	rc = SQLExecDirect(hstmte, (SQLCHAR *) "savepoint yuuki", SQL_NTS);
	CHECK_STMT_RESULT(rc, "savepoint failed", hstmte);
	/*
	 * Scroll next -> EOF -> prior -> BOF -> next -> EOF ->
	 * ......
	 */
	delete_loop(hstmt);	/* the 1st loop */

	rc = SQLExecDirect(hstmte, (SQLCHAR *) "rollback to yuuki;release yuuki", SQL_NTS);
	CHECK_STMT_RESULT(rc, "rollback failed", hstmte);
	for (i = 0, j = count, i = 0; i < 2; i++)
	{
		for (k = 0; k < rowArraySize; k++)
		{
			cbLen[k] = 0;
			id[k] = j++;
		}
		/* rc = SQLBulkOperations(hstmt, SQL_ADD);
		CHECK_STMT_RESULT(rc, "SQLBulkOperations SQL_ADD failed", hstmt); */
		rc = SQLSetPos(hstmt, 0, SQL_ADD, SQL_LCK_NO_CHANGE);
		CHECK_STMT_RESULT(rc, "SQLSetPos SQL_ADD failed", hstmt);
		if (0 == i)
		{
			rc = SQLExecDirect(hstmte, (SQLCHAR *) "savepoint yuuki", SQL_NTS);
			CHECK_STMT_RESULT(rc, "savpoint failed", hstmte);
		}
	}

	delete_loop(hstmt);	/* the 2nd loop */

	rc = SQLExecDirect(hstmte, (SQLCHAR *) "rollback to yuuki;release yuuki", SQL_NTS);
	CHECK_STMT_RESULT(rc, "rollback failed", hstmte);

	delete_loop(hstmt);	/* the 3rd loop */

	rc = SQLExecDirect(hstmte, (SQLCHAR *) "rollback to miho;release miho", SQL_NTS);
	CHECK_STMT_RESULT(rc, "rollback failed", hstmte);

	delete_loop(hstmt);	/* the 4th loop */

	rc = SQLEndTran(SQL_HANDLE_DBC, conn, SQL_ROLLBACK);
	CHECK_STMT_RESULT(rc, "SQLEndTran failed", hstmt);
	rc = SQLFreeStmt(hstmt, SQL_CLOSE);
	CHECK_STMT_RESULT(rc, "SQLFreeStmt failed", hstmt);

	/* Clean up */
	test_disconnect();

	return 0;
}
