622 lines
15 KiB
C
622 lines
15 KiB
C
|
|
/* Module: options.c
|
|
*
|
|
* Description: This module contains routines for getting/setting
|
|
* connection and statement options.
|
|
*
|
|
* Classes: n/a
|
|
*
|
|
* API functions: SQLSetConnectOption, SQLSetStmtOption, SQLGetConnectOption,
|
|
* SQLGetStmtOption
|
|
*
|
|
* Comments: See "notice.txt" for copyright and license information.
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "psqlodbc.h"
|
|
|
|
#ifndef WIN32
|
|
#include "iodbc.h"
|
|
#include "isql.h"
|
|
#include "isqlext.h"
|
|
#else
|
|
#include <windows.h>
|
|
#include <sql.h>
|
|
#include <sqlext.h>
|
|
#endif
|
|
|
|
#include "environ.h"
|
|
#include "connection.h"
|
|
#include "statement.h"
|
|
#include "qresult.h"
|
|
|
|
|
|
extern GLOBAL_VALUES globals;
|
|
|
|
RETCODE set_statement_option(ConnectionClass *conn,
|
|
StatementClass *stmt,
|
|
UWORD fOption,
|
|
UDWORD vParam);
|
|
|
|
|
|
|
|
RETCODE set_statement_option(ConnectionClass *conn,
|
|
StatementClass *stmt,
|
|
UWORD fOption,
|
|
UDWORD vParam)
|
|
{
|
|
static char *func="set_statement_option";
|
|
char changed = FALSE;
|
|
|
|
|
|
switch(fOption) {
|
|
case SQL_ASYNC_ENABLE:/* ignored */
|
|
break;
|
|
|
|
case SQL_BIND_TYPE:
|
|
/* now support multi-column and multi-row binding */
|
|
if (conn) conn->stmtOptions.bind_size = vParam;
|
|
if (stmt) stmt->options.bind_size = vParam;
|
|
break;
|
|
|
|
case SQL_CONCURRENCY:
|
|
/* positioned update isn't supported so cursor concurrency is read-only */
|
|
|
|
if (conn) conn->stmtOptions.scroll_concurrency = vParam;
|
|
if (stmt) stmt->options.scroll_concurrency = vParam;
|
|
break;
|
|
|
|
/*
|
|
if (globals.lie) {
|
|
if (conn) conn->stmtOptions.scroll_concurrency = vParam;
|
|
if (stmt) stmt->options.scroll_concurrency = vParam;
|
|
}
|
|
else {
|
|
|
|
if (conn) conn->stmtOptions.scroll_concurrency = SQL_CONCUR_READ_ONLY;
|
|
if (stmt) stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
|
|
|
|
if (vParam != SQL_CONCUR_READ_ONLY)
|
|
changed = TRUE;
|
|
}
|
|
break;
|
|
*/
|
|
|
|
case SQL_CURSOR_TYPE:
|
|
/* if declare/fetch, then type can only be forward.
|
|
otherwise, it can only be forward or static.
|
|
*/
|
|
mylog("SetStmtOption(): SQL_CURSOR_TYPE = %d\n", vParam);
|
|
|
|
if (globals.lie) {
|
|
|
|
if (conn) conn->stmtOptions.cursor_type = vParam;
|
|
if (stmt) stmt->options.cursor_type = vParam;
|
|
|
|
}
|
|
else {
|
|
if (globals.use_declarefetch) {
|
|
|
|
if (conn) conn->stmtOptions.cursor_type = SQL_CURSOR_FORWARD_ONLY;
|
|
if (stmt) stmt->options.cursor_type = SQL_CURSOR_FORWARD_ONLY;
|
|
|
|
if (vParam != SQL_CURSOR_FORWARD_ONLY)
|
|
changed = TRUE;
|
|
}
|
|
else {
|
|
if (vParam == SQL_CURSOR_FORWARD_ONLY || vParam == SQL_CURSOR_STATIC) {
|
|
|
|
if (conn) conn->stmtOptions.cursor_type = vParam; /* valid type */
|
|
if (stmt) stmt->options.cursor_type = vParam; /* valid type */
|
|
}
|
|
else {
|
|
|
|
if (conn) conn->stmtOptions.cursor_type = SQL_CURSOR_STATIC;
|
|
if (stmt) stmt->options.cursor_type = SQL_CURSOR_STATIC;
|
|
|
|
changed = TRUE;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SQL_KEYSET_SIZE: /* ignored, but saved and returned */
|
|
mylog("SetStmtOption(): SQL_KEYSET_SIZE, vParam = %d\n", vParam);
|
|
|
|
if (conn) conn->stmtOptions.keyset_size = vParam;
|
|
if (stmt) stmt->options.keyset_size = vParam;
|
|
|
|
break;
|
|
|
|
/*
|
|
if (globals.lie)
|
|
stmt->keyset_size = vParam;
|
|
else {
|
|
stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
|
|
stmt->errormsg = "Driver does not support keyset size option";
|
|
SC_log_error(func, "", stmt);
|
|
return SQL_ERROR;
|
|
}
|
|
*/
|
|
|
|
case SQL_MAX_LENGTH:/* ignored, but saved */
|
|
mylog("SetStmtOption(): SQL_MAX_LENGTH, vParam = %d\n", vParam);
|
|
if (conn) conn->stmtOptions.maxLength = vParam;
|
|
if (stmt) stmt->options.maxLength = vParam;
|
|
break;
|
|
|
|
case SQL_MAX_ROWS: /* ignored, but saved */
|
|
mylog("SetStmtOption(): SQL_MAX_ROWS, vParam = %d\n", vParam);
|
|
if (conn) conn->stmtOptions.maxRows = vParam;
|
|
if (stmt) stmt->options.maxRows = vParam;
|
|
break;
|
|
|
|
case SQL_NOSCAN: /* ignored */
|
|
mylog("SetStmtOption: SQL_NOSCAN, vParam = %d\n", vParam);
|
|
break;
|
|
|
|
case SQL_QUERY_TIMEOUT: /* ignored */
|
|
mylog("SetStmtOption: SQL_QUERY_TIMEOUT, vParam = %d\n", vParam);
|
|
/* "0" returned in SQLGetStmtOption */
|
|
break;
|
|
|
|
case SQL_RETRIEVE_DATA: /* ignored, but saved */
|
|
mylog("SetStmtOption(): SQL_RETRIEVE_DATA, vParam = %d\n", vParam);
|
|
if (conn) conn->stmtOptions.retrieve_data = vParam;
|
|
if (stmt) stmt->options.retrieve_data = vParam;
|
|
break;
|
|
|
|
case SQL_ROWSET_SIZE:
|
|
mylog("SetStmtOption(): SQL_ROWSET_SIZE, vParam = %d\n", vParam);
|
|
|
|
|
|
/* Save old rowset size for SQLExtendedFetch purposes
|
|
If the rowset_size is being changed since the last call
|
|
to fetch rows.
|
|
*/
|
|
|
|
if (stmt && stmt->save_rowset_size <= 0 && stmt->last_fetch_count > 0 )
|
|
stmt->save_rowset_size = stmt->options.rowset_size;
|
|
|
|
if (vParam < 1) {
|
|
vParam = 1;
|
|
changed = TRUE;
|
|
}
|
|
|
|
if (conn) conn->stmtOptions.rowset_size = vParam;
|
|
if (stmt) stmt->options.rowset_size = vParam;
|
|
|
|
break;
|
|
|
|
case SQL_SIMULATE_CURSOR: /* NOT SUPPORTED */
|
|
if (stmt) {
|
|
stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
|
|
stmt->errormsg = "Simulated positioned update/delete not supported. Use the cursor library.";
|
|
SC_log_error(func, "", stmt);
|
|
}
|
|
if (conn) {
|
|
conn->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
|
|
conn->errormsg = "Simulated positioned update/delete not supported. Use the cursor library.";
|
|
CC_log_error(func, "", conn);
|
|
}
|
|
return SQL_ERROR;
|
|
|
|
case SQL_USE_BOOKMARKS:
|
|
|
|
if (stmt) stmt->options.use_bookmarks = vParam;
|
|
if (conn) conn->stmtOptions.use_bookmarks = vParam;
|
|
break;
|
|
|
|
default:
|
|
{
|
|
char option[64];
|
|
|
|
if (stmt) {
|
|
stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
|
|
stmt->errormsg = "Unknown statement option (Set)";
|
|
sprintf(option, "fOption=%d, vParam=%ld", fOption, vParam);
|
|
SC_log_error(func, option, stmt);
|
|
}
|
|
if (conn) {
|
|
conn->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
|
|
conn->errormsg = "Unknown statement option (Set)";
|
|
sprintf(option, "fOption=%d, vParam=%ld", fOption, vParam);
|
|
CC_log_error(func, option, conn);
|
|
}
|
|
|
|
return SQL_ERROR;
|
|
}
|
|
}
|
|
|
|
if (changed) {
|
|
if (stmt) {
|
|
stmt->errormsg = "Requested value changed.";
|
|
stmt->errornumber = STMT_OPTION_VALUE_CHANGED;
|
|
}
|
|
if (conn) {
|
|
conn->errormsg = "Requested value changed.";
|
|
conn->errornumber = STMT_OPTION_VALUE_CHANGED;
|
|
}
|
|
return SQL_SUCCESS_WITH_INFO;
|
|
}
|
|
else
|
|
return SQL_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
/* Implements only SQL_AUTOCOMMIT */
|
|
RETCODE SQL_API SQLSetConnectOption(
|
|
HDBC hdbc,
|
|
UWORD fOption,
|
|
UDWORD vParam)
|
|
{
|
|
static char *func="SQLSetConnectOption";
|
|
ConnectionClass *conn = (ConnectionClass *) hdbc;
|
|
char changed = FALSE;
|
|
RETCODE retval;
|
|
int i;
|
|
|
|
mylog("%s: entering...\n", func);
|
|
|
|
if ( ! conn) {
|
|
CC_log_error(func, "", NULL);
|
|
return SQL_INVALID_HANDLE;
|
|
}
|
|
|
|
|
|
switch (fOption) {
|
|
/* Statement Options
|
|
(apply to all stmts on the connection and become defaults for new stmts)
|
|
*/
|
|
case SQL_ASYNC_ENABLE:
|
|
case SQL_BIND_TYPE:
|
|
case SQL_CONCURRENCY:
|
|
case SQL_CURSOR_TYPE:
|
|
case SQL_KEYSET_SIZE:
|
|
case SQL_MAX_LENGTH:
|
|
case SQL_MAX_ROWS:
|
|
case SQL_NOSCAN:
|
|
case SQL_QUERY_TIMEOUT:
|
|
case SQL_RETRIEVE_DATA:
|
|
case SQL_ROWSET_SIZE:
|
|
case SQL_SIMULATE_CURSOR:
|
|
case SQL_USE_BOOKMARKS:
|
|
|
|
/* Affect all current Statements */
|
|
for (i = 0; i < conn->num_stmts; i++) {
|
|
if ( conn->stmts[i]) {
|
|
set_statement_option(NULL, conn->stmts[i], fOption, vParam);
|
|
}
|
|
}
|
|
|
|
/* Become the default for all future statements on this connection */
|
|
retval = set_statement_option(conn, NULL, fOption, vParam);
|
|
|
|
if (retval == SQL_SUCCESS_WITH_INFO)
|
|
changed = TRUE;
|
|
else if (retval == SQL_ERROR)
|
|
return SQL_ERROR;
|
|
|
|
break;
|
|
|
|
/**********************************/
|
|
/***** Connection Options *******/
|
|
/**********************************/
|
|
|
|
case SQL_ACCESS_MODE: /* ignored */
|
|
break;
|
|
|
|
case SQL_AUTOCOMMIT:
|
|
|
|
/* Since we are almost always in a transaction, this is now ok.
|
|
Even if we were, the logic will handle it by sending a commit
|
|
after the statement.
|
|
|
|
if (CC_is_in_trans(conn)) {
|
|
conn->errormsg = "Cannot switch commit mode while a transaction is in progres";
|
|
conn->errornumber = CONN_TRANSACT_IN_PROGRES;
|
|
CC_log_error(func, "", conn);
|
|
return SQL_ERROR;
|
|
}
|
|
*/
|
|
|
|
mylog("SQLSetConnectOption: AUTOCOMMIT: transact_status=%d, vparam=%d\n", conn->transact_status, vParam);
|
|
|
|
switch(vParam) {
|
|
case SQL_AUTOCOMMIT_OFF:
|
|
CC_set_autocommit_off(conn);
|
|
break;
|
|
|
|
case SQL_AUTOCOMMIT_ON:
|
|
CC_set_autocommit_on(conn);
|
|
break;
|
|
|
|
default:
|
|
conn->errormsg = "Illegal parameter value for SQL_AUTOCOMMIT";
|
|
conn->errornumber = CONN_INVALID_ARGUMENT_NO;
|
|
CC_log_error(func, "", conn);
|
|
return SQL_ERROR;
|
|
}
|
|
|
|
break;
|
|
|
|
case SQL_CURRENT_QUALIFIER: /* ignored */
|
|
break;
|
|
|
|
case SQL_LOGIN_TIMEOUT: /* ignored */
|
|
break;
|
|
|
|
case SQL_PACKET_SIZE: /* ignored */
|
|
break;
|
|
|
|
case SQL_QUIET_MODE: /* ignored */
|
|
break;
|
|
|
|
case SQL_TXN_ISOLATION: /* ignored */
|
|
break;
|
|
|
|
/* These options should be handled by driver manager */
|
|
case SQL_ODBC_CURSORS:
|
|
case SQL_OPT_TRACE:
|
|
case SQL_OPT_TRACEFILE:
|
|
case SQL_TRANSLATE_DLL:
|
|
case SQL_TRANSLATE_OPTION:
|
|
CC_log_error(func, "This connect option (Set) is only used by the Driver Manager", conn);
|
|
break;
|
|
|
|
default:
|
|
{
|
|
char option[64];
|
|
conn->errormsg = "Unknown connect option (Set)";
|
|
conn->errornumber = CONN_UNSUPPORTED_OPTION;
|
|
sprintf(option, "fOption=%d, vParam=%ld", fOption, vParam);
|
|
CC_log_error(func, option, conn);
|
|
return SQL_ERROR;
|
|
}
|
|
|
|
}
|
|
|
|
if (changed) {
|
|
conn->errornumber = CONN_OPTION_VALUE_CHANGED;
|
|
conn->errormsg = "Requested value changed.";
|
|
return SQL_SUCCESS_WITH_INFO;
|
|
}
|
|
else
|
|
return SQL_SUCCESS;
|
|
}
|
|
|
|
/* - - - - - - - - - */
|
|
|
|
/* This function just can tell you whether you are in Autcommit mode or not */
|
|
RETCODE SQL_API SQLGetConnectOption(
|
|
HDBC hdbc,
|
|
UWORD fOption,
|
|
PTR pvParam)
|
|
{
|
|
static char *func="SQLGetConnectOption";
|
|
ConnectionClass *conn = (ConnectionClass *) hdbc;
|
|
|
|
mylog("%s: entering...\n", func);
|
|
|
|
if (! conn) {
|
|
CC_log_error(func, "", NULL);
|
|
return SQL_INVALID_HANDLE;
|
|
}
|
|
|
|
switch (fOption) {
|
|
case SQL_ACCESS_MODE:/* NOT SUPPORTED */
|
|
*((UDWORD *) pvParam) = SQL_MODE_READ_WRITE;
|
|
break;
|
|
|
|
case SQL_AUTOCOMMIT:
|
|
*((UDWORD *)pvParam) = (UDWORD)( CC_is_in_autocommit(conn) ?
|
|
SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF);
|
|
break;
|
|
|
|
case SQL_CURRENT_QUALIFIER: /* don't use qualifiers */
|
|
if(pvParam)
|
|
strcpy(pvParam, "");
|
|
|
|
break;
|
|
|
|
case SQL_LOGIN_TIMEOUT: /* NOT SUPPORTED */
|
|
*((UDWORD *) pvParam) = 0;
|
|
break;
|
|
|
|
case SQL_PACKET_SIZE: /* NOT SUPPORTED */
|
|
*((UDWORD *) pvParam) = globals.socket_buffersize;
|
|
break;
|
|
|
|
case SQL_QUIET_MODE:/* NOT SUPPORTED */
|
|
*((UDWORD *) pvParam) = (UDWORD) NULL;
|
|
break;
|
|
|
|
case SQL_TXN_ISOLATION:/* NOT SUPPORTED */
|
|
*((UDWORD *) pvParam) = SQL_TXN_SERIALIZABLE;
|
|
break;
|
|
|
|
/* These options should be handled by driver manager */
|
|
case SQL_ODBC_CURSORS:
|
|
case SQL_OPT_TRACE:
|
|
case SQL_OPT_TRACEFILE:
|
|
case SQL_TRANSLATE_DLL:
|
|
case SQL_TRANSLATE_OPTION:
|
|
CC_log_error(func, "This connect option (Get) is only used by the Driver Manager", conn);
|
|
break;
|
|
|
|
default:
|
|
{
|
|
char option[64];
|
|
conn->errormsg = "Unknown connect option (Get)";
|
|
conn->errornumber = CONN_UNSUPPORTED_OPTION;
|
|
sprintf(option, "fOption=%d", fOption);
|
|
CC_log_error(func, option, conn);
|
|
return SQL_ERROR;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
return SQL_SUCCESS;
|
|
}
|
|
|
|
/* - - - - - - - - - */
|
|
|
|
RETCODE SQL_API SQLSetStmtOption(
|
|
HSTMT hstmt,
|
|
UWORD fOption,
|
|
UDWORD vParam)
|
|
{
|
|
static char *func="SQLSetStmtOption";
|
|
StatementClass *stmt = (StatementClass *) hstmt;
|
|
|
|
mylog("%s: entering...\n", func);
|
|
|
|
/* thought we could fake Access out by just returning SQL_SUCCESS */
|
|
/* all the time, but it tries to set a huge value for SQL_MAX_LENGTH */
|
|
/* and expects the driver to reduce it to the real value */
|
|
|
|
if( ! stmt) {
|
|
SC_log_error(func, "", NULL);
|
|
return SQL_INVALID_HANDLE;
|
|
}
|
|
|
|
return set_statement_option(NULL, stmt, fOption, vParam);
|
|
}
|
|
|
|
|
|
/* - - - - - - - - - */
|
|
|
|
RETCODE SQL_API SQLGetStmtOption(
|
|
HSTMT hstmt,
|
|
UWORD fOption,
|
|
PTR pvParam)
|
|
{
|
|
static char *func="SQLGetStmtOption";
|
|
StatementClass *stmt = (StatementClass *) hstmt;
|
|
QResultClass *res;
|
|
|
|
mylog("%s: entering...\n", func);
|
|
|
|
/* thought we could fake Access out by just returning SQL_SUCCESS */
|
|
/* all the time, but it tries to set a huge value for SQL_MAX_LENGTH */
|
|
/* and expects the driver to reduce it to the real value */
|
|
|
|
if( ! stmt) {
|
|
SC_log_error(func, "", NULL);
|
|
return SQL_INVALID_HANDLE;
|
|
}
|
|
|
|
switch(fOption) {
|
|
case SQL_GET_BOOKMARK:
|
|
case SQL_ROW_NUMBER:
|
|
|
|
res = stmt->result;
|
|
|
|
if ( stmt->manual_result || ! globals.use_declarefetch) {
|
|
/* make sure we're positioned on a valid row */
|
|
if((stmt->currTuple < 0) ||
|
|
(stmt->currTuple >= QR_get_num_tuples(res))) {
|
|
stmt->errormsg = "Not positioned on a valid row.";
|
|
stmt->errornumber = STMT_INVALID_CURSOR_STATE_ERROR;
|
|
SC_log_error(func, "", stmt);
|
|
return SQL_ERROR;
|
|
}
|
|
}
|
|
else {
|
|
if (stmt->currTuple == -1 || ! res || ! res->tupleField) {
|
|
stmt->errormsg = "Not positioned on a valid row.";
|
|
stmt->errornumber = STMT_INVALID_CURSOR_STATE_ERROR;
|
|
SC_log_error(func, "", stmt);
|
|
return SQL_ERROR;
|
|
}
|
|
}
|
|
|
|
if (fOption == SQL_GET_BOOKMARK && stmt->options.use_bookmarks == SQL_UB_OFF) {
|
|
stmt->errormsg = "Operation invalid because use bookmarks not enabled.";
|
|
stmt->errornumber = STMT_OPERATION_INVALID;
|
|
SC_log_error(func, "", stmt);
|
|
return SQL_ERROR;
|
|
}
|
|
|
|
*((UDWORD *) pvParam) = SC_get_bookmark(stmt);
|
|
|
|
break;
|
|
|
|
case SQL_ASYNC_ENABLE: /* NOT SUPPORTED */
|
|
*((SDWORD *) pvParam) = SQL_ASYNC_ENABLE_OFF;
|
|
break;
|
|
|
|
case SQL_BIND_TYPE:
|
|
*((SDWORD *) pvParam) = stmt->options.bind_size;
|
|
break;
|
|
|
|
case SQL_CONCURRENCY: /* NOT REALLY SUPPORTED */
|
|
mylog("GetStmtOption(): SQL_CONCURRENCY\n");
|
|
*((SDWORD *)pvParam) = stmt->options.scroll_concurrency;
|
|
break;
|
|
|
|
case SQL_CURSOR_TYPE: /* PARTIAL SUPPORT */
|
|
mylog("GetStmtOption(): SQL_CURSOR_TYPE\n");
|
|
*((SDWORD *)pvParam) = stmt->options.cursor_type;
|
|
break;
|
|
|
|
case SQL_KEYSET_SIZE: /* NOT SUPPORTED, but saved */
|
|
mylog("GetStmtOption(): SQL_KEYSET_SIZE\n");
|
|
*((SDWORD *)pvParam) = stmt->options.keyset_size;
|
|
break;
|
|
|
|
case SQL_MAX_LENGTH: /* NOT SUPPORTED, but saved */
|
|
*((SDWORD *)pvParam) = stmt->options.maxLength;
|
|
break;
|
|
|
|
case SQL_MAX_ROWS: /* NOT SUPPORTED, but saved */
|
|
*((SDWORD *)pvParam) = stmt->options.maxRows;
|
|
mylog("GetSmtOption: MAX_ROWS, returning %d\n", stmt->options.maxRows);
|
|
break;
|
|
|
|
case SQL_NOSCAN:/* NOT SUPPORTED */
|
|
*((SDWORD *) pvParam) = SQL_NOSCAN_ON;
|
|
break;
|
|
|
|
case SQL_QUERY_TIMEOUT: /* NOT SUPPORTED */
|
|
*((SDWORD *) pvParam) = 0;
|
|
break;
|
|
|
|
case SQL_RETRIEVE_DATA: /* NOT SUPPORTED, but saved */
|
|
*((SDWORD *) pvParam) = stmt->options.retrieve_data;
|
|
break;
|
|
|
|
case SQL_ROWSET_SIZE:
|
|
*((SDWORD *) pvParam) = stmt->options.rowset_size;
|
|
break;
|
|
|
|
case SQL_SIMULATE_CURSOR:/* NOT SUPPORTED */
|
|
*((SDWORD *) pvParam) = SQL_SC_NON_UNIQUE;
|
|
break;
|
|
|
|
case SQL_USE_BOOKMARKS:
|
|
*((SDWORD *) pvParam) = stmt->options.use_bookmarks;
|
|
break;
|
|
|
|
default:
|
|
{
|
|
char option[64];
|
|
stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
|
|
stmt->errormsg = "Unknown statement option (Get)";
|
|
sprintf(option, "fOption=%d", fOption);
|
|
SC_log_error(func, option, stmt);
|
|
return SQL_ERROR;
|
|
}
|
|
}
|
|
|
|
return SQL_SUCCESS;
|
|
}
|
|
|
|
/* - - - - - - - - - */
|