Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ typedef enum
#define CONN_UNABLE_TO_LOAD_DLL 212
#define CONN_ILLEGAL_TRANSACT_STATE 213
#define CONN_VALUE_OUT_OF_RANGE 214

#define CONN_INVALID_INFO_TYPE 215
#define CONN_OPTION_NOT_FOR_THE_DRIVER 216
#define CONN_EXEC_ERROR 217

Expand Down
6 changes: 6 additions & 0 deletions environ.c
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,12 @@ PGAPI_ConnectError(HDBC hdbc,
case CONN_VALUE_OUT_OF_RANGE:
pg_sqlstate_set(env, szSqlState, "HY019", "22003");
break;
case CONN_INVALID_INFO_TYPE:
pg_sqlstate_set(env, szSqlState, "HY096", "S1096");
break;
case CONN_OPTION_NOT_FOR_THE_DRIVER:
pg_sqlstate_set(env, szSqlState, "HY092", "S1092");
break;
case CONNECTION_COULD_NOT_SEND:
case CONNECTION_COULD_NOT_RECEIVE:
case CONNECTION_COMMUNICATION_ERROR:
Expand Down
2 changes: 1 addition & 1 deletion info.c
Original file line number Diff line number Diff line change
Expand Up @@ -1069,7 +1069,7 @@ MYLOG(0, "CONVERT_FUNCTIONS=" FORMAT_ULEN "\n", value);

default:
/* unrecognized key */
CC_set_error(conn, CONN_NOT_IMPLEMENTED_ERROR, "Unrecognized key passed to PGAPI_GetInfo.", NULL);
CC_set_error(conn, CONN_INVALID_INFO_TYPE, "Unrecognized key passed to PGAPI_GetInfo.", NULL);
goto cleanup;
}

