driver/options.cc (738 lines of code) (raw):

// Copyright (c) 2000, 2024, Oracle and/or its affiliates. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License, version 2.0, as // published by the Free Software Foundation. // // This program is designed to work with certain software (including // but not limited to OpenSSL) that is licensed under separate terms, as // designated in a particular file or component or in included license // documentation. The authors of MySQL hereby grant you an additional // permission to link the program and your derivative works with the // separately licensed software that they have either included with // the program or referenced in the documentation. // // Without limiting anything contained in the foregoing, this file, // which is part of Connector/ODBC, is also subject to the // Universal FOSS Exception, version 1.0, a copy of which can be found at // https://oss.oracle.com/licenses/universal-foss-exception. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // See the GNU General Public License, version 2.0, for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA /** @file options.c @brief Functions for handling handle attributes and options. */ #include "driver.h" #include "errmsg.h" /* @type : myodbc3 internal @purpose : sets the common connection/stmt attributes */ static SQLRETURN set_constmt_attr(SQLSMALLINT HandleType, SQLHANDLE Handle, STMT_OPTIONS *options, SQLINTEGER Attribute, SQLPOINTER ValuePtr) { switch (Attribute) { case SQL_ATTR_ASYNC_ENABLE: if (ValuePtr == (SQLPOINTER) SQL_ASYNC_ENABLE_ON) return set_handle_error(HandleType,Handle,MYERR_01S02, "Doesn't support asynchronous, changed to default",0); break; case SQL_ATTR_CURSOR_SENSITIVITY: if (ValuePtr != (SQLPOINTER) SQL_UNSPECIFIED) { return set_handle_error(HandleType,Handle,MYERR_01S02, "Option value changed to default cursor sensitivity(unspecified)",0); } break; case SQL_ATTR_CURSOR_TYPE: if (((STMT *)Handle)->dbc->ds.opt_FORWARD_CURSOR) { options->cursor_type= SQL_CURSOR_FORWARD_ONLY; if (ValuePtr != (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY) return set_handle_error(HandleType,Handle,MYERR_01S02, "Forcing the use of forward-only cursor)",0); } else if (((STMT *)Handle)->dbc->ds.opt_DYNAMIC_CURSOR) { if (ValuePtr != (SQLPOINTER)SQL_CURSOR_KEYSET_DRIVEN) options->cursor_type= (SQLUINTEGER)(SQLULEN)ValuePtr; else { options->cursor_type= SQL_CURSOR_STATIC; return set_handle_error(HandleType,Handle,MYERR_01S02, "Option value changed to default static cursor",0); } } else { if (ValuePtr == (SQLPOINTER)SQL_CURSOR_FORWARD_ONLY || ValuePtr == (SQLPOINTER)SQL_CURSOR_STATIC) options->cursor_type= (SQLUINTEGER)(SQLULEN)ValuePtr; else { options->cursor_type= SQL_CURSOR_STATIC; return set_handle_error(HandleType,Handle,MYERR_01S02, "Option value changed to default static cursor",0); } } break; case SQL_ATTR_MAX_LENGTH: options->max_length= (SQLULEN) ValuePtr; break; case SQL_ATTR_MAX_ROWS: options->max_rows= (SQLULEN) ValuePtr; break; case SQL_ATTR_METADATA_ID: if (ValuePtr == (SQLPOINTER) SQL_TRUE) return set_handle_error(HandleType,Handle,MYERR_01S02, "Doesn't support SQL_ATTR_METADATA_ID to true, changed to default",0); break; case SQL_ATTR_RETRIEVE_DATA: options->retrieve_data = (ValuePtr != (SQLPOINTER)SQL_RD_OFF); break; case SQL_ATTR_SIMULATE_CURSOR: if (ValuePtr != (SQLPOINTER) SQL_SC_TRY_UNIQUE) return set_handle_error(HandleType,Handle,MYERR_01S02, "Option value changed to default cursor simulation",0); break; case 1226:/* MS SQL Server Extension */ case 1227: case 1228: break; case SQL_ATTR_USE_BOOKMARKS: if (ValuePtr == (SQLPOINTER) SQL_UB_VARIABLE || ValuePtr == (SQLPOINTER) SQL_UB_ON) options->bookmarks= (SQLUINTEGER) SQL_UB_VARIABLE; else options->bookmarks= (SQLUINTEGER) SQL_UB_OFF; break; case SQL_ATTR_FETCH_BOOKMARK_PTR: options->bookmark_ptr = ValuePtr; break; case SQL_ATTR_QUERY_TIMEOUT: /* Do something only if the handle is STMT */ if (HandleType == SQL_HANDLE_STMT) { return set_query_timeout((STMT*)Handle, (SQLULEN)ValuePtr); } break; case SQL_ATTR_KEYSET_SIZE: case SQL_ATTR_CONCURRENCY: case SQL_ATTR_NOSCAN: default: /* ignored */ break; } return SQL_SUCCESS; } /* @type : myodbc3 internal @purpose : returns the common connection/stmt attributes */ static SQLRETURN get_constmt_attr(SQLSMALLINT HandleType, SQLHANDLE Handle, STMT_OPTIONS *options, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER *StringLengthPtr __attribute__((unused))) { switch (Attribute) { case SQL_ATTR_ASYNC_ENABLE: *((SQLUINTEGER *) ValuePtr)= SQL_ASYNC_ENABLE_OFF; break; case SQL_ATTR_CURSOR_SENSITIVITY: *((SQLUINTEGER *) ValuePtr)= SQL_UNSPECIFIED; break; case SQL_ATTR_CURSOR_TYPE: *((SQLUINTEGER *) ValuePtr)= options->cursor_type; break; case SQL_ATTR_MAX_LENGTH: *((SQLULEN *) ValuePtr)= options->max_length; break; case SQL_ATTR_MAX_ROWS: *((SQLULEN *) ValuePtr)= options->max_rows; break; case SQL_ATTR_METADATA_ID: *((SQLUINTEGER *) ValuePtr)= SQL_FALSE; break; case SQL_ATTR_QUERY_TIMEOUT: /* Do something only if the handle is STMT */ if (HandleType == SQL_HANDLE_STMT) { /* Check if the query timeout was requested before */ if (options->query_timeout == (SQLULEN)-1) { options->query_timeout= get_query_timeout((STMT*)Handle); } *((SQLULEN *) ValuePtr)= options->query_timeout; } break; case SQL_ATTR_RETRIEVE_DATA: *((SQLULEN *) ValuePtr)= (options->retrieve_data ? SQL_RD_ON : SQL_RD_OFF); break; case SQL_ATTR_SIMULATE_CURSOR: *((SQLUINTEGER *) ValuePtr)= SQL_SC_TRY_UNIQUE; break; case SQL_ATTR_CONCURRENCY: *((SQLUINTEGER *) ValuePtr)= SQL_CONCUR_READ_ONLY; break; case SQL_KEYSET_SIZE: *((SQLUINTEGER *) ValuePtr)= 0L; break; case SQL_NOSCAN: *((SQLUINTEGER *) ValuePtr)= SQL_NOSCAN_ON; break; case SQL_ATTR_USE_BOOKMARKS: *((SQLUINTEGER *) ValuePtr) = options->bookmarks; break; case SQL_ATTR_FETCH_BOOKMARK_PTR: *((void **) ValuePtr) = options->bookmark_ptr; *StringLengthPtr= sizeof(SQLPOINTER); case 1226:/* MS SQL Server Extension */ case 1227: case 1228: default: /* ignored */ break; } return SQL_SUCCESS; } /* @type : myodbc3 internal @purpose : sets the connection attributes */ SQLRETURN SQL_API MySQLSetConnectAttr(SQLHDBC hdbc, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLengthPtr) { DBC *dbc= (DBC *) hdbc; /* In fact it should be awaken when DM checks whether connection is alive before taking it from pool. Keeping the check here to stay on the safe side */ WAKEUP_CONN_IF_NEEDED(dbc); switch (Attribute) { case SQL_ATTR_ACCESS_MODE: break; case SQL_ATTR_AUTOCOMMIT: if (ValuePtr != (SQLPOINTER) SQL_AUTOCOMMIT_ON) { if (!is_connected(dbc)) { dbc->commit_flag= CHECK_AUTOCOMMIT_OFF; return SQL_SUCCESS; } if (!(trans_supported(dbc)) || dbc->ds.opt_NO_TRANSACTIONS) return ((DBC*)hdbc)->set_error(MYERR_S1C00, "Transactions are not enabled", 4000); if (autocommit_on(dbc)) return dbc->execute_query("SET AUTOCOMMIT=0", SQL_NTS, TRUE); } else if (!is_connected(dbc)) { dbc->commit_flag= CHECK_AUTOCOMMIT_ON; return SQL_SUCCESS; } else if (trans_supported(dbc) && !(autocommit_on(dbc))) return dbc->execute_query("SET AUTOCOMMIT=1", SQL_NTS, TRUE); break; case SQL_ATTR_LOGIN_TIMEOUT: { /* we can't change timeout values in post connect state */ if (is_connected(dbc)) { return dbc->set_error(MYERR_S1011, NULL, 0); } else { dbc->login_timeout= (SQLUINTEGER)(SQLULEN)ValuePtr; return SQL_SUCCESS; } } break; case SQL_ATTR_CONNECTION_TIMEOUT: { /* We don't do anything with this, but we pretend that we did to be consistent with Microsoft SQL Server. */ return SQL_SUCCESS; } break; /* If this is done before connect (I would say a function sequence but .NET IDE does this) then we store the value but it is quite likely that it will get replaced by DATABASE in a DSN or connect string. */ case SQL_ATTR_CURRENT_CATALOG: { char ldb[NAME_LEN+1], *db; size_t cat_len= StringLengthPtr == SQL_NTS ? strlen((char *)ValuePtr) : StringLengthPtr; LOCK_DBC(dbc); if (cat_len > NAME_LEN) { return dbc->set_error(MYERR_01004, "Invalid string or buffer length", 0); } if (!(db= fix_str((char *)ldb, (char *)ValuePtr, StringLengthPtr))) return dbc->set_error(MYERR_S1009,NULL, 0); if (is_connected(dbc)) { if (mysql_select_db(dbc->mysql,(char*) db)) { dbc->set_error(MYERR_S1000, mysql_error(dbc->mysql), mysql_errno(dbc->mysql)); return SQL_ERROR; } } dbc->database = db ? db : ""; } break; case SQL_ATTR_ODBC_CURSORS: if (dbc->ds.opt_FORWARD_CURSOR && ValuePtr != (SQLPOINTER) SQL_CUR_USE_ODBC) return ((DBC*)hdbc)->set_error(MYERR_01S02, "Forcing the Driver Manager to use ODBC cursor library",0); break; case SQL_OPT_TRACE: case SQL_OPT_TRACEFILE: case SQL_QUIET_MODE: case SQL_TRANSLATE_DLL: case SQL_TRANSLATE_OPTION: { char buff[100]; myodbc_snprintf(buff, sizeof(buff), "Suppose to set this attribute '%d' through driver " "manager, not by the driver", (int)Attribute); return ((DBC*)hdbc)->set_error(MYERR_01S02, buff, 0); } case SQL_ATTR_PACKET_SIZE: break; case SQL_ATTR_TXN_ISOLATION: if (!is_connected(dbc)) /* no connection yet */ { dbc->txn_isolation= (SQLINTEGER)(SQLLEN)ValuePtr; return SQL_SUCCESS; } if (trans_supported(dbc)) { char buff[80]; const char *level= NULL; if ((SQLLEN)ValuePtr == SQL_TXN_SERIALIZABLE) level="SERIALIZABLE"; else if ((SQLLEN)ValuePtr == SQL_TXN_REPEATABLE_READ) level="REPEATABLE READ"; else if ((SQLLEN)ValuePtr == SQL_TXN_READ_COMMITTED) level="READ COMMITTED"; else if ((SQLLEN)ValuePtr == SQL_TXN_READ_UNCOMMITTED) level="READ UNCOMMITTED"; if (level) { SQLRETURN rc; sprintf(buff,"SET SESSION TRANSACTION ISOLATION LEVEL %s", level); if (SQL_SUCCEEDED(rc = dbc->execute_query(buff, SQL_NTS, TRUE))) { dbc->txn_isolation = (int)((size_t)ValuePtr); } return rc; } else { return dbc->set_error("HY024", "Invalid attribute value", 0); } } break; #ifndef USE_IODBC case SQL_ATTR_RESET_CONNECTION: if (ValuePtr != (SQLPOINTER)SQL_RESET_CONNECTION_YES) { return dbc->set_error( "HY024", "Invalid attribute value", 0); } /* TODO 3.8 feature */ reset_connection(dbc); dbc->need_to_wakeup= 1; return SQL_SUCCESS; #endif case CB_FIDO_CONNECTION: dbc->fido_callback = (fido_callback_func)ValuePtr; break; case CB_FIDO_GLOBAL: { std::unique_lock<std::mutex> fido_lock(global_fido_mutex); global_fido_callback = (fido_callback_func)ValuePtr; break; } case SQL_ATTR_ENLIST_IN_DTC: return dbc->set_error( "HYC00", "Optional feature not supported", 0); /* 3.x driver doesn't support any statement attributes at connection level, but to make sure all 2.x apps works fine...lets support it..nothing to lose.. */ default: return set_constmt_attr(2, dbc, &dbc->stmt_options, Attribute, ValuePtr); } return SQL_SUCCESS; } /** Retrieve connection attribute values. @param[in] hdbc @param[in] attrib @param[out] char_attr @param[out] num_attr */ SQLRETURN SQL_API MySQLGetConnectAttr(SQLHDBC hdbc, SQLINTEGER attrib, SQLCHAR **char_attr, SQLPOINTER num_attr) { DBC *dbc= (DBC *)hdbc; SQLRETURN result= SQL_SUCCESS; /* (Windows) DM checks SQL_ATTR_CONNECTION_DEAD before taking it from the pool and returning to the application. We can use wake-up procedure for diagnostics of whether connection is alive instead of mysql_ping(). But we are leaving this check for other attributes, too */ if (attrib != SQL_ATTR_CONNECTION_DEAD) { WAKEUP_CONN_IF_NEEDED(dbc); } switch (attrib) { case SQL_ATTR_ACCESS_MODE: *((SQLUINTEGER *)num_attr)= SQL_MODE_READ_WRITE; break; case SQL_ATTR_AUTO_IPD: *((SQLUINTEGER *)num_attr)= SQL_FALSE; break; case SQL_ATTR_AUTOCOMMIT: *((SQLUINTEGER *)num_attr)= (autocommit_on(dbc) || (!(trans_supported(dbc)) ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF)); break; case SQL_ATTR_CONNECTION_DEAD: /* If waking up fails - we return "connection is dead", no matter what really the reason is */ if (dbc->need_to_wakeup != 0 && wakeup_connection(dbc) || dbc->need_to_wakeup == 0 && mysql_ping(dbc->mysql) && is_connection_lost(mysql_errno(dbc->mysql))) *((SQLUINTEGER *)num_attr)= SQL_CD_TRUE; else *((SQLUINTEGER *)num_attr)= SQL_CD_FALSE; break; case SQL_ATTR_CONNECTION_TIMEOUT: /* We don't support this option, so it is always 0. */ *((SQLUINTEGER *)num_attr)= 0; break; case SQL_ATTR_CURRENT_CATALOG: if (is_connected(dbc) && reget_current_catalog(dbc)) { return set_handle_error(SQL_HANDLE_DBC, hdbc, MYERR_S1000, "Unable to get current catalog", 0); } else if (is_connected(dbc)) { *char_attr= (SQLCHAR *)(!dbc->database.empty() ? dbc->database.c_str() : "null"); } else { return set_handle_error(SQL_HANDLE_DBC, hdbc, MYERR_S1C00, "Getting catalog name is not supported "\ "before connection is established", 0); } break; case SQL_ATTR_LOGIN_TIMEOUT: *((SQLUINTEGER *)num_attr)= dbc->login_timeout; break; case SQL_ATTR_ODBC_CURSORS: if (dbc->ds.opt_FORWARD_CURSOR) *((SQLUINTEGER *)num_attr)= SQL_CUR_USE_ODBC; else *((SQLUINTEGER *)num_attr)= SQL_CUR_USE_IF_NEEDED; break; case SQL_ATTR_PACKET_SIZE: *((SQLUINTEGER *)num_attr)= dbc->mysql->net.max_packet; break; case SQL_ATTR_TXN_ISOLATION: /* If we don't know the isolation level already, we need to ask the server. */ if (!dbc->txn_isolation) { /* Unless we're not connected yet, then we just assume it will be REPEATABLE READ, which is the server default. */ if (!is_connected(dbc)) { *((SQLINTEGER *)num_attr)= SQL_TRANSACTION_REPEATABLE_READ; break; } if (is_minimum_version(dbc->mysql->server_version, "8.0")) result = dbc->execute_query("SELECT @@transaction_isolation", SQL_NTS, true); else result = dbc->execute_query("SELECT @@tx_isolation", SQL_NTS, true); if (result != SQL_SUCCESS) { return set_handle_error(SQL_HANDLE_DBC, hdbc, MYERR_S1000, "Failed to get isolation level", 0); } else { MYSQL_RES *res; MYSQL_ROW row; if ((res= mysql_store_result(dbc->mysql)) && (row= mysql_fetch_row(res))) { if (strncmp(row[0], "READ-UNCOMMITTED", 16) == 0) { dbc->txn_isolation= SQL_TRANSACTION_READ_UNCOMMITTED; } else if (strncmp(row[0], "READ-COMMITTED", 14) == 0) { dbc->txn_isolation= SQL_TRANSACTION_READ_COMMITTED; } else if (strncmp(row[0], "REPEATABLE-READ", 15) == 0) { dbc->txn_isolation= SQL_TRANSACTION_REPEATABLE_READ; } else if (strncmp(row[0], "SERIALIZABLE", 12) == 0) { dbc->txn_isolation= SQL_TRANSACTION_SERIALIZABLE; } } mysql_free_result(res); } } *((SQLINTEGER *)num_attr)= dbc->txn_isolation; break; default: return set_handle_error(SQL_HANDLE_DBC, hdbc, MYERR_S1092, NULL, 0); } return result; } /* @type : myodbc3 internal @purpose : sets the statement attributes */ SQLRETURN SQL_API MySQLSetStmtAttr(SQLHSTMT hstmt, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLengthPtr __attribute__((unused))) { STMT *stmt= (STMT *)hstmt; SQLRETURN result= SQL_SUCCESS; STMT_OPTIONS *options= &stmt->stmt_options; CLEAR_STMT_ERROR(stmt); switch (Attribute) { case SQL_ATTR_CURSOR_SCROLLABLE: if (ValuePtr == (SQLPOINTER)SQL_NONSCROLLABLE && options->cursor_type != SQL_CURSOR_FORWARD_ONLY) options->cursor_type= SQL_CURSOR_FORWARD_ONLY; else if (ValuePtr == (SQLPOINTER)SQL_SCROLLABLE && options->cursor_type == SQL_CURSOR_FORWARD_ONLY) options->cursor_type= SQL_CURSOR_STATIC; break; case SQL_ATTR_APP_PARAM_DESC: case SQL_ATTR_APP_ROW_DESC: { DESC *desc= (DESC *) ValuePtr; DESC **dest= NULL; desc_desc_type desc_type; if (Attribute == SQL_ATTR_APP_PARAM_DESC) { dest= &stmt->apd; desc_type= DESC_PARAM; } else if (Attribute == SQL_ATTR_APP_ROW_DESC) { dest= &stmt->ard; desc_type= DESC_ROW; } (*dest)->stmt_list_remove(stmt); /* reset to implicit if null */ if (desc == SQL_NULL_HANDLE) { if (Attribute == SQL_ATTR_APP_PARAM_DESC) stmt->apd= stmt->imp_apd; else if (Attribute == SQL_ATTR_APP_ROW_DESC) stmt->ard= stmt->imp_ard; break; } if (desc->alloc_type == SQL_DESC_ALLOC_AUTO && desc->stmt != stmt) return ((STMT*)hstmt)->set_error(MYERR_S1017, "Invalid use of an automatically allocated " "descriptor handle",0); if (desc->alloc_type == SQL_DESC_ALLOC_USER && stmt->dbc != desc->dbc) return ((STMT*)hstmt)->set_error(MYERR_S1024, "Invalid attribute value",0); if (desc->desc_type != DESC_UNKNOWN && desc->desc_type != desc_type) { return ((STMT*)hstmt)->set_error(MYERR_S1024, "Descriptor type mismatch",0); } assert(desc); assert(dest); desc->stmt_list_add(stmt); desc->desc_type= desc_type; *dest= desc; } break; case SQL_ATTR_AUTO_IPD: case SQL_ATTR_ENABLE_AUTO_IPD: if (ValuePtr != (SQLPOINTER)SQL_FALSE) return ((STMT*)hstmt)->set_error(MYERR_S1C00, "Optional feature not implemented",0); break; case SQL_ATTR_IMP_PARAM_DESC: case SQL_ATTR_IMP_ROW_DESC: return ((STMT*)hstmt)->set_error(MYERR_S1024, "Invalid attribute/option identifier",0); case SQL_ATTR_PARAM_BIND_OFFSET_PTR: return stmt_SQLSetDescField(stmt, stmt->apd, 0, SQL_DESC_BIND_OFFSET_PTR, ValuePtr, SQL_IS_POINTER); case SQL_ATTR_PARAM_BIND_TYPE: return stmt_SQLSetDescField(stmt, stmt->apd, 0, SQL_DESC_BIND_TYPE, ValuePtr, SQL_IS_INTEGER); case SQL_ATTR_PARAM_OPERATION_PTR: /* need to support this ....*/ return stmt_SQLSetDescField(stmt, stmt->apd, 0, SQL_DESC_ARRAY_STATUS_PTR, ValuePtr, SQL_IS_POINTER); case SQL_ATTR_PARAM_STATUS_PTR: /* need to support this ....*/ return stmt_SQLSetDescField(stmt, stmt->ipd, 0, SQL_DESC_ARRAY_STATUS_PTR, ValuePtr, SQL_IS_POINTER); case SQL_ATTR_PARAMS_PROCESSED_PTR: /* need to support this ....*/ return stmt_SQLSetDescField(stmt, stmt->ipd, 0, SQL_DESC_ROWS_PROCESSED_PTR, ValuePtr, SQL_IS_POINTER); case SQL_ATTR_PARAMSET_SIZE: return stmt_SQLSetDescField(stmt, stmt->apd, 0, SQL_DESC_ARRAY_SIZE, ValuePtr, SQL_IS_ULEN); case SQL_ATTR_ROW_ARRAY_SIZE: case SQL_ROWSET_SIZE: return stmt_SQLSetDescField(stmt, stmt->ard, 0, SQL_DESC_ARRAY_SIZE, ValuePtr, SQL_IS_ULEN); case SQL_ATTR_ROW_BIND_OFFSET_PTR: return stmt_SQLSetDescField(stmt, stmt->ard, 0, SQL_DESC_BIND_OFFSET_PTR, ValuePtr, SQL_IS_POINTER); case SQL_ATTR_ROW_BIND_TYPE: return stmt_SQLSetDescField(stmt, stmt->ard, 0, SQL_DESC_BIND_TYPE, ValuePtr, SQL_IS_INTEGER); case SQL_ATTR_ROW_NUMBER: return ((STMT*)hstmt)->set_error(MYERR_S1000, "Trying to set read-only attribute",0); case SQL_ATTR_ROW_OPERATION_PTR: return stmt_SQLSetDescField(stmt, stmt->ard, 0, SQL_DESC_ARRAY_STATUS_PTR, ValuePtr, SQL_IS_POINTER); case SQL_ATTR_ROW_STATUS_PTR: return stmt_SQLSetDescField(stmt, stmt->ird, 0, SQL_DESC_ARRAY_STATUS_PTR, ValuePtr, SQL_IS_POINTER); case SQL_ATTR_ROWS_FETCHED_PTR: return stmt_SQLSetDescField(stmt, stmt->ird, 0, SQL_DESC_ROWS_PROCESSED_PTR, ValuePtr, SQL_IS_POINTER); case SQL_ATTR_SIMULATE_CURSOR: options->simulateCursor= (SQLUINTEGER)(SQLULEN)ValuePtr; break; /* 3.x driver doesn't support any statement attributes at connection level, but to make sure all 2.x apps works fine...lets support it..nothing to lose.. */ default: result= set_constmt_attr(3,hstmt,options, Attribute,ValuePtr); } return result; } /* @type : myodbc3 internal @purpose : returns the statement attribute values */ SQLRETURN SQL_API MySQLGetStmtAttr(SQLHSTMT hstmt, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER BufferLength __attribute__((unused)), SQLINTEGER *StringLengthPtr) { SQLRETURN result= SQL_SUCCESS; STMT *stmt= (STMT *) hstmt; STMT_OPTIONS *options= &stmt->stmt_options; SQLINTEGER vparam= 0; SQLINTEGER len; if (!ValuePtr) ValuePtr= &vparam; if (!StringLengthPtr) StringLengthPtr= &len; switch (Attribute) { case SQL_ATTR_CURSOR_SCROLLABLE: if (options->cursor_type == SQL_CURSOR_FORWARD_ONLY) *(SQLUINTEGER*)ValuePtr= SQL_NONSCROLLABLE; else *(SQLUINTEGER*)ValuePtr= SQL_SCROLLABLE; break; case SQL_ATTR_AUTO_IPD: *(SQLUINTEGER *)ValuePtr= SQL_FALSE; break; case SQL_ATTR_PARAM_BIND_OFFSET_PTR: *(SQLPOINTER *)ValuePtr= stmt->apd->bind_offset_ptr; break; case SQL_ATTR_PARAM_BIND_TYPE: *(SQLINTEGER *)ValuePtr= stmt->apd->bind_type; break; case SQL_ATTR_PARAM_OPERATION_PTR: /* need to support this ....*/ *(SQLPOINTER *)ValuePtr= stmt->apd->array_status_ptr; break; case SQL_ATTR_PARAM_STATUS_PTR: /* need to support this ....*/ *(SQLPOINTER *)ValuePtr= stmt->ipd->array_status_ptr; break; case SQL_ATTR_PARAMS_PROCESSED_PTR: /* need to support this ....*/ *(SQLPOINTER *)ValuePtr= stmt->ipd->rows_processed_ptr; break; case SQL_ATTR_PARAMSET_SIZE: *(SQLUINTEGER *)ValuePtr = (SQLUINTEGER)stmt->apd->array_size; break; case SQL_ATTR_ROW_ARRAY_SIZE: case SQL_ROWSET_SIZE: *(SQLUINTEGER *)ValuePtr = (SQLUINTEGER)stmt->ard->array_size; break; case SQL_ATTR_ROW_BIND_OFFSET_PTR: *((SQLPOINTER *) ValuePtr)= stmt->ard->bind_offset_ptr; break; case SQL_ATTR_ROW_BIND_TYPE: *((SQLINTEGER *) ValuePtr)= stmt->ard->bind_type; break; case SQL_ATTR_ROW_NUMBER: *(SQLUINTEGER *)ValuePtr= stmt->current_row+1; break; case SQL_ATTR_ROW_OPERATION_PTR: /* need to support this ....*/ *(SQLPOINTER *)ValuePtr= stmt->ard->array_status_ptr; break; case SQL_ATTR_ROW_STATUS_PTR: *(SQLPOINTER *)ValuePtr= stmt->ird->array_status_ptr; break; case SQL_ATTR_ROWS_FETCHED_PTR: *(SQLPOINTER *)ValuePtr= stmt->ird->rows_processed_ptr; break; case SQL_ATTR_SIMULATE_CURSOR: *(SQLUINTEGER *)ValuePtr= options->simulateCursor; break; case SQL_ATTR_APP_ROW_DESC: *(SQLPOINTER *)ValuePtr= stmt->ard; *StringLengthPtr= sizeof(SQLPOINTER); break; case SQL_ATTR_IMP_ROW_DESC: *(SQLPOINTER *)ValuePtr= stmt->ird; *StringLengthPtr= sizeof(SQLPOINTER); break; case SQL_ATTR_APP_PARAM_DESC: *(SQLPOINTER *)ValuePtr= stmt->apd; *StringLengthPtr= sizeof(SQLPOINTER); break; case SQL_ATTR_IMP_PARAM_DESC: *(SQLPOINTER *)ValuePtr= stmt->ipd; *StringLengthPtr= sizeof(SQLPOINTER); break; /* 3.x driver doesn't support any statement attributes at connection level, but to make sure all 2.x apps works fine...lets support it..nothing to lose.. */ default: result= get_constmt_attr(3,hstmt,options, Attribute,ValuePtr, StringLengthPtr); } return result; } /* @type : ODBC 3.0 API @purpose : sets the environment attributes */ SQLRETURN SQL_API SQLSetEnvAttr(SQLHENV henv, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER StringLength __attribute__((unused))) { CHECK_HANDLE(henv); if (((ENV *)henv)->has_connections()) return set_env_error((ENV*)henv, MYERR_S1010, NULL, 0); switch (Attribute) { case SQL_ATTR_ODBC_VERSION: { switch((SQLINTEGER)(SQLLEN)ValuePtr) { case SQL_OV_ODBC2: case SQL_OV_ODBC3: #ifndef USE_IODBC case SQL_OV_ODBC3_80: #endif ((ENV *)henv)->odbc_ver= (SQLINTEGER)(SQLLEN)ValuePtr; break; default: return set_env_error((ENV*)henv,MYERR_S1024,NULL,0); } break; } case SQL_ATTR_OUTPUT_NTS: if (ValuePtr == (SQLPOINTER)SQL_TRUE) break; default: return set_env_error((ENV*)henv,MYERR_S1C00,NULL,0); } return SQL_SUCCESS; } /* @type : ODBC 3.0 API @purpose : returns the environment attributes */ SQLRETURN SQL_API SQLGetEnvAttr(SQLHENV henv, SQLINTEGER Attribute, SQLPOINTER ValuePtr, SQLINTEGER BufferLength __attribute__((unused)), SQLINTEGER *StringLengthPtr __attribute__((unused))) { CHECK_HANDLE(henv); /* NULL is acceptable for ValuePtr, so we are not checking for it here */ switch ( Attribute ) { case SQL_ATTR_CONNECTION_POOLING: IF_NOT_NULL(ValuePtr, *(SQLINTEGER*)ValuePtr = SQL_CP_ONE_PER_DRIVER); break; case SQL_ATTR_ODBC_VERSION: IF_NOT_NULL(ValuePtr, *(SQLINTEGER*)ValuePtr= ((ENV *)henv)->odbc_ver); break; case SQL_ATTR_OUTPUT_NTS: IF_NOT_NULL(ValuePtr, *((SQLINTEGER*)ValuePtr)= SQL_TRUE); break; default: return set_env_error((ENV*)henv,MYERR_S1C00,NULL,0); } return SQL_SUCCESS; } #ifdef USE_IODBC // iODBC has problems mapping SQLGetStmtOption()/SQLSetStmtOption() to // SQLGetStmtAttr()/SQLSetStmtAttr() SQLRETURN SQL_API SQLGetStmtOption(SQLHSTMT hstmt,SQLUSMALLINT option, SQLPOINTER param) { LOCK_STMT(hstmt); return MySQLGetStmtAttr(hstmt, option, param, SQL_NTS, (SQLINTEGER *)NULL); } SQLRETURN SQL_API SQLSetStmtOption(SQLHSTMT hstmt, SQLUSMALLINT option, SQLULEN param) { LOCK_STMT(hstmt); return MySQLSetStmtAttr(hstmt, option, (SQLPOINTER)param, SQL_NTS); } #endif