Expand Down
10 changes: 8 additions & 2 deletions options.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,14 @@ set_statement_option(ConnectionClass *conn,
ci = &(SC_get_conn(stmt)->connInfo);
switch (fOption)
{
case SQL_ASYNC_ENABLE: /* ignored */
break;
case SQL_ASYNC_ENABLE:
if (vParam == SQL_ASYNC_ENABLE_ON) {
if (stmt)
SC_set_error(stmt, STMT_OPTION_NOT_FOR_THE_DRIVER,
"Asynchronous execution is not supported", func);
return SQL_ERROR;
}
break; /* SQL_ASYNC_ENABLE_OFF is a no-op, which is correct */

case SQL_BIND_TYPE:
/* now support multi-column and multi-row binding */
Expand Down
7 changes: 6 additions & 1 deletion pgapi30.c
Original file line number Diff line number Diff line change
Expand Up @@ -2401,12 +2401,17 @@ PGAPI_SetStmtAttr(HSTMT StatementHandle,
/* Whether automatic population of IPD is supported */
if (SQL_FALSE == Value)
break;
case SQL_ATTR_CURSOR_SCROLLABLE: /* -1 */
case SQL_ATTR_CURSOR_SENSITIVITY: /* -2 */
case SQL_ATTR_AUTO_IPD: /* 10001 */
/* Unsupported attributes */
SC_set_error(stmt, DESC_OPTION_NOT_FOR_THE_DRIVER, "Unsupported statement option (Set)", func);
return SQL_ERROR;
case SQL_ATTR_CURSOR_SCROLLABLE: /* -1 */
if ((SQLUINTEGER)(SQLULEN)Value == SQL_SCROLLABLE)
ret = PGAPI_SetStmtOption(StatementHandle, SQL_CURSOR_TYPE, SQL_CURSOR_STATIC);
else
ret = PGAPI_SetStmtOption(StatementHandle, SQL_CURSOR_TYPE, SQL_CURSOR_FORWARD_ONLY);
return ret;
/* case SQL_ATTR_ROW_BIND_TYPE: ** == SQL_BIND_TYPE(ODBC2.0) */
case SQL_ATTR_IMP_ROW_DESC: /* 10012 (read-only) */
case SQL_ATTR_IMP_PARAM_DESC: /* 10013 (read-only) */
Expand Down
8 changes: 8 additions & 0 deletions test/expected/async-enable.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
connected
Setting SQL_ATTR_ASYNC_ENABLE = SQL_ASYNC_ENABLE_ON
ok, got SQL_ERROR as expected
Setting SQL_ATTR_ASYNC_ENABLE = SQL_ASYNC_ENABLE_OFF
ok
Getting SQL_ATTR_ASYNC_ENABLE
async_enable: SQL_ASYNC_ENABLE_OFF
disconnecting
14 changes: 14 additions & 0 deletions test/expected/cursor-scrollable.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
connected
Setting SQL_ATTR_CURSOR_SCROLLABLE = SQL_SCROLLABLE
ok
cursor_type after SQL_SCROLLABLE: SQL_CURSOR_STATIC
scrollable: SQL_SCROLLABLE
Setting SQL_ATTR_CURSOR_SCROLLABLE = SQL_NONSCROLLABLE
ok
cursor_type after SQL_NONSCROLLABLE: SQL_CURSOR_FORWARD_ONLY
scrollable: SQL_NONSCROLLABLE
Testing scrollable cursor with SQL_FETCH_FIRST
row: row1
row: row2
after SQL_FETCH_FIRST: row1
disconnecting
6 changes: 6 additions & 0 deletions test/expected/odbc-conformance.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
connected
Testing SQLGetInfo with invalid info type 99999
SQLSTATE: HY096
Testing SQLSetConnectAttr with invalid attribute 99999
SQLSTATE: HY092
disconnecting
69 changes: 69 additions & 0 deletions test/src/async-enable-test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Test SQL_ATTR_ASYNC_ENABLE handling.
*
* Verifies that setting SQL_ATTR_ASYNC_ENABLE to SQL_ASYNC_ENABLE_ON
* returns SQL_ERROR (since async is not supported), and that setting
* SQL_ASYNC_ENABLE_OFF succeeds.
*/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#include "common.h"

int
main(int argc, char **argv)
{
SQLRETURN rc;
HSTMT hstmt = SQL_NULL_HSTMT;
SQLINTEGER async_enable;

test_connect();

rc = SQLAllocHandle(SQL_HANDLE_STMT, conn, &hstmt);
if (!SQL_SUCCEEDED(rc))
{
print_diag("failed to allocate stmt handle", SQL_HANDLE_DBC, conn);
exit(1);
}

/*
* Test 1: Setting SQL_ASYNC_ENABLE_ON should fail with SQL_ERROR
*/
printf("Setting SQL_ATTR_ASYNC_ENABLE = SQL_ASYNC_ENABLE_ON\n");
rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ASYNC_ENABLE,
(SQLPOINTER) SQL_ASYNC_ENABLE_ON, SQL_IS_UINTEGER);
if (rc == SQL_ERROR)
printf("ok, got SQL_ERROR as expected\n");
else
printf("unexpected result: %d\n", rc);

/*
* Test 2: Setting SQL_ASYNC_ENABLE_OFF should succeed
*/
printf("Setting SQL_ATTR_ASYNC_ENABLE = SQL_ASYNC_ENABLE_OFF\n");
rc = SQLSetStmtAttr(hstmt, SQL_ATTR_ASYNC_ENABLE,
(SQLPOINTER) SQL_ASYNC_ENABLE_OFF, SQL_IS_UINTEGER);
CHECK_STMT_RESULT(rc, "SQLSetStmtAttr SQL_ASYNC_ENABLE_OFF failed", hstmt);
printf("ok\n");

/*
* Test 3: Getting SQL_ATTR_ASYNC_ENABLE should return SQL_ASYNC_ENABLE_OFF
*/
printf("Getting SQL_ATTR_ASYNC_ENABLE\n");
rc = SQLGetStmtAttr(hstmt, SQL_ATTR_ASYNC_ENABLE,
&async_enable, SQL_IS_UINTEGER, NULL);
CHECK_STMT_RESULT(rc, "SQLGetStmtAttr SQL_ATTR_ASYNC_ENABLE failed", hstmt);
printf("async_enable: %s\n",
async_enable == SQL_ASYNC_ENABLE_OFF ? "SQL_ASYNC_ENABLE_OFF" :
async_enable == SQL_ASYNC_ENABLE_ON ? "SQL_ASYNC_ENABLE_ON" :
"other");

rc = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
CHECK_STMT_RESULT(rc, "SQLFreeHandle failed", hstmt);

/* Clean up */
test_disconnect();

return 0;
}
132 changes: 132 additions & 0 deletions test/src/cursor-scrollable-test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
* Test SQL_ATTR_CURSOR_SCROLLABLE setter support.
*
* Verifies that setting SQL_ATTR_CURSOR_SCROLLABLE to SQL_SCROLLABLE
* or SQL_NONSCROLLABLE properly sets the cursor type and allows
* scrollable operations.
*/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#include "common.h"

int
main(int argc, char **argv)
{
SQLRETURN rc;
HSTMT hstmt = SQL_NULL_HSTMT;
SQLUINTEGER scrollable;
SQLUINTEGER cursor_type;

test_connect();

rc = SQLAllocHandle(SQL_HANDLE_STMT, conn, &hstmt);
if (!SQL_SUCCEEDED(rc))
{
print_diag("failed to allocate stmt handle", SQL_HANDLE_DBC, conn);
exit(1);
}

/*
* Test 1: Set SQL_ATTR_CURSOR_SCROLLABLE to SQL_SCROLLABLE
*/
printf("Setting SQL_ATTR_CURSOR_SCROLLABLE = SQL_SCROLLABLE\n");
rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_SCROLLABLE,
(SQLPOINTER) SQL_SCROLLABLE, SQL_IS_UINTEGER);
CHECK_STMT_RESULT(rc, "SQLSetStmtAttr SQL_SCROLLABLE failed", hstmt);
printf("ok\n");

/* Verify cursor type is now SQL_CURSOR_STATIC */
rc = SQLGetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE,
&cursor_type, SQL_IS_UINTEGER, NULL);
CHECK_STMT_RESULT(rc, "SQLGetStmtAttr SQL_ATTR_CURSOR_TYPE failed", hstmt);
printf("cursor_type after SQL_SCROLLABLE: %s\n",
cursor_type == SQL_CURSOR_STATIC ? "SQL_CURSOR_STATIC" :
cursor_type == SQL_CURSOR_FORWARD_ONLY ? "SQL_CURSOR_FORWARD_ONLY" :
"other");

/* Verify SQL_ATTR_CURSOR_SCROLLABLE reads back correctly */
rc = SQLGetStmtAttr(hstmt, SQL_ATTR_CURSOR_SCROLLABLE,
&scrollable, SQL_IS_UINTEGER, NULL);
CHECK_STMT_RESULT(rc, "SQLGetStmtAttr SQL_ATTR_CURSOR_SCROLLABLE failed", hstmt);
printf("scrollable: %s\n",
scrollable == SQL_SCROLLABLE ? "SQL_SCROLLABLE" :
scrollable == SQL_NONSCROLLABLE ? "SQL_NONSCROLLABLE" :
"other");

/*
* Test 2: Set SQL_ATTR_CURSOR_SCROLLABLE to SQL_NONSCROLLABLE
*/
printf("Setting SQL_ATTR_CURSOR_SCROLLABLE = SQL_NONSCROLLABLE\n");
rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_SCROLLABLE,
(SQLPOINTER) SQL_NONSCROLLABLE, SQL_IS_UINTEGER);
CHECK_STMT_RESULT(rc, "SQLSetStmtAttr SQL_NONSCROLLABLE failed", hstmt);
printf("ok\n");

/* Verify cursor type is now SQL_CURSOR_FORWARD_ONLY */
rc = SQLGetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE,
&cursor_type, SQL_IS_UINTEGER, NULL);
CHECK_STMT_RESULT(rc, "SQLGetStmtAttr SQL_ATTR_CURSOR_TYPE failed", hstmt);
printf("cursor_type after SQL_NONSCROLLABLE: %s\n",
cursor_type == SQL_CURSOR_STATIC ? "SQL_CURSOR_STATIC" :
cursor_type == SQL_CURSOR_FORWARD_ONLY ? "SQL_CURSOR_FORWARD_ONLY" :
"other");

/* Verify SQL_ATTR_CURSOR_SCROLLABLE reads back correctly */
rc = SQLGetStmtAttr(hstmt, SQL_ATTR_CURSOR_SCROLLABLE,
&scrollable, SQL_IS_UINTEGER, NULL);
CHECK_STMT_RESULT(rc, "SQLGetStmtAttr SQL_ATTR_CURSOR_SCROLLABLE failed", hstmt);
printf("scrollable: %s\n",
scrollable == SQL_SCROLLABLE ? "SQL_SCROLLABLE" :
scrollable == SQL_NONSCROLLABLE ? "SQL_NONSCROLLABLE" :
"other");

/*
* Test 3: Verify scrollable cursor actually works by doing a
* SQL_FETCH_FIRST after some fetches
*/
printf("Testing scrollable cursor with SQL_FETCH_FIRST\n");
rc = SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_SCROLLABLE,
(SQLPOINTER) SQL_SCROLLABLE, SQL_IS_UINTEGER);
CHECK_STMT_RESULT(rc, "SQLSetStmtAttr SQL_SCROLLABLE failed", hstmt);

rc = SQLExecDirect(hstmt, (SQLCHAR *) "SELECT 'row' || g FROM generate_series(1, 5) g", SQL_NTS);
CHECK_STMT_RESULT(rc, "SQLExecDirect failed", hstmt);

/* Fetch first two rows */
{
char buf[40];
SQLLEN ind;

rc = SQLFetch(hstmt);
CHECK_STMT_RESULT(rc, "SQLFetch failed", hstmt);
rc = SQLGetData(hstmt, 1, SQL_C_CHAR, buf, sizeof(buf), &ind);
CHECK_STMT_RESULT(rc, "SQLGetData failed", hstmt);
printf("row: %s\n", buf);

rc = SQLFetch(hstmt);
CHECK_STMT_RESULT(rc, "SQLFetch failed", hstmt);
rc = SQLGetData(hstmt, 1, SQL_C_CHAR, buf, sizeof(buf), &ind);
CHECK_STMT_RESULT(rc, "SQLGetData failed", hstmt);
printf("row: %s\n", buf);

/* Now scroll back to first */
rc = SQLFetchScroll(hstmt, SQL_FETCH_FIRST, 0);
CHECK_STMT_RESULT(rc, "SQLFetchScroll SQL_FETCH_FIRST failed", hstmt);
rc = SQLGetData(hstmt, 1, SQL_C_CHAR, buf, sizeof(buf), &ind);
CHECK_STMT_RESULT(rc, "SQLGetData failed", hstmt);
printf("after SQL_FETCH_FIRST: %s\n", buf);
}

rc = SQLFreeStmt(hstmt, SQL_CLOSE);
CHECK_STMT_RESULT(rc, "SQLFreeStmt failed", hstmt);

rc = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
CHECK_STMT_RESULT(rc, "SQLFreeHandle failed", hstmt);

/* Clean up */
test_disconnect();

return 0;
}
59 changes: 59 additions & 0 deletions test/src/odbc-conformance-test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Test that SQLGetInfo returns SQLSTATE HY096 for invalid info types,
* and that SQLSetConnectAttr returns SQLSTATE HY092 for invalid attributes.
*/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#include "common.h"

int
main(int argc, char **argv)
{
SQLRETURN rc;
char buf[256];
SQLSMALLINT len;
char sqlstate[6];
SQLINTEGER nativeerror;
SQLSMALLINT textlen;

test_connect();

/*
* Test 1: SQLGetInfo with invalid info type should return HY096
*/
printf("Testing SQLGetInfo with invalid info type 99999\n");
rc = SQLGetInfo(conn, (SQLUSMALLINT) 99999, buf, sizeof(buf), &len);
if (rc == SQL_ERROR)
{
rc = SQLGetDiagRec(SQL_HANDLE_DBC, conn, 1, (SQLCHAR *) sqlstate,
&nativeerror, NULL, 0, &textlen);
printf("SQLSTATE: %s\n", sqlstate);
}
else
{
printf("unexpected result: %d\n", rc);
}

/*
* Test 2: SQLSetConnectAttr with invalid attribute should return HY092
*/
printf("Testing SQLSetConnectAttr with invalid attribute 99999\n");
rc = SQLSetConnectAttr(conn, (SQLINTEGER) 99999, (SQLPOINTER) 0, SQL_IS_UINTEGER);
if (rc == SQL_ERROR)
{
rc = SQLGetDiagRec(SQL_HANDLE_DBC, conn, 1, (SQLCHAR *) sqlstate,
&nativeerror, NULL, 0, &textlen);
printf("SQLSTATE: %s\n", sqlstate);
}
else
{
printf("unexpected result: %d\n", rc);
}

/* Clean up */
test_disconnect();

return 0;
}
3 changes: 3 additions & 0 deletions test/tests
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,11 @@ TESTBINS = exe/connect-test \
exe/boolsaschar-test \
exe/cvtnulldate-test \
exe/alter-test \
exe/async-enable-test \
exe/quotes-test \
exe/cursors-test \
exe/cursor-movement-test \
exe/cursor-scrollable-test \
exe/cursor-commit-test \
exe/cursor-name-test \
exe/cursor-block-delete-test \
Expand All @@ -51,6 +53,7 @@ TESTBINS = exe/connect-test \
exe/large-object-test \
exe/large-object-data-at-exec-test \
exe/odbc-escapes-test \
exe/odbc-conformance-test \
exe/wchar-char-test \
exe/params-batch-exec-test \
exe/fetch-refcursors-test \
Expand Down