/* +----------------------------------------------------------------------+ | Licensed Materials - Property of IBM | | | | (C) Copyright IBM Corporation 2006, 2007. | +----------------------------------------------------------------------+ | Authors: Sushant Koduru, Lynh Nguyen, Kanchana Padmanabhan, | | Dan Scott, Helmut Tessarek, Sam Ruby, Kellen Bombardier, | | Tony Cairns, Manas Dadarkar, Swetha Patel, Salvador Ledezma | +----------------------------------------------------------------------+ */ #define MODULE_RELEASE "0.9.5" #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "ruby_ibm_db.h" #include /* True global resources - no need for thread safety here */ static VALUE le_conn_struct, le_stmt_struct, le_pconn_struct, le_row_struct; static VALUE le_client_info, le_server_info; static struct _ibm_db_globals *ibm_db_globals; static void _ruby_ibm_db_check_sql_errors( SQLHANDLE handle, SQLSMALLINT hType, int rc, int cpy_to_global, char* ret_str, int API, SQLSMALLINT recno ); static void _ruby_ibm_db_assign_options( void* handle, int type, long opt_key, VALUE data ); static void _ruby_ibm_db_clear_conn_err_cache(); static void _ruby_ibm_db_clear_stmt_err_cache(); static char * _ruby_ibm_db_instance_name; static int is_systemi, is_informix; /* 1 == TRUE; 0 == FALSE; */ /* Defines a linked list structure for caching param data */ typedef struct _param_cache_node { SQLSMALLINT data_type; /* Datatype */ SQLUINTEGER param_size; /* param size */ SQLSMALLINT nullable; /* is Nullable */ SQLSMALLINT scale; /* Decimal scale */ SQLUINTEGER file_options; /* File options if PARAM_FILE */ SQLINTEGER bind_indicator; /* indicator variable for SQLBindParameter */ int param_num; /* param number in stmt */ int param_type; /* Type of param - INP/OUT/INP-OUT/FILE */ int size; /* Size of param */ char *varname; /* bound variable name */ long ivalue; /* Temp storage value */ double fvalue; /* Temp storage value */ char *svalue; /* Temp storage value */ struct _param_cache_node *next; /* Pointer to next node */ } param_node; typedef struct _conn_handle_struct { SQLHANDLE henv; SQLHANDLE hdbc; long auto_commit; long c_bin_mode; long c_case_mode; long c_cursor_type; int handle_active; SQLSMALLINT error_recno_tracker; SQLSMALLINT errormsg_recno_tracker; int flag_pconnect; /* Indicates that this connection is persistent */ } conn_handle; typedef union { SQLINTEGER i_val; SQLDOUBLE d_val; SQLFLOAT f_val; SQLSMALLINT s_val; SQLCHAR *str_val; } ibm_db_row_data_type; typedef struct { SQLINTEGER out_length; ibm_db_row_data_type data; } ibm_db_row_type; typedef struct _ibm_db_result_set_info_struct { SQLCHAR *name; SQLSMALLINT type; SQLUINTEGER size; SQLSMALLINT scale; SQLSMALLINT nullable; SQLINTEGER lob_loc; SQLINTEGER loc_ind; SQLSMALLINT loc_type; } ibm_db_result_set_info; typedef struct _row_hash_struct { VALUE hash; } row_hash_struct; typedef struct _stmt_handle_struct { SQLHANDLE hdbc; SQLHANDLE hstmt; long s_bin_mode; long cursor_type; long s_case_mode; SQLSMALLINT error_recno_tracker; SQLSMALLINT errormsg_recno_tracker; /* Parameter Caching variables */ param_node *head_cache_list; param_node *current_node; int num_params; /* Number of Params */ int file_param; /* if option passed in is FILE_PARAM */ int num_columns; ibm_db_result_set_info *column_info; ibm_db_row_type *row_data; } stmt_handle; void ruby_init_ibm_db(); /* equivalent functions on different platforms */ #ifdef _WIN32 #define STRCASECMP stricmp #else #define STRCASECMP strcasecmp #endif static VALUE mDB; static VALUE id_new; static VALUE id_keys; static VALUE id_id2name; #define RUBY_FE(function) \ rb_define_singleton_method(mDB, #function, ibm_db_##function, -1); #define RUBY_FALIAS(alias, function) \ rb_define_singleton_method(mDB, #alias, ibm_db_##function, -1); /* Every user visible function must have an entry in Init_ibm_db */ void Init_ibm_db(void) { mDB = rb_define_module("IBM_DB"); rb_define_module_function(mDB, "connect", ibm_db_connect, -1); rb_define_module_function(mDB, "commit", ibm_db_commit, -1); rb_define_module_function(mDB, "pconnect", ibm_db_pconnect, -1); rb_define_module_function(mDB, "autocommit", ibm_db_autocommit, -1); rb_define_module_function(mDB, "bind_param", ibm_db_bind_param, -1); rb_define_module_function(mDB, "close", ibm_db_close, -1); rb_define_module_function(mDB, "column_privileges", ibm_db_column_privileges, -1); rb_define_module_function(mDB, "columnprivileges", ibm_db_column_privileges, -1); rb_define_module_function(mDB, "columns", ibm_db_columns, -1); rb_define_module_function(mDB, "foreign_keys", ibm_db_foreign_keys, -1); rb_define_module_function(mDB, "foreignkeys", ibm_db_foreign_keys, -1); rb_define_module_function(mDB, "primary_keys", ibm_db_primary_keys, -1); rb_define_module_function(mDB, "primarykeys", ibm_db_primary_keys, -1); rb_define_module_function(mDB, "procedure_columns", ibm_db_procedure_columns, -1); rb_define_module_function(mDB, "procedurecolumns", ibm_db_procedure_columns, -1); rb_define_module_function(mDB, "procedures", ibm_db_procedures, -1); rb_define_module_function(mDB, "special_columns", ibm_db_special_columns, -1); rb_define_module_function(mDB, "specialcolumns", ibm_db_special_columns, -1); rb_define_module_function(mDB, "statistics", ibm_db_statistics, -1); rb_define_module_function(mDB, "table_privileges", ibm_db_table_privileges, -1); rb_define_module_function(mDB, "tableprivileges", ibm_db_table_privileges, -1); rb_define_module_function(mDB, "tables", ibm_db_tables, -1); rb_define_module_function(mDB, "exec", ibm_db_exec, -1); rb_define_module_function(mDB, "prepare", ibm_db_prepare, -1); rb_define_module_function(mDB, "execute", ibm_db_execute, -1); rb_define_module_function(mDB, "stmt_errormsg", ibm_db_stmt_errormsg, -1); rb_define_module_function(mDB, "conn_errormsg", ibm_db_conn_errormsg, -1); rb_define_module_function(mDB, "conn_error", ibm_db_conn_error, -1); rb_define_module_function(mDB, "stmt_error", ibm_db_stmt_error, -1); rb_define_module_function(mDB, "next_result", ibm_db_next_result, -1); rb_define_module_function(mDB, "num_fields", ibm_db_num_fields, -1); rb_define_module_function(mDB, "num_rows", ibm_db_num_rows, -1); rb_define_module_function(mDB, "field_name", ibm_db_field_name, -1); rb_define_module_function(mDB, "field_display_size", ibm_db_field_display_size, -1); rb_define_module_function(mDB, "field_num", ibm_db_field_num, -1); rb_define_module_function(mDB, "field_precision", ibm_db_field_precision, -1); rb_define_module_function(mDB, "field_scale", ibm_db_field_scale, -1); rb_define_module_function(mDB, "field_type", ibm_db_field_type, -1); rb_define_module_function(mDB, "field_width", ibm_db_field_width, -1); rb_define_module_function(mDB, "cursor_type", ibm_db_cursor_type, -1); rb_define_module_function(mDB, "rollback", ibm_db_rollback, -1); rb_define_module_function(mDB, "free_stmt", ibm_db_free_stmt, -1); rb_define_module_function(mDB, "result", ibm_db_result, -1); rb_define_module_function(mDB, "fetch_row", ibm_db_fetch_row, -1); rb_define_module_function(mDB, "fetch_assoc", ibm_db_fetch_assoc, -1); rb_define_module_function(mDB, "fetch_array", ibm_db_fetch_array, -1); rb_define_module_function(mDB, "fetch_both", ibm_db_fetch_both, -1); rb_define_module_function(mDB, "free_result", ibm_db_free_result, -1); rb_define_module_function(mDB, "set_option", ibm_db_set_option, -1); rb_define_module_function(mDB, "setoption", ibm_db_set_option, -1); rb_define_module_function(mDB, "get_option", ibm_db_get_option, -1); rb_define_module_function(mDB, "getoption", ibm_db_get_option, -1); rb_define_module_function(mDB, "get_last_serial_value", ibm_db_get_last_serial_value, -1); rb_define_module_function(mDB, "fetch_object", ibm_db_fetch_object, -1); rb_define_module_function(mDB, "server_info", ibm_db_server_info, -1); rb_define_module_function(mDB, "client_info", ibm_db_client_info, -1); rb_define_module_function(mDB, "active", ibm_db_active, -1); RUBY_FE(connect) RUBY_FE(commit) RUBY_FE(pconnect) RUBY_FE(autocommit) RUBY_FE(bind_param) RUBY_FE(close) RUBY_FE(column_privileges) RUBY_FALIAS(columnprivileges, column_privileges) RUBY_FE(columns) RUBY_FE(foreign_keys) RUBY_FALIAS(foreignkeys, foreign_keys) RUBY_FE(primary_keys) RUBY_FALIAS(primarykeys, primary_keys) RUBY_FE(procedure_columns) RUBY_FALIAS(procedurecolumns, procedure_columns) RUBY_FE(procedures) RUBY_FE(special_columns) RUBY_FALIAS(specialcolumns, special_columns) RUBY_FE(statistics) RUBY_FE(table_privileges) RUBY_FALIAS(tableprivileges, table_privileges) RUBY_FE(tables) RUBY_FE(exec) RUBY_FE(prepare) RUBY_FE(execute) RUBY_FE(stmt_errormsg) RUBY_FE(conn_errormsg) RUBY_FE(conn_error) RUBY_FE(stmt_error) RUBY_FE(next_result) RUBY_FE(num_fields) RUBY_FE(num_rows) RUBY_FE(field_name) RUBY_FE(field_display_size) RUBY_FE(field_num) RUBY_FE(field_precision) RUBY_FE(field_scale) RUBY_FE(field_type) RUBY_FE(field_width) RUBY_FE(cursor_type) RUBY_FE(rollback) RUBY_FE(free_stmt) RUBY_FE(result) RUBY_FE(fetch_row) RUBY_FE(fetch_assoc) RUBY_FE(fetch_array) RUBY_FE(fetch_both) RUBY_FE(free_result) RUBY_FE(set_option) RUBY_FALIAS(setoption, set_option) RUBY_FE(get_option) RUBY_FALIAS(getoption, get_option) RUBY_FE(get_last_serial_value) RUBY_FE(fetch_object) RUBY_FE(server_info) RUBY_FE(client_info) RUBY_FE(active) ruby_init_ibm_db(); id_keys = rb_intern("keys"); id_new = rb_intern("new"); id_id2name = rb_intern("id2name"); }; /* */ /* {{{ RUBY_INI */ #define INI_STR(name) NULL /* }}} */ static void ruby_ibm_db_init_globals(struct _ibm_db_globals *ibm_db_globals) { /* env handle */ ibm_db_globals->bin_mode = 1; memset(ibm_db_globals->__ruby_conn_err_msg, 0, DB2_MAX_ERR_MSG_LEN); memset(ibm_db_globals->__ruby_stmt_err_msg, 0, DB2_MAX_ERR_MSG_LEN); memset(ibm_db_globals->__ruby_conn_err_state, 0, SQL_SQLSTATE_SIZE + 1); memset(ibm_db_globals->__ruby_stmt_err_state, 0, SQL_SQLSTATE_SIZE + 1); } /* */ static VALUE persistent_list; char *estrdup(char *data) { int len = strlen(data); char *dup = ALLOC_N(char, len+1); strcpy(dup, data); return dup; } char *estrndup(char *data, int max) { int len = strlen(data); char *dup; if (len > max) len = max; dup = ALLOC_N(char, len+1); strcpy(dup, data); return dup; } char *strtolower(char *data, int max) { while (max--) data[max] = tolower(data[max]); return data; } char *strtoupper(char *data, int max) { while (max--) data[max] = toupper(data[max]); return data; } /* static void _ruby_ibm_db_mark_conn_struct */ static void _ruby_ibm_db_mark_conn_struct(conn_handle *handle) { } /* */ /* static void _ruby_ibm_db_free_conn_struct */ static void _ruby_ibm_db_free_conn_struct(conn_handle *handle) { int rc; /* Disconnect from DB. If stmt is allocated, it is freed automatically*/ if ( handle->handle_active ) { rc = SQLDisconnect((SQLHDBC)handle->hdbc); rc = SQLFreeHandle(SQL_HANDLE_DBC, handle->hdbc); rc = SQLFreeHandle(SQL_HANDLE_ENV, handle->henv); } if ( handle != NULL ) { if ( handle->flag_pconnect ) { ruby_xfree(handle); } else { ruby_xfree(handle); } } } /* */ /* static void _ruby_ibm_db_mark_pconn_struct */ static void _ruby_ibm_db_mark_pconn_struct(conn_handle *handle) { } /* */ /* static void _ruby_ibm_db_free_pconn_struct */ static void _ruby_ibm_db_free_pconn_struct(conn_handle *handle) { _ruby_ibm_db_free_conn_struct(handle); } /* */ /* static void _ruby_ibm_db_mark_row_struct */ static void _ruby_ibm_db_mark_row_struct(row_hash_struct *handle) { rb_gc_mark(handle->hash); } /* */ /* static void _ruby_ibm_db_free_row_struct */ static void _ruby_ibm_db_free_row_struct(row_hash_struct *handle) { ruby_xfree(handle); } /* */ /* static void _ruby_ibm_db_free_result_struct(stmt_handle* handle) */ static void _ruby_ibm_db_free_result_struct(stmt_handle* handle) { int i; param_node *curr_ptr = NULL, *prev_ptr = NULL; if ( handle != NULL ) { /* Free param cache list */ curr_ptr = handle->head_cache_list; prev_ptr = handle->head_cache_list; while (curr_ptr != NULL) { curr_ptr = curr_ptr->next; if (prev_ptr->varname) { ruby_xfree(prev_ptr->varname); } if (prev_ptr->svalue) ruby_xfree(prev_ptr->svalue); ruby_xfree(prev_ptr); prev_ptr = curr_ptr; } /* free row data cache */ if (handle->row_data) { for (i=0; inum_columns;i++) { switch (handle->column_info[i].type) { case SQL_CHAR: case SQL_VARCHAR: #ifndef PASE /* i5/OS SQL_LONGVARCHAR is SQL_VARCHAR */ case SQL_LONGVARCHAR: #endif /* PASE */ case SQL_TYPE_DATE: case SQL_TYPE_TIME: case SQL_TYPE_TIMESTAMP: case SQL_BIGINT: case SQL_DECIMAL: case SQL_NUMERIC: case SQL_XML: if ( handle->row_data[i].data.str_val != NULL ) { ruby_xfree(handle->row_data[i].data.str_val); handle->row_data[i].data.str_val = NULL; } } } ruby_xfree(handle->row_data); handle->row_data = NULL; } /* free column info cache */ if ( handle->column_info ) { for (i=0; inum_columns; i++) { ruby_xfree(handle->column_info[i].name); } ruby_xfree(handle->column_info); handle->column_info = NULL; handle->num_columns = 0; } } } /* */ /* static stmt_handle *_ibm_db_new_stmt_struct(conn_handle* conn_res) */ static stmt_handle *_ibm_db_new_stmt_struct(conn_handle* conn_res) { stmt_handle *stmt_res; stmt_res = ALLOC(stmt_handle); memset(stmt_res, 0, sizeof(stmt_handle)); /* Initialize stmt resource so parsing assigns updated options if needed */ stmt_res->hdbc = conn_res->hdbc; stmt_res->s_bin_mode = conn_res->c_bin_mode; stmt_res->cursor_type = conn_res->c_cursor_type; stmt_res->s_case_mode = conn_res->c_case_mode; stmt_res->head_cache_list = NULL; stmt_res->current_node = NULL; stmt_res->num_params = 0; stmt_res->file_param = 0; stmt_res->column_info = NULL; stmt_res->num_columns = 0; stmt_res->error_recno_tracker = 1; stmt_res->errormsg_recno_tracker = 1; stmt_res->row_data = NULL; return stmt_res; } /* */ /* static _ruby_ibm_db_mark_stmt_struct */ static void _ruby_ibm_db_mark_stmt_struct(stmt_handle *handle) { } /* */ /* static _ruby_ibm_db_free_stmt_struct */ static void _ruby_ibm_db_free_stmt_struct(stmt_handle *handle) { int rc; rc = SQLFreeHandle( SQL_HANDLE_STMT, handle->hstmt); if ( handle ) { _ruby_ibm_db_free_result_struct(handle); ruby_xfree(handle); } } /* */ /* allow direct access to hash object as object attributes */ VALUE ibm_db_row_object(int argc, VALUE *argv, VALUE self) { row_hash_struct *row_res; VALUE symbol; VALUE rest; VALUE index; Data_Get_Struct(self, row_hash_struct, row_res); rb_scan_args(argc, argv, "1*", &symbol, &rest); if (symbol == ID2SYM(id_keys)) { return rb_funcall(row_res->hash, id_keys, 0); } else { index = rb_funcall(symbol, id_id2name, 0); return rb_hash_aref(row_res->hash, index); } } /* Module initialization */ void ruby_init_ibm_db() { #ifndef _WIN32 /* Declare variables for DB2 instance settings */ char * tmp_name = NULL; char * instance_name = NULL; #endif ibm_db_globals = ALLOC(struct _ibm_db_globals); memset(ibm_db_globals, 0, sizeof(struct _ibm_db_globals)); ruby_ibm_db_init_globals(ibm_db_globals); /* Specifies that binary data shall be converted to a hexadecimal encoding and returned as an ASCII string */ rb_define_const(mDB, "BINARY", INT2NUM(1)); /* Specifies that binary data shall be converted to a hexadecimal encoding and returned as an ASCII string */ rb_define_const(mDB, "CONVERT", INT2NUM(2)); /* Specifies that binary data shall be converted to a NULL value */ rb_define_const(mDB, "PASSTHRU", INT2NUM(3)); /* Specifies that the column should be bound directly to a file for input */ rb_define_const(mDB, "PARAM_FILE", INT2NUM(11)); /* Specifies the column names case attribute ATTENTION this number is not currently in CLI but used for ibm_db purpose only */ rb_define_const(mDB, "ATTR_CASE", INT2NUM(ATTR_CASE)); /* Specifies that column names will be returned in their natural case */ rb_define_const(mDB, "CASE_NATURAL", INT2NUM(0)); /* Specifies that column names will be returned in lower case */ rb_define_const(mDB, "CASE_LOWER", INT2NUM(1)); /* Specifies that column names will be returned in upper case */ rb_define_const(mDB, "CASE_UPPER", INT2NUM(2)); /* Specifies the cursor type */ rb_define_const(mDB, "SQL_ATTR_CURSOR_TYPE", INT2NUM(SQL_ATTR_CURSOR_TYPE)); /* Cursor type that detects all changes to the result set ATTENTION Only supported when using DB2 for z/OS Version 8.1 and later. */ rb_define_const(mDB, "SQL_CURSOR_DYNAMIC", INT2NUM(SQL_CURSOR_DYNAMIC)); /* Cursor type that only scrolls forward. This is the default */ rb_define_const(mDB, "SQL_CURSOR_FORWARD_ONLY", INT2NUM(SQL_CURSOR_FORWARD_ONLY)); /* Cursor type is a pure keyset cursor */ rb_define_const(mDB, "SQL_CURSOR_KEYSET_DRIVEN", INT2NUM(SQL_CURSOR_KEYSET_DRIVEN)); /* Cursor type that only scrolls forward */ rb_define_const(mDB, "SQL_SCROLL_FORWARD_ONLY", INT2NUM(SQL_SCROLL_FORWARD_ONLY)); /* Cursor type in which the data in the result set is static */ rb_define_const(mDB, "SQL_CURSOR_STATIC", INT2NUM(SQL_CURSOR_STATIC)); /* Parmater binding type of input */ rb_define_const(mDB, "SQL_PARAM_INPUT", INT2NUM(SQL_PARAM_INPUT)); /* Parmater binding type of output */ rb_define_const(mDB, "SQL_PARAM_OUTPUT", INT2NUM(SQL_PARAM_OUTPUT)); /* Parmater binding type of input/output */ rb_define_const(mDB, "SQL_PARAM_INPUT_OUTPUT", INT2NUM(SQL_PARAM_INPUT_OUTPUT)); /* Data type used to specify binary data */ rb_define_const(mDB, "SQL_BINARY", INT2NUM(SQL_BINARY)); /* Data type used to specify bigint data */ rb_define_const(mDB, "SQL_BIGINT", INT2NUM(SQL_BIGINT)); /* Data type used to specify long data */ rb_define_const(mDB, "SQL_LONG", INT2NUM(SQL_INTEGER)); /* Data type used to specify double data */ rb_define_const(mDB, "SQL_DOUBLE", INT2NUM(SQL_DOUBLE)); /* Data type used to specify char data */ rb_define_const(mDB, "SQL_CHAR", INT2NUM(SQL_CHAR)); /* Operates in auto-commit mode off. The application must manually commit or rollback transactions */ rb_define_const(mDB, "SQL_AUTOCOMMIT_OFF", INT2NUM(SQL_AUTOCOMMIT_OFF)); /* Operates in auto-commit mode on. This is the default */ rb_define_const(mDB, "SQL_AUTOCOMMIT_ON", INT2NUM(SQL_AUTOCOMMIT_ON)); /* Specifies whether to use auto-commit or manual commit mode */ rb_define_const(mDB, "SQL_ATTR_AUTOCOMMIT", INT2NUM(SQL_ATTR_AUTOCOMMIT)); /* String used to identify the client user ID sent to the host database */ rb_define_const(mDB, "SQL_ATTR_INFO_USERID", INT2NUM(SQL_ATTR_INFO_USERID)); /* String used to identify the client workstation name sent to the host database */ rb_define_const(mDB, "SQL_ATTR_INFO_WRKSTNNAME", INT2NUM(SQL_ATTR_INFO_WRKSTNNAME)); /* String used to identify the client application name sent to the host database */ rb_define_const(mDB, "SQL_ATTR_INFO_APPLNAME", INT2NUM(SQL_ATTR_INFO_APPLNAME)); /* String used to identify the client accounting string sent to the host database */ rb_define_const(mDB, "SQL_ATTR_INFO_ACCTSTR", INT2NUM(SQL_ATTR_INFO_ACCTSTR)); rb_global_variable(&persistent_list); /* REGISTER_INI_ENTRIES(); */ #ifndef _WIN32 /* Tell DB2 where to find its libraries */ tmp_name = INI_STR("ibm_db.instance_name"); if (NULL != tmp_name) { instance_name = (char *)malloc(strlen(DB2_VAR_INSTANCE) + strlen(tmp_name) + 1); strcpy(instance_name, DB2_VAR_INSTANCE); strcat(instance_name, tmp_name); putenv(instance_name); _ruby_ibm_db_instance_name = instance_name; } #endif #ifdef _AIX /* atexit() handler in the DB2/AIX library segfaults in Ruby CLI */ /* DB2NOEXITLIST env variable prevents DB2 from invoking atexit() */ putenv("DB2NOEXITLIST=TRUE"); #endif persistent_list = rb_hash_new(); le_conn_struct = rb_define_class_under(mDB, "Connection", rb_cObject); le_pconn_struct = rb_define_class_under(mDB, "PConnection", rb_cObject); le_stmt_struct = rb_define_class_under(mDB, "Statement", rb_cObject); le_row_struct = rb_define_class_under(mDB, "RowObject", rb_cObject); le_client_info = rb_define_class_under(mDB, "ClientInfo", rb_cObject); le_server_info = rb_define_class_under(mDB, "ServerInfo", rb_cObject); rb_define_method(le_row_struct, "method_missing", ibm_db_row_object, -1); rb_attr(le_client_info, rb_intern("DRIVER_NAME"), 1, 0, 0); rb_attr(le_client_info, rb_intern("DRIVER_VER"), 1, 0, 0); rb_attr(le_client_info, rb_intern("DATA_SOURCE_NAME"), 1, 0, 0); rb_attr(le_client_info, rb_intern("DRIVER_ODBC_VER"), 1, 0, 0); rb_attr(le_client_info, rb_intern("ODBC_VER"), 1, 0, 0); rb_attr(le_client_info, rb_intern("ODBC_SQL_CONFORMANCE"), 1, 0, 0); rb_attr(le_client_info, rb_intern("APPL_CODEPAGE"), 1, 0, 0); rb_attr(le_client_info, rb_intern("CONN_CODEPAGE"), 1, 0, 0); rb_attr(le_server_info, rb_intern("DBMS_NAME"), 1, 0, 0); rb_attr(le_server_info, rb_intern("DBMS_VER"), 1, 0, 0); rb_attr(le_server_info, rb_intern("DB_CODEPAGE"), 1, 0, 0); rb_attr(le_server_info, rb_intern("DB_NAME"), 1, 0, 0); rb_attr(le_server_info, rb_intern("INST_NAME"), 1, 0, 0); rb_attr(le_server_info, rb_intern("SPECIAL_CHARS"), 1, 0, 0); rb_attr(le_server_info, rb_intern("KEYWORDS"), 1, 0, 0); rb_attr(le_server_info, rb_intern("DFT_ISOLATION"), 1, 0, 0); rb_attr(le_server_info, rb_intern("ISOLATION_OPTION"), 1, 0, 0); rb_attr(le_server_info, rb_intern("SQL_CONFORMANCE"), 1, 0, 0); rb_attr(le_server_info, rb_intern("PROCEDURES"), 1, 0, 0); rb_attr(le_server_info, rb_intern("IDENTIFIER_QUOTE_CHAR"), 1, 0, 0); rb_attr(le_server_info, rb_intern("LIKE_ESCAPE_CLAUSE"), 1, 0, 0); rb_attr(le_server_info, rb_intern("MAX_COL_NAME_LEN"), 1, 0, 0); rb_attr(le_server_info, rb_intern("MAX_ROW_SIZE"), 1, 0, 0); rb_attr(le_server_info, rb_intern("MAX_IDENTIFIER_LEN"), 1, 0, 0); rb_attr(le_server_info, rb_intern("MAX_INDEX_SIZE"), 1, 0, 0); rb_attr(le_server_info, rb_intern("MAX_PROC_NAME_LEN"), 1, 0, 0); rb_attr(le_server_info, rb_intern("MAX_SCHEMA_NAME_LEN"), 1, 0, 0); rb_attr(le_server_info, rb_intern("MAX_STATEMENT_LEN"), 1, 0, 0); rb_attr(le_server_info, rb_intern("MAX_TABLE_NAME_LEN"), 1, 0, 0); rb_attr(le_server_info, rb_intern("NON_NULLABLE_COLUMNS"), 1, 0, 0); } /* */ /* static void _ruby_ibm_db_init_error_info(stmt_handle *stmt_res) */ static void _ruby_ibm_db_init_error_info(stmt_handle *stmt_res) { stmt_res->error_recno_tracker = 1; stmt_res->errormsg_recno_tracker = 1; } /* */ /* static void _ruby_ibm_db_check_sql_errors( SQLHANDLE handle, SQLSMALLINT hType, int rc, int cpy_to_global, char* ret_str, int API SQLSMALLINT recno) */ static void _ruby_ibm_db_check_sql_errors( SQLHANDLE handle, SQLSMALLINT hType, int rc, int cpy_to_global, char* ret_str, int API, SQLSMALLINT recno ) { SQLCHAR msg[SQL_MAX_MESSAGE_LENGTH + 1]; SQLCHAR sqlstate[SQL_SQLSTATE_SIZE + 1]; SQLCHAR errMsg[DB2_MAX_ERR_MSG_LEN]; SQLINTEGER sqlcode; SQLSMALLINT length; char *p; memset(errMsg, '\0', DB2_MAX_ERR_MSG_LEN); memset(msg, '\0', SQL_MAX_MESSAGE_LENGTH + 1); if ( SQLGetDiagRec(hType, handle, recno, sqlstate, &sqlcode, msg, SQL_MAX_MESSAGE_LENGTH + 1, &length ) == SQL_SUCCESS) { while ((p = strchr( (char *)msg, '\n' ))) { *p = '\0'; } sprintf((char*)errMsg, "%s SQLCODE=%d", (char*)msg, (int)sqlcode); switch (rc) { case SQL_ERROR: /* Need to copy the error msg and sqlstate into the symbol Table to cache these results */ if ( cpy_to_global ) { switch (hType) { case SQL_HANDLE_DBC: strncpy(IBM_DB_G(__ruby_conn_err_state), (char*)sqlstate, SQL_SQLSTATE_SIZE+1); strncpy(IBM_DB_G(__ruby_conn_err_msg), (char*)errMsg, DB2_MAX_ERR_MSG_LEN); break; case SQL_HANDLE_STMT: strncpy(IBM_DB_G(__ruby_stmt_err_state), (char*)sqlstate, SQL_SQLSTATE_SIZE+1); strncpy(IBM_DB_G(__ruby_stmt_err_msg), (char*)errMsg, DB2_MAX_ERR_MSG_LEN); break; } } /* This call was made from ibm_db_errmsg or ibm_db_error */ /* Check for error and return */ switch (API) { case DB2_ERR: if ( ret_str != NULL ) { strncpy(ret_str, (char*)sqlstate, SQL_SQLSTATE_SIZE+1); } return; case DB2_ERRMSG: if ( ret_str != NULL ) { strncpy(ret_str, (char*)msg, DB2_MAX_ERR_MSG_LEN); } return; default: break; } break; default: break; } } } /* */ /* static void _ruby_ibm_db_assign_options( void *handle, int type, long opt_key, VALUE data ) */ static void _ruby_ibm_db_assign_options( void *handle, int type, long opt_key, VALUE data ) { int rc = 0; SQLINTEGER autocommit; long option_num = 0; char *option_str = NULL; int i = 0; int err = 0; /* First check to see if it is a non-cli attribut */ if (opt_key == ATTR_CASE) { option_num = NUM2LONG(data); if (type == SQL_HANDLE_STMT) { switch (option_num) { case CASE_LOWER: ((stmt_handle*)handle)->s_case_mode = CASE_LOWER; break; case CASE_UPPER: ((stmt_handle*)handle)->s_case_mode = CASE_UPPER; break; case CASE_NATURAL: ((stmt_handle*)handle)->s_case_mode = CASE_NATURAL; break; default: rb_throw("ATTR_CASE attribute must be one of CASE_LOWER, CASE_UPPER, or CASE_NATURAL", Qnil); } } else if (type == SQL_HANDLE_DBC) { switch (option_num) { case CASE_LOWER: ((conn_handle*)handle)->c_case_mode = CASE_LOWER; break; case CASE_UPPER: ((conn_handle*)handle)->c_case_mode = CASE_UPPER; break; case CASE_NATURAL: ((conn_handle*)handle)->c_case_mode = CASE_NATURAL; break; default: rb_throw("ATTR_CASE attribute must be one of CASE_LOWER, CASE_UPPER, or CASE_NATURAL", Qnil); } } else { rb_throw("Connection or statement handle must be passed in.", Qnil); } } else if (type == SQL_HANDLE_STMT) { if (TYPE(data) == T_STRING) { option_str = STR2CSTR(data); #ifdef PASE rc = SQLSetStmtAttr((SQLHSTMT)((stmt_handle *)handle)->hstmt, opt_key, (SQLPOINTER)&option_str, SQL_IS_INTEGER ); #else rc = SQLSetStmtAttr((SQLHSTMT)((stmt_handle *)handle)->hstmt, opt_key, (SQLPOINTER)option_str, SQL_IS_INTEGER ); #endif if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors((SQLHSTMT)((stmt_handle *)handle)->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); } } else { option_num = NUM2LONG(data); if (opt_key == SQL_ATTR_AUTOCOMMIT && option_num == SQL_AUTOCOMMIT_OFF) ((conn_handle*)handle)->auto_commit = 0; else if (opt_key == SQL_ATTR_AUTOCOMMIT && option_num == SQL_AUTOCOMMIT_ON) ((conn_handle*)handle)->auto_commit = 1; #ifdef PASE rc = SQLSetStmtAttr((SQLHSTMT)((stmt_handle *)handle)->hstmt, opt_key, (SQLPOINTER)&option_num, SQL_IS_INTEGER ); #else rc = SQLSetStmtAttr((SQLHSTMT)((stmt_handle *)handle)->hstmt, opt_key, (SQLPOINTER)option_num, SQL_IS_INTEGER ); #endif if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors((SQLHSTMT)((stmt_handle *)handle)->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); } } } else if (type == SQL_HANDLE_DBC) { if (TYPE(data) == T_STRING) { option_str = STR2CSTR(data); #ifdef PASE rc = SQLSetConnectAttr((SQLHSTMT)((conn_handle*)handle)->hdbc, opt_key, (SQLPOINTER)&option_str, SQL_NTS); #else rc = SQLSetConnectAttr((SQLHSTMT)((conn_handle*)handle)->hdbc, opt_key, (SQLPOINTER)option_str, SQL_NTS); #endif if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors((SQLHSTMT)((stmt_handle *)handle)->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); } } else { option_num = NUM2LONG(data); if (opt_key == SQL_ATTR_AUTOCOMMIT && option_num == SQL_AUTOCOMMIT_OFF) ((conn_handle*)handle)->auto_commit = 0; else if (opt_key == SQL_ATTR_AUTOCOMMIT && option_num == SQL_AUTOCOMMIT_ON) ((conn_handle*)handle)->auto_commit = 1; #ifdef PASE rc = SQLSetConnectAttr((SQLHSTMT)((conn_handle*)handle)->hdbc, opt_key, (SQLPOINTER)&option_num, SQL_IS_INTEGER); #else rc = SQLSetConnectAttr((SQLHSTMT)((conn_handle*)handle)->hdbc, opt_key, (SQLPOINTER)option_num, SQL_IS_INTEGER); #endif if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors((SQLHSTMT)((stmt_handle *)handle)->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); } } } else { rb_throw("Connection or statement handle must be passed in.", Qnil); } } /* */ /* static int _ruby_ibm_db_parse_options( VALUE options, int type, void *handle) */ static int _ruby_ibm_db_parse_options ( VALUE options, int type, void *handle ) { int numOpts = 0, i = 0; VALUE keys; VALUE key; /* Holds the Option Index Key */ VALUE data; if ( !NIL_P(options) ) { keys = rb_funcall(options, id_keys, 0); numOpts = RARRAY(keys)->len; for ( i = 0; i < numOpts; i++) { key = rb_ary_entry(keys,i); data = rb_hash_aref(options,key); /* Assign options to handle. */ /* Sets the options in the handle with CLI/ODBC calls */ _ruby_ibm_db_assign_options( handle, type, NUM2LONG(key), data ); } } return SQL_SUCCESS; } /* */ /* static int _ruby_ibm_db_get_result_set_info(stmt_handle *stmt_res) initialize the result set information of each column. This must be done once */ static int _ruby_ibm_db_get_result_set_info(stmt_handle *stmt_res) { int rc = -1, i; SQLSMALLINT nResultCols = 0, name_length; SQLCHAR tmp_name[BUFSIZ]; rc = SQLNumResultCols((SQLHSTMT)stmt_res->hstmt, &nResultCols); if ( rc == SQL_ERROR || nResultCols == 0) { _ruby_ibm_db_check_sql_errors((SQLHSTMT)stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); return -1; } stmt_res->num_columns = nResultCols; stmt_res->column_info = ALLOC_N(ibm_db_result_set_info, nResultCols); memset(stmt_res->column_info, 0, sizeof(ibm_db_result_set_info)*nResultCols); /* return a set of attributes for a column */ for (i = 0 ; i < nResultCols; i++) { stmt_res->column_info[i].lob_loc = 0; stmt_res->column_info[i].loc_ind = 0; stmt_res->column_info[i].loc_type = 0; rc = SQLDescribeCol((SQLHSTMT)stmt_res->hstmt, (SQLSMALLINT)(i + 1 ), (SQLCHAR *)&tmp_name, BUFSIZ, &name_length, &stmt_res->column_info[i].type, &stmt_res->column_info[i].size, &stmt_res->column_info[i].scale, &stmt_res->column_info[i].nullable); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors((SQLHSTMT)stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); return -1; } if ( name_length <= 0 ) { stmt_res->column_info[i].name = (SQLCHAR *)estrdup(""); } else if (name_length >= BUFSIZ ) { /* column name is longer than BUFSIZ*/ stmt_res->column_info[i].name = (SQLCHAR*)ALLOC_N(char, name_length+1); rc = SQLDescribeCol((SQLHSTMT)stmt_res->hstmt, (SQLSMALLINT)(i + 1 ), stmt_res->column_info[i].name, name_length, &name_length, &stmt_res->column_info[i].type, &stmt_res->column_info[i].size, &stmt_res->column_info[i].scale, &stmt_res->column_info[i].nullable); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors((SQLHSTMT)stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); return -1; } } else { stmt_res->column_info[i].name = (SQLCHAR*)estrdup((char*)tmp_name); } } return 0; } /* */ /* static int _ruby_ibn_bind_column_helper(stmt_handle *stmt_res) bind columns to data, this must be done once */ static int _ruby_ibm_db_bind_column_helper(stmt_handle *stmt_res) { SQLINTEGER in_length = 0; SQLSMALLINT column_type; ibm_db_row_data_type *row_data; int i, rc = SQL_SUCCESS; stmt_res->row_data = ALLOC_N(ibm_db_row_type, stmt_res->num_columns); memset(stmt_res->row_data,0,sizeof(ibm_db_row_type)*stmt_res->num_columns); for (i=0; inum_columns; i++) { column_type = stmt_res->column_info[i].type; row_data = &stmt_res->row_data[i].data; switch(column_type) { case SQL_CHAR: case SQL_VARCHAR: #ifndef PASE /* i5/OS EBCIDIC<->ASCII related - we do getdata call instead */ case SQL_LONGVARCHAR: in_length = stmt_res->column_info[i].size+1; row_data->str_val = (SQLCHAR *) ALLOC_N(char, in_length); rc = SQLBindCol((SQLHSTMT)stmt_res->hstmt, (SQLUSMALLINT)(i+1), SQL_C_DEFAULT, row_data->str_val, in_length, (SQLINTEGER *)(&stmt_res->row_data[i].out_length)); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors((SQLHSTMT)stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); } #else stmt_res->row_data[i].out_length = 0; #endif break; case SQL_BINARY: #ifndef PASE /* i5/OS SQL_LONGVARBINARY is SQL_VARBINARY */ case SQL_LONGVARBINARY: #endif /* PASE */ case SQL_VARBINARY: if ( stmt_res->s_bin_mode == CONVERT ) { in_length = 2*(stmt_res->column_info[i].size)+1; row_data->str_val = (SQLCHAR *)ALLOC_N(char, in_length); rc = SQLBindCol((SQLHSTMT)stmt_res->hstmt, (SQLUSMALLINT)(i+1), SQL_C_CHAR, row_data->str_val, in_length, (SQLINTEGER *)(&stmt_res->row_data[i].out_length)); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors((SQLHSTMT)stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); } } else { in_length = stmt_res->column_info[i].size+1; row_data->str_val = (SQLCHAR *)ALLOC_N(char, in_length); rc = SQLBindCol((SQLHSTMT)stmt_res->hstmt, (SQLUSMALLINT)(i+1), SQL_C_DEFAULT, row_data->str_val, in_length, (SQLINTEGER *)(&stmt_res->row_data[i].out_length)); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors((SQLHSTMT)stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); } } break; case SQL_TYPE_DATE: case SQL_TYPE_TIME: case SQL_TYPE_TIMESTAMP: case SQL_BIGINT: in_length = stmt_res->column_info[i].size+1; row_data->str_val = (SQLCHAR *)ALLOC_N(char, in_length); rc = SQLBindCol((SQLHSTMT)stmt_res->hstmt, (SQLUSMALLINT)(i+1), SQL_C_CHAR, row_data->str_val, in_length, (SQLINTEGER *)(&stmt_res->row_data[i].out_length)); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors((SQLHSTMT)stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); } break; case SQL_SMALLINT: rc = SQLBindCol((SQLHSTMT)stmt_res->hstmt, (SQLUSMALLINT)(i+1), SQL_C_DEFAULT, &row_data->s_val, sizeof(row_data->s_val), (SQLINTEGER *)(&stmt_res->row_data[i].out_length)); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors((SQLHSTMT)stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); } break; case SQL_INTEGER: rc = SQLBindCol((SQLHSTMT)stmt_res->hstmt, (SQLUSMALLINT)(i+1), SQL_C_DEFAULT, &row_data->i_val, sizeof(row_data->i_val), (SQLINTEGER *)(&stmt_res->row_data[i].out_length)); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors((SQLHSTMT)stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); } break; case SQL_REAL: case SQL_FLOAT: rc = SQLBindCol((SQLHSTMT)stmt_res->hstmt, (SQLUSMALLINT)(i+1), SQL_C_DEFAULT, &row_data->f_val, sizeof(row_data->f_val), (SQLINTEGER *)(&stmt_res->row_data[i].out_length)); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors((SQLHSTMT)stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); } break; case SQL_DOUBLE: rc = SQLBindCol((SQLHSTMT)stmt_res->hstmt, (SQLUSMALLINT)(i+1), SQL_C_DEFAULT, &row_data->d_val, sizeof(row_data->d_val), (SQLINTEGER *)(&stmt_res->row_data[i].out_length)); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors((SQLHSTMT)stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); } break; case SQL_DECIMAL: case SQL_NUMERIC: in_length = stmt_res->column_info[i].size + stmt_res->column_info[i].scale + 2 + 1; row_data->str_val = (SQLCHAR *)ALLOC_N(char, in_length); rc = SQLBindCol((SQLHSTMT)stmt_res->hstmt, (SQLUSMALLINT)(i+1), SQL_C_CHAR, row_data->str_val, in_length, (SQLINTEGER *)(&stmt_res->row_data[i].out_length)); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors((SQLHSTMT)stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); } break; case SQL_CLOB: stmt_res->row_data[i].out_length = 0; stmt_res->column_info[i].loc_type = SQL_CLOB_LOCATOR; rc = SQLBindCol((SQLHSTMT)stmt_res->hstmt, (SQLUSMALLINT)(i+1), stmt_res->column_info[i].loc_type, &stmt_res->column_info[i].lob_loc, 4, &stmt_res->column_info[i].loc_ind); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors((SQLHSTMT)stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); } break; case SQL_BLOB: stmt_res->row_data[i].out_length = 0; stmt_res->column_info[i].loc_type = SQL_BLOB_LOCATOR; rc = SQLBindCol((SQLHSTMT)stmt_res->hstmt, (SQLUSMALLINT)(i+1), stmt_res->column_info[i].loc_type, &stmt_res->column_info[i].lob_loc, 4, &stmt_res->column_info[i].loc_ind); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors((SQLHSTMT)stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); } break; case SQL_XML: stmt_res->row_data[i].out_length = 0; break; default: break; } } return rc; } /* */ /* static void _ruby_ibm_db_clear_stmt_err_cache () */ static void _ruby_ibm_db_clear_stmt_err_cache() { memset(IBM_DB_G(__ruby_stmt_err_msg), 0, DB2_MAX_ERR_MSG_LEN); memset(IBM_DB_G(__ruby_stmt_err_state), 0, SQL_SQLSTATE_SIZE + 1); } /* */ /* static int _ruby_ibm_db_connect_helper( argc, argv, isPersistent ) */ static VALUE _ruby_ibm_db_connect_helper( int argc, VALUE *argv, int isPersistent ) { char *database = NULL; char *uid = NULL; char *password = NULL; long database_len; long uid_len; long password_len; VALUE r_db, r_uid, r_passwd, options; int rc = 0; SQLINTEGER conn_alive; SQLINTEGER enable_numeric_literals = 1; /* Enable CLI numeric literals */ conn_handle *conn_res = NULL; int reused = 0; int hKeyLen = 0; char *hKey = NULL; VALUE entry = Qnil; char server[2048]; conn_alive = 1; rb_scan_args(argc, argv, "31", &r_db, &r_uid, &r_passwd, &options); database = rb_str2cstr(r_db, &database_len); uid = rb_str2cstr(r_uid, &uid_len); password = rb_str2cstr(r_passwd, &password_len); do { /* Check if we already have a connection for this userID & database combination */ if (isPersistent) { hKeyLen = strlen(database) + strlen(uid) + strlen(password) + 12; hKey = ALLOC_N(char, hKeyLen); sprintf(hKey, "__ibm_db_%s.%s.%s", uid, database, password); if ( !NIL_P(entry=rb_hash_aref(persistent_list, rb_str_new2(hKey))) ) { Data_Get_Struct(entry, conn_handle, conn_res); #ifndef PASE /* i5/OS server mode is persistant */ /* Need to reinitialize connection? */ rc = SQLGetConnectAttr(conn_res->hdbc, SQL_ATTR_PING_DB, (SQLPOINTER)&conn_alive, 0, NULL); if ( (rc == SQL_SUCCESS) && conn_alive ) { _ruby_ibm_db_check_sql_errors( conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); reused = 1; } /* else will re-connect since connection is dead */ #endif /* PASE */ reused = 1; } } else { /* Need to check for max pconnections? */ } if (conn_res == NULL) { conn_res = ALLOC(conn_handle); memset(conn_res, 0, sizeof(conn_handle)); } /* We need to set this early, in case we get an error below, so we know how to free the connection */ conn_res->flag_pconnect = isPersistent; /* Allocate ENV handles if not present */ if ( !conn_res->henv ) { #ifndef PASE /* i5/OS difference */ rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &(conn_res->henv)); #else /* PASE */ rc = SQLAllocEnv(&(conn_res->henv)); #endif /* PASE */ if (rc != SQL_SUCCESS) { _ruby_ibm_db_check_sql_errors( conn_res->henv, SQL_HANDLE_ENV, rc, 1, NULL, -1, 1); break; } #ifndef PASE /* i5/OS server mode */ rc = SQLSetEnvAttr((SQLHENV)conn_res->henv, SQL_ATTR_ODBC_VERSION, (void *)SQL_OV_ODBC3, 0); #else /* PASE */ long attr = SQL_TRUE; SQLSetEnvAttr((SQLHENV)conn_res->henv, SQL_ATTR_SERVER_MODE, &attr, 0); #endif /* PASE */ } if (! reused) { /* Alloc CONNECT Handle */ rc = SQLAllocHandle( SQL_HANDLE_DBC, conn_res->henv, &(conn_res->hdbc)); if (rc != SQL_SUCCESS) { _ruby_ibm_db_check_sql_errors(conn_res->henv, SQL_HANDLE_ENV, rc, 1, NULL, -1, 1); break; } } /* Set this after the connection handle has been allocated to avoid unnecessary network flows. Initialize the structure to default values */ #ifndef PASE conn_res->auto_commit = SQL_AUTOCOMMIT_ON; rc = SQLSetConnectAttr((SQLHDBC)conn_res->hdbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)(conn_res->auto_commit), SQL_NTS); #else conn_res->auto_commit = SQL_AUTOCOMMIT_ON; rc = SQLSetConnectAttr((SQLHDBC)conn_res->hdbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)(&conn_res->auto_commit), SQL_NTS); if (!IBM_DB_G(i5_allow_commit)) { if (!rc) { SQLINTEGER nocommitpase = SQL_TXN_NO_COMMIT; SQLSetConnectOption((SQLHDBC)conn_res->hdbc, SQL_ATTR_COMMIT, (SQLPOINTER)&nocommitpase); } } #endif conn_res->c_bin_mode = IBM_DB_G(bin_mode); conn_res->c_case_mode = CASE_NATURAL; conn_res->c_cursor_type = SQL_SCROLL_FORWARD_ONLY; conn_res->error_recno_tracker = 1; conn_res->errormsg_recno_tracker = 1; /* handle not active as of yet */ conn_res->handle_active = 0; /* Set Options */ if ( !NIL_P(options) ) { rc = _ruby_ibm_db_parse_options( options, SQL_HANDLE_DBC, conn_res ); if (rc != SQL_SUCCESS) { rb_throw("Options Array must have string indexes", Qnil); } } if (! reused) { /* Connect */ /* If the string contains a =, use SQLDriverConnect */ if ( strstr(database, "=") != NULL ) { rc = SQLDriverConnect((SQLHDBC)conn_res->hdbc, (SQLHWND)NULL, (SQLCHAR*)database, SQL_NTS, NULL, 0, NULL, SQL_DRIVER_NOPROMPT ); } else { rc = SQLConnect( (SQLHDBC)conn_res->hdbc, (SQLCHAR *)database, (SQLSMALLINT)database_len, (SQLCHAR *)uid, (SQLSMALLINT)uid_len, (SQLCHAR *)password, (SQLSMALLINT)password_len ); } if ( rc != SQL_SUCCESS ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); SQLFreeHandle( SQL_HANDLE_DBC, conn_res->hdbc ); SQLFreeHandle(SQL_HANDLE_ENV, conn_res->henv); break; } /* Get the server name */ memset(server, 0, sizeof(server)); rc = SQLGetInfo(conn_res->hdbc, SQL_DBMS_NAME, (SQLPOINTER)server, 2048, NULL); if (!strcmp(server, "AS")) is_systemi = 1; if (!strncmp(server, "IDS", 3)) is_informix = 1; /* Set SQL_ATTR_REPLACE_QUOTED_LITERALS connection attribute to * enable CLI numeric literal feature. This is equivalent to * PATCH2=71 in the db2cli.ini file * Note, for backward compatibility with older CLI drivers having a * different value for SQL_ATTR_REPLACE_QUOTED_LITERALS, we call * SQLSetConnectAttr() with both the old and new value */ #ifndef PASE /* Only enable this feature if we are not connected to an Informix data server */ if (!is_informix) { rc = SQLSetConnectAttr((SQLHDBC)conn_res->hdbc, SQL_ATTR_REPLACE_QUOTED_LITERALS, (SQLPOINTER)(enable_numeric_literals), SQL_IS_INTEGER); if (rc != SQL_SUCCESS) rc = SQLSetConnectAttr((SQLHDBC)conn_res->hdbc, SQL_ATTR_REPLACE_QUOTED_LITERALS_OLDVALUE, (SQLPOINTER)(enable_numeric_literals), SQL_IS_INTEGER); } #else /* Only enable this feature if we are not connected to an Informix data server */ if (!is_informix) { rc = SQLSetConnectAttr((SQLHDBC)conn_res->hdbc, SQL_ATTR_REPLACE_QUOTED_LITERALS, (SQLPOINTER)(&enable_numeric_literals), SQL_IS_INTEGER); if (rc != SQL_SUCCESS) rc = SQLSetConnectAttr((SQLHDBC)conn_res->hdbc, SQL_ATTR_REPLACE_QUOTED_LITERALS_OLDVALUE, (SQLPOINTER)(&enable_numeric_literals), SQL_IS_INTEGER); } #endif if (rc != SQL_SUCCESS) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); } } conn_res->handle_active = 1; } while (0); if (hKey != NULL) { if (! reused && rc == SQL_SUCCESS) { /* If we created a new persistent connection, add it to the persistent_list */ entry = Data_Wrap_Struct(le_pconn_struct, _ruby_ibm_db_mark_pconn_struct, _ruby_ibm_db_free_pconn_struct, conn_res); rb_hash_aset(persistent_list, rb_str_new2(hKey), entry); } ruby_xfree(hKey); } if ( rc != SQL_SUCCESS ) { if (conn_res != NULL && conn_res->handle_active) { rc = SQLFreeHandle( SQL_HANDLE_DBC, conn_res->hdbc); } /* free memory */ if (conn_res != NULL) { ruby_xfree(conn_res); } return Qfalse; } else if (!NIL_P(entry)) { return entry; } else if (isPersistent) { return Data_Wrap_Struct(le_pconn_struct, _ruby_ibm_db_mark_pconn_struct, _ruby_ibm_db_free_pconn_struct, conn_res); } else { return Data_Wrap_Struct(le_conn_struct, _ruby_ibm_db_mark_conn_struct, _ruby_ibm_db_free_conn_struct, conn_res); } } /* */ /* static void _ruby_ibm_db_clear_conn_err_cache () */ static void _ruby_ibm_db_clear_conn_err_cache() { /* Clear out the cached conn messages */ memset(IBM_DB_G(__ruby_conn_err_msg), 0, DB2_MAX_ERR_MSG_LEN); memset(IBM_DB_G(__ruby_conn_err_state), 0, SQL_SQLSTATE_SIZE + 1); } /* */ /* IBM_DB::connect -- Returns a connection to a database * * ===Description * * resource IBM_DB::connect ( string database, string username, string password [, array options] ) * * Creates a new connection to an IBM DB2 Universal Database, IBM Cloudscape, or Apache Derby database. * ==Parameters * * ====database * For a cataloged connection to a database, database represents the database alias in the DB2 client catalog. * For an uncataloged connection to a database, database represents a complete connection string in the following format: * DRIVER={IBM DB2 ODBC DRIVER};DATABASE=database;HOSTNAME=hostname;PORT=port;PROTOCOL=TCPIP;UID=username;PWD=password; * where the parameters represent the following values: * hostname * The hostname or IP address of the database server. * port * The TCP/IP port on which the database is listening for requests. * username * The username with which you are connecting to the database. * password * The password with which you are connecting to the database. * * ====username * The username with which you are connecting to the database. * For uncataloged connections, you must pass a NULL value or empty string. * ====password * The password with which you are connecting to the database. * For uncataloged connections, you must pass a NULL value or empty string. * ====options * An associative array of connection options that affect the behavior of the connection, * where valid array keys include: * SQL_ATTR_AUTOCOMMIT * Passing the SQL_AUTOCOMMIT_ON value turns autocommit on for this connection handle. * Passing the SQL_AUTOCOMMIT_OFF value turns autocommit off for this connection handle. * ATTR_CASE * Passing the CASE_NATURAL value specifies that column names are returned in natural case. * Passing the CASE_LOWER value specifies that column names are returned in lower case. * Passing the CASE_UPPER value specifies that column names are returned in upper case. * CURSOR * Passing the SQL_SCROLL_FORWARD_ONLY value specifies a forward-only cursor for a statement resource. * This is the default cursor type and is supported on all database servers. * Passing the SQL_CURSOR_KEYSET_DRIVEN value specifies a scrollable cursor for a statement resource. * This mode enables random access to rows in a result set, but currently is supported * only by IBM DB2 Universal Database. * * ==Return Values * Returns a connection handle resource if the connection attempt is successful. * If the connection attempt fails, IBM_DB::connect() returns FALSE. * */ VALUE ibm_db_connect(int argc, VALUE *argv, VALUE self) { _ruby_ibm_db_clear_conn_err_cache(); return _ruby_ibm_db_connect_helper( argc, argv, 0 ); } /* */ /* * IBM_DB::pconnect -- Returns a persistent connection to a database * * ===Description * resource IBM_DB::pconnect ( string database, string username, string password [, array options] ) * * Returns a persistent connection to an IBM DB2 Universal Database, IBM Cloudscape, * or Apache Derby database. * * Calling IBM_DB::close() on a persistent connection always returns TRUE, but the underlying DB2 client * connection remains open and waiting to serve the next matching IBM_DB::pconnect() request. * * ===Parameters * * database * The database alias in the DB2 client catalog. * * username * The username with which you are connecting to the database. * * password * The password with which you are connecting to the database. * * options * An associative array of connection options that affect the behavior of the connection, * where valid array keys include: * * autocommit * Passing the DB2_AUTOCOMMIT_ON value turns autocommit on for this connection handle. * Passing the DB2_AUTOCOMMIT_OFF value turns autocommit off for this connection handle. * * DB2_ATTR_CASE * Passing the DB2_CASE_NATURAL value specifies that column names are returned in natural case. * Passing the DB2_CASE_LOWER value specifies that column names are returned in lower case. * Passing the DB2_CASE_UPPER value specifies that column names are returned in upper case. * * CURSOR * Passing the SQL_SCROLL_FORWARD_ONLY value specifies a forward-only cursor for a statement resource. * This is the default cursor type and is supported on all database servers. * Passing the SQL_CURSOR_KEYSET_DRIVEN value specifies a scrollable cursor for a statement resource. * This mode enables random access to rows in a result set, but currently is supported only * by IBM DB2 Universal Database. * * ===Return Values * * Returns a connection handle resource if the connection attempt is successful. IBM_DB::pconnect() * tries to reuse an existing connection resource that exactly matches the database, username, * and password parameters. If the connection attempt fails, IBM_DB::pconnect() returns FALSE. */ VALUE ibm_db_pconnect(int argc, VALUE *argv, VALUE self) { _ruby_ibm_db_clear_conn_err_cache(); return _ruby_ibm_db_connect_helper( argc, argv, 1); } /* */ /* * IBM_DB::autocommit -- Returns or sets the AUTOCOMMIT state for a database connection * * ===Description * mixed IBM_DB::autocommit ( resource connection [, bool value] ) * * Returns or sets the AUTOCOMMIT behavior of the specified connection resource. * * ===Parameters * * connection * A valid database connection resource variable as returned from connect() or pconnect(). * * value * One of the following constants: * DB2_AUTOCOMMIT_OFF * Turns AUTOCOMMIT off. * DB2_AUTOCOMMIT_ON * Turns AUTOCOMMIT on. * * ===Return Values * * When IBM_DB::autocommit() receives only the connection parameter, it returns the current state * of AUTOCOMMIT for the requested connection as an integer value. * A value of 0 indicates that AUTOCOMMIT is off, while a value of 1 indicates that AUTOCOMMIT is on. * * When IBM_DB::autocommit() receives both the connection parameter and autocommit parameter, * it attempts to set the AUTOCOMMIT state of the requested connection to the corresponding state. * * Returns TRUE on success or FALSE on failure. */ VALUE ibm_db_autocommit(int argc, VALUE *argv, VALUE self) { VALUE value; VALUE connection = Qnil; conn_handle *conn_res; int rc; SQLINTEGER autocommit; rb_scan_args(argc, argv, "11", &connection, &value); if (!NIL_P(connection)) { Data_Get_Struct(connection, conn_handle, conn_res); if (!conn_res->handle_active) { rb_throw("Connection is not active", Qnil); } /* If value in handle is different from value passed in */ if (argc == 2) { autocommit = FIX2INT(value); if(autocommit != (conn_res->auto_commit)) { #ifndef PASE rc = SQLSetConnectAttr((SQLHDBC)conn_res->hdbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)autocommit, SQL_IS_INTEGER); #else rc = SQLSetConnectAttr((SQLHDBC)conn_res->hdbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)&autocommit, SQL_IS_INTEGER); #endif if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); } conn_res->auto_commit = autocommit; } return Qtrue; } else { return INT2NUM(conn_res->auto_commit); } } return Qnil; } /* */ /* static void _ruby_ibm_db_add_param_cache( stmt_handle *stmt_res, int param_no, char *varname, int param_type, int size, SQLSMALLINT data_type, SQLSMALLINT precision, SQLSMALLINT scale, SQLSMALLINT nullable ) */ static void _ruby_ibm_db_add_param_cache( stmt_handle *stmt_res, int param_no, char *varname, int varname_len, int param_type, int size, SQLSMALLINT data_type, SQLUINTEGER precision, SQLSMALLINT scale, SQLSMALLINT nullable ) { param_node *tmp_curr = NULL, *prev = stmt_res->head_cache_list, *curr = stmt_res->head_cache_list; while ( (curr != NULL) && (curr->param_num < param_no) ) { prev = curr; curr = curr->next; } if ( curr == NULL || curr->param_num != param_no ) { /* Allocate memory and make new node to be added */ tmp_curr = ALLOC(param_node); memset(tmp_curr, 0, sizeof(param_node)); /* assign values */ tmp_curr->data_type = data_type; tmp_curr->param_size = precision; tmp_curr->nullable = nullable; tmp_curr->scale = scale; tmp_curr->param_num = param_no; tmp_curr->file_options = SQL_FILE_READ; tmp_curr->param_type = param_type; tmp_curr->size = size; /* Set this flag in stmt_res if a FILE INPUT is present */ if ( param_type == PARAM_FILE) { stmt_res->file_param = 1; } if ( varname != NULL) { tmp_curr->varname = estrndup(varname, varname_len); } /* link pointers for the list */ if ( prev == NULL ) { stmt_res->head_cache_list = tmp_curr; } else { prev->next = tmp_curr; } tmp_curr->next = curr; /* Increment num params added */ stmt_res->num_params++; } else { /* Both the nodes are for the same param no */ /* Replace Information */ curr->data_type = data_type; curr->param_size = precision; curr->nullable = nullable; curr->scale = scale; curr->param_num = param_no; curr->file_options = SQL_FILE_READ; curr->param_type = param_type; curr->size = size; /* Set this flag in stmt_res if a FILE INPUT is present */ if ( param_type == PARAM_FILE) { stmt_res->file_param = 1; } /* Free and assign the variable name again */ /* Var lengths can be variable and different in both cases. */ /* This shouldn't happen often anyway */ if ( varname != NULL) { ruby_xfree(curr->varname); curr->varname = estrndup(varname, varname_len); } } } /* */ /* * IBM_DB::bind_param -- Binds a Ruby variable to an SQL statement parameter * * ===Description * bool IBM_DB::bind_param ( resource stmt, int parameter-number, string variable-name [, int parameter-type * [, int data-type [, int precision [, int scale [, int size[]]]]]] ) * * Binds a Ruby variable to an SQL statement parameter in a statement resource returned by IBM_DB::prepare(). * This function gives you more control over the parameter type, data type, precision, and scale for * the parameter than simply passing the variable as part of the optional input array to IBM_DB::execute(). * * ===Parameters * * stmt * A prepared statement returned from IBM_DB::prepare(). * * parameter-number * Specifies the 1-indexed position of the parameter in the prepared statement. * * variable-name * A string specifying the name of the Ruby variable to bind to the parameter specified by parameter-number. * * parameter-type * A constant specifying whether the Ruby variable should be bound to the SQL parameter as an input parameter * (SQL_PARAM_INPUT), an output parameter (SQL_PARAM_OUTPUT), or as a parameter that accepts input and returns output * (SQL_PARAM_INPUT_OUTPUT). To avoid memory overhead, you can also specify PARAM_FILE to bind the Ruby variable * to the name of a file that contains large object (BLOB, CLOB, or DBCLOB) data. * * data-type * A constant specifying the SQL data type that the Ruby variable should be bound as: one of SQL_BINARY, * DB2_CHAR, DB2_DOUBLE, or DB2_LONG . * * precision * Specifies the precision that the variable should be bound to the database. * * scale * Specifies the scale that the variable should be bound to the database. * * size * Specifies the size that should be retreived from an INOUT/OUT parameter. * * ===Return Values * * Returns TRUE on success or FALSE on failure. */ VALUE ibm_db_bind_param(int argc, VALUE *argv, VALUE self) { char *varname = NULL; char error[DB2_MAX_ERR_MSG_LEN]; long varname_len; long param_type = SQL_PARAM_INPUT; /* LONG types used for data being passed in */ SQLUSMALLINT param_no = 0; long data_type = 0; long precision = 0; long scale = 0; long size = 0; SQLSMALLINT sql_data_type = 0; SQLUINTEGER sql_precision = 0; SQLSMALLINT sql_scale = 0; SQLSMALLINT sql_nullable = SQL_NO_NULLS; VALUE stmt = Qnil; stmt_handle *stmt_res; int rc = 0; VALUE r_param_no, r_varname, r_param_type=Qnil, r_size=Qnil; VALUE r_data_type=Qnil, r_precision=Qnil, r_scale=Qnil; rb_scan_args(argc, argv, "35", &stmt, &r_param_no, &r_varname, &r_param_type, &r_data_type, &r_precision, &r_scale, &r_size); param_no = NUM2INT(r_param_no); varname = rb_str2cstr(r_varname, &varname_len); if (!NIL_P(r_param_type)) param_type = NUM2LONG(r_param_type); if (!NIL_P(r_data_type)) data_type = NUM2LONG(r_data_type); if (!NIL_P(r_precision)) precision = NUM2LONG(r_precision); if (!NIL_P(r_scale)) scale = NUM2LONG(r_scale); if (!NIL_P(r_size)) size = NUM2LONG(r_size); if (!NIL_P(stmt)) { Data_Get_Struct(stmt, stmt_handle, stmt_res); /* Check for Param options */ switch (argc) { /* if argc == 3, then the default value for param_type will be used */ case 3: param_type = SQL_PARAM_INPUT; rc = SQLDescribeParam((SQLHSTMT)stmt_res->hstmt, (SQLUSMALLINT)param_no, &sql_data_type, &sql_precision, &sql_scale, &sql_nullable); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); sprintf(error, "Describe Param Failed: %s", IBM_DB_G(__ruby_stmt_err_msg)); rb_throw(error, Qnil); return Qfalse; } /* Add to cache */ _ruby_ibm_db_add_param_cache( stmt_res, param_no, varname, varname_len, param_type, size, sql_data_type, sql_precision, sql_scale, sql_nullable ); break; case 4: rc = SQLDescribeParam((SQLHSTMT)stmt_res->hstmt, (SQLUSMALLINT)param_no, &sql_data_type, &sql_precision, &sql_scale, &sql_nullable); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); sprintf(error, "Describe Param Failed: %s", IBM_DB_G(__ruby_stmt_err_msg)); rb_throw(error, Qnil); return Qfalse; } /* Add to cache */ _ruby_ibm_db_add_param_cache( stmt_res, param_no, varname, varname_len, param_type, size, sql_data_type, sql_precision, sql_scale, sql_nullable ); break; case 5: rc = SQLDescribeParam((SQLHSTMT)stmt_res->hstmt, (SQLUSMALLINT)param_no, &sql_data_type, &sql_precision, &sql_scale, &sql_nullable); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); sprintf(error, "Describe Param Failed: %s", IBM_DB_G(__ruby_stmt_err_msg)); rb_throw(error, Qnil); return Qfalse; } sql_data_type = (SQLSMALLINT)data_type; /* Add to cache */ _ruby_ibm_db_add_param_cache( stmt_res, param_no, varname, varname_len, param_type, size, sql_data_type, sql_precision, sql_scale, sql_nullable ); break; case 6: rc = SQLDescribeParam((SQLHSTMT)stmt_res->hstmt, (SQLUSMALLINT)param_no, &sql_data_type, &sql_precision, &sql_scale, &sql_nullable); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); sprintf(error, "Describe Param Failed: %s", IBM_DB_G(__ruby_stmt_err_msg)); rb_throw(error, Qnil); return Qfalse; } sql_data_type = (SQLSMALLINT)data_type; sql_precision = (SQLUINTEGER)precision; /* Add to cache */ _ruby_ibm_db_add_param_cache( stmt_res, param_no, varname, varname_len, param_type, size, sql_data_type, sql_precision, sql_scale, sql_nullable ); break; case 7: case 8: /* Cache param data passed */ /* I am using a linked list of nodes here because I dont know before hand how many params are being passed in/bound. */ /* To determine this, a call to SQLNumParams is necessary. This is take away any advantages an array would have over linked list access */ /* Data is being copied over to the correct types for subsequent CLI call because this might cause problems on other platforms such as AIX */ sql_data_type = (SQLSMALLINT)data_type; sql_precision = (SQLUINTEGER)precision; sql_scale = (SQLSMALLINT)scale; _ruby_ibm_db_add_param_cache( stmt_res, param_no, varname, varname_len, param_type, size, sql_data_type, sql_precision, sql_scale, sql_nullable ); break; default: /* WRONG_PARAM_COUNT; */ return Qfalse; } /* end Switch */ /* We bind data with DB2 CLI in IBM_DB::execute() */ /* This will save network flow if we need to override params in it */ return Qtrue; } else { rb_throw("Supplied parameter is invalid", Qnil); return Qfalse; } } /* */ /* * IBM_DB::close -- Closes a database connection * * ===Description * * bool IBM_DB::close ( resource connection ) * * This function closes a DB2 client connection created with IBM_DB::connect() and returns * the corresponding resources to the database server. * * If you attempt to close a persistent DB2 client connection created with IBM_DB::pconnect(), the close request * returns TRUE and the persistent DB2 client connection remains available for the next caller. * * ===Parameters * * connection * Specifies an active DB2 client connection. * * ===Return Values * Returns TRUE on success or FALSE on failure. */ VALUE ibm_db_close(int argc, VALUE *argv, VALUE self) { VALUE connection = Qnil; conn_handle *conn_res; int rc; rb_scan_args(argc, argv, "1", &connection); if (!NIL_P(connection)) { /* Check to see if it's a persistent connection; if so, just return true */ Data_Get_Struct(connection, conn_handle, conn_res); if (!conn_res->handle_active) { rb_throw("Connection is not active", Qnil); } if ( conn_res->handle_active && !conn_res->flag_pconnect ) { /* Disconnect from DB. If stmt is allocated, it is freed automatically */ if (conn_res->auto_commit == 0) { rc = SQLEndTran(SQL_HANDLE_DBC, (SQLHDBC)conn_res->hdbc, SQL_ROLLBACK); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } } rc = SQLDisconnect((SQLHDBC)conn_res->hdbc); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } rc = SQLFreeHandle( SQL_HANDLE_DBC, conn_res->hdbc); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } conn_res->handle_active = 0; connection = Qnil; return Qtrue; } else if ( conn_res->flag_pconnect ) { /* Do we need to call FreeStmt or something to close cursors? */ return Qtrue; } else { return Qfalse; } } else { return Qfalse; } } /* */ /* * IBM_DB::column_privileges -- Returns a result set listing the columns and associated privileges for a table * * ===Description * resource IBM_DB::column_privileges ( resource connection [, string qualifier [, string schema * [, string table-name [, string column-name]]]] ) * * Returns a result set listing the columns and associated privileges for a table. * ===Parameters * * connection * A valid connection to an IBM DB2, Cloudscape, or Apache Derby database. * * qualifier * A qualifier for DB2 databases running on OS/390 or z/OS servers. For other databases, * pass NULL or an empty string. * * schema * The schema which contains the tables. To match all schemas, pass NULL or an empty string. * * table-name * The name of the table or view. To match all tables in the database, pass NULL or an empty string. * * column-name * The name of the column. To match all columns in the table, pass NULL or an empty string. * * ===Return Values * Returns a statement resource with a result set containing rows describing the column privileges * for columns matching the specified parameters. The rows are composed of the following columns: * * Column name:: Description * TABLE_CAT:: Name of the catalog. The value is NULL if this table does not have catalogs. * TABLE_SCHEM:: Name of the schema. * TABLE_NAME:: Name of the table or view. * COLUMN_NAME:: Name of the column. * GRANTOR:: Authorization ID of the user who granted the privilege. * GRANTEE:: Authorization ID of the user to whom the privilege was granted. * PRIVILEGE:: The privilege for the column. * IS_GRANTABLE:: Whether the GRANTEE is permitted to grant this privilege to other users. */ VALUE ibm_db_column_privileges(int argc, VALUE *argv, VALUE self) { SQLCHAR *qualifier = NULL; SQLCHAR *owner = NULL; SQLCHAR *table_name = NULL; SQLCHAR *column_name = NULL; VALUE r_qualifier = Qnil; VALUE r_owner = Qnil; VALUE r_table_name = Qnil; VALUE r_table_type = Qnil; VALUE r_column_name = Qnil; VALUE connection = Qnil; conn_handle *conn_res; stmt_handle *stmt_res; int rc; rb_scan_args(argc, argv, "14", &connection, &r_qualifier, &r_owner, &r_table_name, &r_table_type); if (!NIL_P(r_qualifier)) qualifier=(SQLCHAR*)STR2CSTR(r_qualifier); if (!NIL_P(r_owner)) owner=(SQLCHAR*)STR2CSTR(r_owner); if (!NIL_P(r_table_name)) table_name=(SQLCHAR*)STR2CSTR(r_table_name); if (!NIL_P(r_column_name)) column_name=(SQLCHAR*)STR2CSTR(r_column_name); if (!NIL_P(connection)) { Data_Get_Struct(connection, conn_handle, conn_res); if (!conn_res->handle_active) { rb_throw("Connection is not active", Qnil); } if (!conn_res) { rb_throw("Connection Resource cannot be found", Qnil); return Qfalse; } stmt_res = _ibm_db_new_stmt_struct(conn_res); rc = SQLAllocHandle(SQL_HANDLE_STMT, conn_res->hdbc, &(stmt_res->hstmt)); if (rc == SQL_ERROR) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } rc = SQLColumnPrivileges((SQLHSTMT)stmt_res->hstmt, qualifier, SQL_NTS, owner,SQL_NTS, table_name,SQL_NTS, column_name,SQL_NTS); if (rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors((SQLHSTMT)stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); return Qfalse; } return Data_Wrap_Struct(le_stmt_struct, _ruby_ibm_db_mark_stmt_struct, _ruby_ibm_db_free_stmt_struct, stmt_res); } else { return Qfalse; } } /* */ /* * IBM_DB::columns -- Returns a result set listing the columns and associated metadata for a table * ===Description * resource IBM_DB::columns ( resource connection [, string qualifier [, string schema [, string table-name [, string column-name]]]] ) * * Returns a result set listing the columns and associated metadata for a table. * * ===Parameters * connection * A valid connection to an IBM DB2, Cloudscape, or Apache Derby database. * * qualifier * A qualifier for DB2 databases running on OS/390 or z/OS servers. For other databases, pass NULL or an empty string. * * schema * The schema which contains the tables. To match all schemas, pass '%'. * * table-name * The name of the table or view. To match all tables in the database, pass NULL or an empty string. * * column-name * The name of the column. To match all columns in the table, pass NULL or an empty string. * * ===Return Values * Returns a statement resource with a result set containing rows describing the columns matching the specified parameters. * The rows are composed of the following columns: * * Column name:: Description * TABLE_CAT:: Name of the catalog. The value is NULL if this table does not have catalogs. * TABLE_SCHEM:: Name of the schema. * TABLE_NAME:: Name of the table or view. * COLUMN_NAME:: Name of the column. * DATA_TYPE:: The SQL data type for the column represented as an integer value. * TYPE_NAME:: A string representing the data type for the column. * COLUMN_SIZE:: An integer value representing the size of the column. * BUFFER_LENGTH:: Maximum number of bytes necessary to store data from this column. * DECIMAL_DIGITS:: The scale of the column, or NULL where scale is not applicable. * NUM_PREC_RADIX:: An integer value of either 10 (representing an exact numeric data type), 2 (representing an approximate numeric data type), or NULL (representing a data type for which radix is not applicable). * NULLABLE:: An integer value representing whether the column is nullable or not. * REMARKS:: Description of the column. * COLUMN_DEF:: Default value for the column. * SQL_DATA_TYPE:: An integer value representing the size of the column. * SQL_DATETIME_SUB:: Returns an integer value representing a datetime subtype code, or NULL for SQL data types to which this does not apply. * CHAR_OCTET_LENGTH:: Maximum length in octets for a character data type column, which matches COLUMN_SIZE for single-byte character set data, or NULL for non-character data types. * ORDINAL_POSITION:: The 1-indexed position of the column in the table. * IS_NULLABLE:: A string value where 'YES' means that the column is nullable and 'NO' means that the column is not nullable. */ VALUE ibm_db_columns(int argc, VALUE *argv, VALUE self) { SQLCHAR *qualifier = NULL; SQLCHAR *owner = NULL; SQLCHAR *table_name = NULL; SQLCHAR *column_name = NULL; VALUE r_qualifier = Qnil; VALUE r_owner = Qnil; VALUE r_table_name = Qnil; VALUE r_column_name = Qnil; VALUE connection = Qnil; conn_handle *conn_res; stmt_handle *stmt_res; int rc; rb_scan_args(argc, argv, "14", &connection, &r_qualifier, &r_owner, &r_table_name, &r_column_name); if (!NIL_P(r_qualifier)) qualifier=(SQLCHAR*)STR2CSTR(r_qualifier); if (!NIL_P(r_owner)) owner=(SQLCHAR*)STR2CSTR(r_owner); if (!NIL_P(r_table_name)) table_name=(SQLCHAR*)STR2CSTR(r_table_name); if (!NIL_P(r_column_name)) column_name=(SQLCHAR*)STR2CSTR(r_column_name); if (!NIL_P(connection)) { Data_Get_Struct(connection, conn_handle, conn_res); if (!conn_res->handle_active) { rb_throw("Connection is not active", Qnil); } stmt_res = _ibm_db_new_stmt_struct(conn_res); rc = SQLAllocHandle(SQL_HANDLE_STMT, conn_res->hdbc, &(stmt_res->hstmt)); if (rc == SQL_ERROR) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } rc = SQLColumns((SQLHSTMT)stmt_res->hstmt, qualifier, SQL_NTS, owner,SQL_NTS, table_name,SQL_NTS, column_name,SQL_NTS); if (rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors((SQLHSTMT)stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); return Qfalse; } return Data_Wrap_Struct(le_stmt_struct, _ruby_ibm_db_mark_stmt_struct, _ruby_ibm_db_free_stmt_struct, stmt_res); } else { return Qfalse; } } /* */ /* * IBM_DB::foreign_keys -- Returns a result set listing the foreign keys for a table * * ===Description * resource IBM_DB::foreign_keys ( resource connection, string qualifier, string schema, string table-name ) * * Returns a result set listing the foreign keys for a table. * * ===Parameters * * connection * A valid connection to an IBM DB2, Cloudscape, or Apache Derby database. * * qualifier * A qualifier for DB2 databases running on OS/390 or z/OS servers. For other databases, pass NULL * or an empty string. * * schema * The schema which contains the tables. If schema is NULL, IBM_DB::foreign_keys() matches the schema * for the current connection. * * table-name * The name of the table. * * ===Return Values * * Returns a statement resource with a result set containing rows describing the foreign keys for * the specified table. The result set is composed of the following columns: * * Column name:: Description * PKTABLE_CAT:: Name of the catalog for the table containing the primary key. The value is NULL if this table does not have catalogs. * PKTABLE_SCHEM:: Name of the schema for the table containing the primary key. * PKTABLE_NAME:: Name of the table containing the primary key. * PKCOLUMN_NAME:: Name of the column containing the primary key. * FKTABLE_CAT:: Name of the catalog for the table containing the foreign key. The value is NULL if this table does not have catalogs. * FKTABLE_SCHEM:: Name of the schema for the table containing the foreign key. * FKTABLE_NAME:: Name of the table containing the foreign key. * FKCOLUMN_NAME:: Name of the column containing the foreign key. * KEY_SEQ:: 1-indexed position of the column in the key. * UPDATE_RULE:: Integer value representing the action applied to the foreign key when the SQL operation is UPDATE. * DELETE_RULE:: Integer value representing the action applied to the foreign key when the SQL operation is DELETE. * FK_NAME:: The name of the foreign key. * PK_NAME:: The name of the primary key. * DEFERRABILITY:: An integer value representing whether the foreign key deferrability is SQL_INITIALLY_DEFERRED, SQL_INITIALLY_IMMEDIATE, or SQL_NOT_DEFERRABLE. */ VALUE ibm_db_foreign_keys(int argc, VALUE *argv, VALUE self) { SQLCHAR *qualifier = NULL; SQLCHAR *owner = NULL; SQLCHAR *table_name = NULL; SQLCHAR *column_name = NULL; VALUE r_qualifier = Qnil; VALUE r_owner = Qnil; VALUE r_table_name = Qnil; VALUE r_table_type = Qnil; VALUE r_column_name = Qnil; VALUE connection = Qnil; conn_handle *conn_res; stmt_handle *stmt_res; int rc; rb_scan_args(argc, argv, "4", &connection, &r_qualifier, &r_owner, &r_table_name, &r_table_type); if (!NIL_P(r_qualifier)) qualifier=(SQLCHAR*)STR2CSTR(r_qualifier); if (!NIL_P(r_owner)) owner=(SQLCHAR*)STR2CSTR(r_owner); if (!NIL_P(r_table_name)) table_name=(SQLCHAR*)STR2CSTR(r_table_name); if (!NIL_P(r_column_name)) column_name=(SQLCHAR*)STR2CSTR(r_column_name); if (connection) { Data_Get_Struct(connection, conn_handle, conn_res); if (!conn_res->handle_active) { rb_throw("Connection is not active", Qnil); } stmt_res = _ibm_db_new_stmt_struct(conn_res); rc = SQLAllocHandle(SQL_HANDLE_STMT, conn_res->hdbc, &(stmt_res->hstmt)); if (rc == SQL_ERROR) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } rc = SQLForeignKeys((SQLHSTMT)stmt_res->hstmt, qualifier, SQL_NTS, owner, SQL_NTS, table_name ,SQL_NTS, NULL, SQL_NTS, NULL, SQL_NTS, NULL, SQL_NTS); if (rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); return Qfalse; } return Data_Wrap_Struct(le_stmt_struct, _ruby_ibm_db_mark_stmt_struct, _ruby_ibm_db_free_stmt_struct, stmt_res); } else { return Qfalse; } } /* */ /* * IBM_DB::primary_keys -- Returns a result set listing primary keys for a table * * ===Description * resource IBM_DB::primary_keys ( resource connection, string qualifier, string schema, string table-name ) * * Returns a result set listing the primary keys for a table. * * ===Parameters * * connection * A valid connection to an IBM DB2, Cloudscape, or Apache Derby database. * * qualifier * A qualifier for DB2 databases running on OS/390 or z/OS servers. For other databases, pass NULL or an empty string. * * schema * The schema which contains the tables. If schema is NULL, IBM_DB::primary_keys() matches the schema for the current connection. * * table-name * The name of the table. * * ===Return Values * * Returns a statement resource with a result set containing rows describing the primary keys for the specified table. * The result set is composed of the following columns: * * Column name:: Description * TABLE_CAT:: Name of the catalog for the table containing the primary key. The value is NULL if this table does not have catalogs. * TABLE_SCHEM:: Name of the schema for the table containing the primary key. * TABLE_NAME:: Name of the table containing the primary key. * COLUMN_NAME:: Name of the column containing the primary key. * KEY_SEQ:: 1-indexed position of the column in the key. * PK_NAME:: The name of the primary key. */ VALUE ibm_db_primary_keys(int argc, VALUE *argv, VALUE self) { SQLCHAR *qualifier = NULL; SQLCHAR *owner = NULL; SQLCHAR *table_name = NULL; SQLCHAR *column_name = NULL; VALUE r_qualifier = Qnil; VALUE r_owner = Qnil; VALUE r_table_name = Qnil; VALUE r_table_type = Qnil; VALUE r_column_name = Qnil; VALUE connection = Qnil; conn_handle *conn_res; stmt_handle *stmt_res; int rc; rb_scan_args(argc, argv, "4", &connection, &r_qualifier, &r_owner, &r_table_name, &r_table_type); if (!NIL_P(r_qualifier)) qualifier=(SQLCHAR*)STR2CSTR(r_qualifier); if (!NIL_P(r_owner)) owner=(SQLCHAR*)STR2CSTR(r_owner); if (!NIL_P(r_table_name)) table_name=(SQLCHAR*)STR2CSTR(r_table_name); if (!NIL_P(r_column_name)) column_name=(SQLCHAR*)STR2CSTR(r_column_name); if (connection) { Data_Get_Struct(connection, conn_handle, conn_res); if (!conn_res->handle_active) { rb_throw("Connection is not active", Qnil); } stmt_res = _ibm_db_new_stmt_struct(conn_res); rc = SQLAllocHandle(SQL_HANDLE_STMT, conn_res->hdbc, &(stmt_res->hstmt)); if (rc == SQL_ERROR) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } rc = SQLPrimaryKeys((SQLHSTMT)stmt_res->hstmt, qualifier, SQL_NTS, owner, SQL_NTS, table_name,SQL_NTS); if (rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); return Qfalse; } return Data_Wrap_Struct(le_stmt_struct, _ruby_ibm_db_mark_stmt_struct, _ruby_ibm_db_free_stmt_struct, stmt_res); } else { return Qfalse; } } /* */ /* * IBM_DB::procedure_columns -- Returns a result set listing stored procedure parameters * * ===Description * resource IBM_DB::procedure_columns ( resource connection, string qualifier, string schema, string procedure, string parameter ) * * Returns a result set listing the parameters for one or more stored procedures. * * ===Parameters * * connection * A valid connection to an IBM DB2, Cloudscape, or Apache Derby database. * * qualifier * A qualifier for DB2 databases running on OS/390 or z/OS servers. For other databases, pass NULL or an empty string. * * schema * The schema which contains the procedures. This parameter accepts a search pattern containing _ and % as wildcards. * * procedure * The name of the procedure. This parameter accepts a search pattern containing _ and % as wildcards. * * parameter * The name of the parameter. This parameter accepts a search pattern containing _ and % as wildcards. * If this parameter is NULL, all parameters for the specified stored procedures are returned. * * ===Return Values * * Returns a statement resource with a result set containing rows describing the parameters for the stored procedures * matching the specified parameters. The rows are composed of the following columns: * * Column name:: Description * PROCEDURE_CAT:: The catalog that contains the procedure. The value is NULL if this table does not have catalogs. * PROCEDURE_SCHEM:: Name of the schema that contains the stored procedure. * PROCEDURE_NAME:: Name of the procedure. * COLUMN_NAME:: Name of the parameter. * COLUMN_TYPE:: An integer value representing the type of the parameter: * Return value:: Parameter type * 1:: (SQL_PARAM_INPUT) Input (IN) parameter. * 2:: (SQL_PARAM_INPUT_OUTPUT) Input/output (INOUT) parameter. * 3:: (SQL_PARAM_OUTPUT) Output (OUT) parameter. * DATA_TYPE:: The SQL data type for the parameter represented as an integer value. * TYPE_NAME:: A string representing the data type for the parameter. * COLUMN_SIZE:: An integer value representing the size of the parameter. * BUFFER_LENGTH:: Maximum number of bytes necessary to store data for this parameter. * DECIMAL_DIGITS:: The scale of the parameter, or NULL where scale is not applicable. * NUM_PREC_RADIX:: An integer value of either 10 (representing an exact numeric data type), 2 (representing anapproximate numeric data type), or NULL (representing a data type for which radix is not applicable). * NULLABLE:: An integer value representing whether the parameter is nullable or not. * REMARKS:: Description of the parameter. * COLUMN_DEF:: Default value for the parameter. * SQL_DATA_TYPE:: An integer value representing the size of the parameter. * SQL_DATETIME_SUB:: Returns an integer value representing a datetime subtype code, or NULL for SQL data types to which this does not apply. * CHAR_OCTET_LENGTH:: Maximum length in octets for a character data type parameter, which matches COLUMN_SIZE for single-byte character set data, or NULL for non-character data types. * ORDINAL_POSITION:: The 1-indexed position of the parameter in the CALL statement. * IS_NULLABLE:: A string value where 'YES' means that the parameter accepts or returns NULL values and 'NO' means that the parameter does not accept or return NULL values. */ VALUE ibm_db_procedure_columns(int argc, VALUE *argv, VALUE self) { SQLCHAR *qualifier = NULL; SQLCHAR *owner = NULL; SQLCHAR *proc_name = NULL; SQLCHAR *column_name = NULL; int qualifier_len; int owner_len; int proc_name_len; int column_name_len; VALUE r_qualifier = Qnil; VALUE r_qualifier_len = Qnil; VALUE r_owner = Qnil; VALUE r_owner_len = Qnil; VALUE r_proc_name = Qnil; VALUE r_proc_name_len = Qnil; VALUE r_column_name = Qnil; VALUE r_column_name_len = Qnil; VALUE connection = Qnil; int rc = 0; conn_handle *conn_res; stmt_handle *stmt_res; rb_scan_args(argc, argv, "5", &connection, &r_qualifier, &r_qualifier_len, &r_owner, &r_owner_len, &proc_name, &proc_name_len, &column_name, &column_name_len); if (!NIL_P(r_qualifier)) qualifier=(SQLCHAR*)STR2CSTR(r_qualifier); if (!NIL_P(r_qualifier_len)) qualifier_len=(int)r_qualifier_len; if (!NIL_P(r_owner)) owner=(SQLCHAR*)STR2CSTR(r_owner); if (!NIL_P(r_owner_len)) owner_len=(int)r_owner; if (!NIL_P(r_proc_name)) proc_name=(SQLCHAR*)STR2CSTR(r_proc_name); if (!NIL_P(r_proc_name_len)) proc_name_len=(int)r_proc_name_len; if (!NIL_P(r_column_name)) column_name=(SQLCHAR*)STR2CSTR(r_column_name); if (!NIL_P(r_column_name_len)) column_name_len=(int)r_column_name_len; if (!NIL_P(connection)) { Data_Get_Struct(connection, conn_handle, conn_res); if (!conn_res->handle_active) { rb_throw("Connection is not active", Qnil); } stmt_res = _ibm_db_new_stmt_struct(conn_res); rc = SQLAllocHandle(SQL_HANDLE_STMT, conn_res->hdbc, &(stmt_res->hstmt)); if (rc == SQL_ERROR) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } rc = SQLProcedureColumns((SQLHSTMT)stmt_res->hstmt, qualifier, SQL_NTS, owner, SQL_NTS, proc_name, SQL_NTS, column_name, SQL_NTS); if (rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); return Qfalse; } return Data_Wrap_Struct(le_stmt_struct, _ruby_ibm_db_mark_stmt_struct, _ruby_ibm_db_free_stmt_struct, stmt_res); } else { return Qfalse; } } /* */ /* * IBM_DB::procedures -- Returns a result set listing the stored procedures registered in a database * * ===Description * resource IBM_DB::procedures ( resource connection, string qualifier, string schema, string procedure ) * * Returns a result set listing the stored procedures registered in a database. * * ===Parameters * * connection * A valid connection to an IBM DB2, Cloudscape, or Apache Derby database. * * qualifier * A qualifier for DB2 databases running on OS/390 or z/OS servers. For other databases, pass NULL or an empty string. * * schema * The schema which contains the procedures. This parameter accepts a search pattern containing _ and % as wildcards. * * procedure * The name of the procedure. This parameter accepts a search pattern containing _ and % as wildcards. * * ===Return Values * * Returns a statement resource with a result set containing rows describing the stored procedures matching the specified parameters. The rows are composed of the following columns: * * Column name:: Description * PROCEDURE_CAT:: The catalog that contains the procedure. The value is NULL if this table does not have catalogs. * PROCEDURE_SCHEM:: Name of the schema that contains the stored procedure. * PROCEDURE_NAME:: Name of the procedure. * NUM_INPUT_PARAMS:: Number of input (IN) parameters for the stored procedure. * NUM_OUTPUT_PARAMS:: Number of output (OUT) parameters for the stored procedure. * NUM_RESULT_SETS:: Number of result sets returned by the stored procedure. * REMARKS:: Any comments about the stored procedure. * PROCEDURE_TYPE:: Always returns 1, indicating that the stored procedure does not return a return value. */ VALUE ibm_db_procedures(int argc, VALUE *argv, VALUE self) { SQLCHAR *qualifier = NULL; SQLCHAR *owner = NULL; SQLCHAR *proc_name = NULL; int qualifier_len; int owner_len; int proc_name_len; int rc = 0; conn_handle *conn_res; stmt_handle *stmt_res; VALUE r_qualifier = Qnil; VALUE r_qualifier_len = Qnil; VALUE r_owner = Qnil; VALUE r_owner_len = Qnil; VALUE r_proc_name = Qnil; VALUE r_proc_name_len = Qnil; VALUE connection = Qnil; rb_scan_args(argc, argv, "4", &connection, &r_qualifier, &r_qualifier_len, &r_owner, &r_owner_len, &proc_name, &proc_name_len); if (!NIL_P(r_qualifier)) qualifier=(SQLCHAR*)STR2CSTR(r_qualifier); if (!NIL_P(r_qualifier_len)) qualifier_len=(int)r_qualifier_len; if (!NIL_P(r_owner)) owner=(SQLCHAR*)STR2CSTR(r_owner); if (!NIL_P(r_owner_len)) owner_len=(int)r_owner; if (!NIL_P(r_proc_name)) proc_name=(SQLCHAR*)STR2CSTR(r_proc_name); if (!NIL_P(r_proc_name_len)) proc_name_len=(int)r_proc_name_len; if (!NIL_P(connection)) { Data_Get_Struct(connection, conn_handle, conn_res); if (!conn_res->handle_active) { rb_throw("Connection is not active", Qnil); } stmt_res = _ibm_db_new_stmt_struct(conn_res); rc = SQLAllocHandle(SQL_HANDLE_STMT, conn_res->hdbc, &(stmt_res->hstmt)); if (rc == SQL_ERROR) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } rc = SQLProcedures((SQLHSTMT)stmt_res->hstmt, qualifier, SQL_NTS, owner, SQL_NTS, proc_name, SQL_NTS); if (rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); return Qfalse; } return Data_Wrap_Struct(le_stmt_struct, _ruby_ibm_db_mark_stmt_struct, _ruby_ibm_db_free_stmt_struct, stmt_res); } else { return Qfalse; } } /* */ /* * IBM_DB::special_columns -- Returns a result set listing the unique row identifier columns for a table * * ===Description * resource IBM_DB::special_columns ( resource connection, string qualifier, string schema, string table_name, int scope ) * * Returns a result set listing the unique row identifier columns for a table. * * ===Parameters * * connection * A valid connection to an IBM DB2, Cloudscape, or Apache Derby database. * * qualifier * A qualifier for DB2 databases running on OS/390 or z/OS servers. For other databases, pass NULL or an empty string. * * schema * The schema which contains the tables. * * table_name * The name of the table. * * scope * Integer value representing the minimum duration for which the unique row identifier is valid. This can be one of the following values: * * 0: Row identifier is valid only while the cursor is positioned on the row. (SQL_SCOPE_CURROW) * 1: Row identifier is valid for the duration of the transaction. (SQL_SCOPE_TRANSACTION) * 2: Row identifier is valid for the duration of the connection. (SQL_SCOPE_SESSION) * * ===Return Values * * Returns a statement resource with a result set containing rows with unique row identifier information for a table. * The rows are composed of the following columns: * * Column name:: Description * SCOPE:: Integer value representing the minimum duration for which the unique row identifier is valid. * * 0: Row identifier is valid only while the cursor is positioned on the row. (SQL_SCOPE_CURROW) * * 1: Row identifier is valid for the duration of the transaction. (SQL_SCOPE_TRANSACTION) * * 2: Row identifier is valid for the duration of the connection. (SQL_SCOPE_SESSION) * COLUMN_NAME:: Name of the unique column. * DATA_TYPE:: SQL data type for the column. * TYPE_NAME:: Character string representation of the SQL data type for the column. * COLUMN_SIZE:: An integer value representing the size of the column. * BUFFER_LENGTH:: Maximum number of bytes necessary to store data from this column. * DECIMAL_DIGITS:: The scale of the column, or NULL where scale is not applicable. * NUM_PREC_RADIX:: An integer value of either 10 (representing an exact numeric data type),2 (representing an * approximate numeric data type), or NULL (representing a data type for which radix is not applicable). * PSEUDO_COLUMN:: Always returns 1. */ VALUE ibm_db_special_columns(int argc, VALUE *argv, VALUE self) { SQLCHAR *qualifier = NULL; SQLCHAR *owner = NULL; SQLCHAR *table_name = NULL; int qualifier_len; int owner_len; int table_name_len; int scope = 0; conn_handle *conn_res; stmt_handle *stmt_res; int rc; VALUE r_qualifier = Qnil; VALUE r_qualifier_len = Qnil; VALUE r_owner = Qnil; VALUE r_owner_len = Qnil; VALUE r_table_name = Qnil; VALUE r_table_name_len = Qnil; VALUE r_scope = Qnil; VALUE connection = Qnil; rb_scan_args(argc, argv, "5", &connection, &r_qualifier, &r_qualifier_len, &r_owner, &r_owner_len, &r_table_name, &r_table_name_len, &r_scope); if (!NIL_P(r_qualifier)) qualifier=(SQLCHAR*)STR2CSTR(r_qualifier); if (!NIL_P(r_qualifier_len)) qualifier_len=(int)r_qualifier_len; if (!NIL_P(r_owner)) owner=(SQLCHAR*)STR2CSTR(r_owner); if (!NIL_P(r_owner_len)) owner_len=(int)r_owner_len; if (!NIL_P(r_table_name)) table_name=(SQLCHAR*)STR2CSTR(r_table_name); if (!NIL_P(r_table_name_len)) table_name_len=(int)r_table_name_len; if (!NIL_P(r_scope)) scope=(int)r_scope; if (!NIL_P(connection)) { Data_Get_Struct(connection, conn_handle, conn_res); if (!conn_res->handle_active) { rb_throw("Connection is not active", Qnil); } stmt_res = _ibm_db_new_stmt_struct(conn_res); rc = SQLAllocHandle(SQL_HANDLE_STMT, conn_res->hdbc, &(stmt_res->hstmt)); if (rc == SQL_ERROR) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } rc = SQLSpecialColumns((SQLHSTMT)stmt_res->hstmt,SQL_BEST_ROWID, qualifier, SQL_NTS, owner,SQL_NTS, table_name,SQL_NTS,scope,SQL_NULLABLE); if (rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); return Qfalse; } return Data_Wrap_Struct(le_stmt_struct, _ruby_ibm_db_mark_stmt_struct, _ruby_ibm_db_free_stmt_struct, stmt_res); } else { return Qfalse; } } /* */ /* * IBM_DB::statistics -- Returns a result set listing the index and statistics for a table * * ===Description * resource IBM_DB::statistics ( resource connection, string qualifier, string schema, string table-name, bool unique ) * * Returns a result set listing the index and statistics for a table. * * ===Parameters * * connection * A valid connection to an IBM DB2, Cloudscape, or Apache Derby database. * * qualifier * A qualifier for DB2 databases running on OS/390 or z/OS servers. For other databases, pass NULL or an empty string. * * schema * The schema that contains the targeted table. If this parameter is NULL, the statistics and indexes are * returned for the schema of the current user. * * table_name * The name of the table. * * unique * An boolean value representing the type of index information to return. * * False Return only the information for unique indexes on the table. * * True Return the information for all indexes on the table. * * ===Return Values * * Returns a statement resource with a result set containing rows describing the statistics and indexes for the base tables * matching the specified parameters. The rows are composed of the following columns: * * Column name:: Description * TABLE_CAT:: The catalog that contains the table. The value is NULL if this table does not have catalogs. * TABLE_SCHEM:: Name of the schema that contains the table. * TABLE_NAME:: Name of the table. * NON_UNIQUE:: An integer value representing whether the index prohibits unique values, or whether the row represents * statistics on the table itself: * * Return value:: Parameter type * 0 (SQL_FALSE):: The index allows duplicate values. * 1 (SQL_TRUE):: The index values must be unique. * NULL:: This row is statistics information for the table itself. * * INDEX_QUALIFIER:: A string value representing the qualifier that would have to be prepended to INDEX_NAME to fully qualify the index. * INDEX_NAME:: A string representing the name of the index. * TYPE:: An integer value representing the type of information contained in this row of the result set: * * Return value:: Parameter type * 0 (SQL_TABLE_STAT):: The row contains statistics about the table itself. * 1 (SQL_INDEX_CLUSTERED):: The row contains information about a clustered index. * 2 (SQL_INDEX_HASH):: The row contains information about a hashed index. * 3 (SQL_INDEX_OTHER):: The row contains information about a type of index that is neither clustered nor hashed. * * ORDINAL_POSITION:: The 1-indexed position of the column in the index. NULL if the row contains statistics information about the table itself. * COLUMN_NAME:: The name of the column in the index. NULL if the row contains statistics information about the table itself. * ASC_OR_DESC:: A if the column is sorted in ascending order, D if the column is sorted in descending order, NULL * if the row contains statistics information about the table itself. * CARDINALITY:: If the row contains information about an index, this column contains an integer value representing the number * of unique values in the index. * If the row contains information about the table itself, this column contains an integer value representing the * number of rows in the table. * PAGES:: If the row contains information about an index, this column contains an integer value representing the number of pages * used to store the index. * If the row contains information about the table itself, this column contains an integer value representing the number * of pages used to store the table. * FILTER_CONDITION:: Always returns NULL. */ VALUE ibm_db_statistics(int argc, VALUE *argv, VALUE self) { SQLCHAR *qualifier = NULL; SQLCHAR *owner = NULL; SQLCHAR *table_name = NULL; int unique = 0; int rc = 0; SQLUSMALLINT sql_unique; conn_handle *conn_res; stmt_handle *stmt_res; VALUE r_qualifier = Qnil; VALUE r_owner = Qnil; VALUE r_table_name = Qnil; VALUE r_unique = Qnil; VALUE connection = Qnil; rb_scan_args(argc, argv, "5", &connection, &r_qualifier, &r_owner, &r_table_name, &r_unique); if (!NIL_P(r_qualifier)) qualifier=(SQLCHAR*)STR2CSTR(r_qualifier); if (!NIL_P(r_owner)) owner=(SQLCHAR*)STR2CSTR(r_owner); if (!NIL_P(r_table_name)) table_name=(SQLCHAR*)STR2CSTR(r_table_name); if (!NIL_P(r_unique)) unique=(int)NUM2INT(r_unique); if (!NIL_P(connection)) { Data_Get_Struct(connection, conn_handle, conn_res); if (!conn_res->handle_active) { rb_throw("Connection is not active", Qnil); } stmt_res = _ibm_db_new_stmt_struct(conn_res); sql_unique = unique; rc = SQLAllocHandle(SQL_HANDLE_STMT, conn_res->hdbc, &(stmt_res->hstmt)); if (rc == SQL_ERROR) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } rc = SQLStatistics((SQLHSTMT)stmt_res->hstmt, qualifier, SQL_NTS, owner, SQL_NTS, table_name, SQL_NTS, sql_unique, SQL_QUICK); if (rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); return Qfalse; } return Data_Wrap_Struct(le_stmt_struct, _ruby_ibm_db_mark_stmt_struct, _ruby_ibm_db_free_stmt_struct, stmt_res); } else { return Qfalse; } } /* */ /* * IBM_DB::table_privileges -- Returns a result set listing the tables and associated privileges in a database * * ===Description * resource IBM_DB::table_privileges ( resource connection [, string qualifier [, string schema [, string table_name]]] ) * * Returns a result set listing the tables and associated privileges in a database. * * ===Parameters * * connection * A valid connection to an IBM DB2, Cloudscape, or Apache Derby database. * * qualifier * A qualifier for DB2 databases running on OS/390 or z/OS servers. For other databases, * pass NULL or an empty string. * * schema * The schema which contains the tables. This parameter accepts a search pattern containing _ * and % as wildcards. * * table_name * The name of the table. This parameter accepts a search pattern containing _ and % as wildcards. * * ===Return Values * * Returns a statement resource with a result set containing rows describing the privileges for the * tables that match the specified parameters. The rows are composed of the following columns: * * Column name:: Description * TABLE_CAT:: The catalog that contains the table. The value is NULL if this table does not have catalogs. * TABLE_SCHEM:: Name of the schema that contains the table. * TABLE_NAME:: Name of the table. * GRANTOR:: Authorization ID of the user who granted the privilege. * GRANTEE:: Authorization ID of the user to whom the privilege was granted. * PRIVILEGE:: The privilege that has been granted. This can be one of ALTER, CONTROL, DELETE, INDEX, * INSERT, REFERENCES, SELECT, or UPDATE. * IS_GRANTABLE:: A string value of "YES" or "NO" indicating whether the grantee can grant the privilege to other users. */ VALUE ibm_db_table_privileges(int argc, VALUE *argv, VALUE self) { SQLCHAR *qualifier = NULL; SQLCHAR *owner = NULL; SQLCHAR *table_name = NULL; int qualifier_len; int owner_len; int table_name_len; conn_handle *conn_res; stmt_handle *stmt_res; int rc; VALUE r_qualifier = Qnil; VALUE r_qualifier_len = Qnil; VALUE r_owner = Qnil; VALUE r_owner_len = Qnil; VALUE r_table_name = Qnil; VALUE r_table_name_len = Qnil; VALUE connection = Qnil; rb_scan_args(argc, argv, "13", &connection, &r_qualifier, &r_qualifier_len, &r_owner, &r_owner_len, &r_table_name, &r_table_name_len); if (!NIL_P(r_qualifier)) qualifier=(SQLCHAR*)STR2CSTR(r_qualifier); if (!NIL_P(r_qualifier_len)) qualifier_len=(int)r_qualifier_len; if (!NIL_P(r_owner)) owner=(SQLCHAR*)STR2CSTR(r_owner); if (!NIL_P(r_owner_len)) owner_len=(int)r_owner_len; if (!NIL_P(r_table_name)) table_name=(SQLCHAR*)STR2CSTR(r_table_name); if (!NIL_P(r_table_name_len)) table_name_len=(int)r_table_name_len; if (!NIL_P(connection)) { Data_Get_Struct(connection, conn_handle, conn_res); if (!conn_res->handle_active) { rb_throw("Connection is not active", Qnil); } if (!conn_res) { rb_throw("Connection Resource cannot be found", Qnil); return Qfalse; } stmt_res = _ibm_db_new_stmt_struct(conn_res); rc = SQLAllocHandle(SQL_HANDLE_STMT, conn_res->hdbc, &(stmt_res->hstmt)); if (rc == SQL_ERROR) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } rc = SQLTablePrivileges((SQLHSTMT)stmt_res->hstmt, qualifier, SQL_NTS, owner, SQL_NTS, table_name, SQL_NTS); if (rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); return Qfalse; } return Data_Wrap_Struct(le_stmt_struct, _ruby_ibm_db_mark_stmt_struct, _ruby_ibm_db_free_stmt_struct, stmt_res); } else { return Qfalse; } } /* */ /* * IBM_DB::tables -- Returns a result set listing the tables and associated metadata in a database * * ===Description * resource IBM_DB::tables ( resource connection [, string qualifier [, string schema [, string table-name [, string table-type]]]] ) * * Returns a result set listing the tables and associated metadata in a database. * * ===Parameters * * connection * A valid connection to an IBM DB2, Cloudscape, or Apache Derby database. * * qualifier * A qualifier for DB2 databases running on OS/390 or z/OS servers. For other databases, pass NULL or an empty string. * * schema * The schema which contains the tables. This parameter accepts a search pattern containing _ and % as wildcards. * * table-name * The name of the table. This parameter accepts a search pattern containing _ and % as wildcards. * * table-type * A list of comma-delimited table type identifiers. To match all table types, pass NULL or an empty string. * Valid table type identifiers include: ALIAS, HIERARCHY TABLE, INOPERATIVE VIEW, NICKNAME, MATERIALIZED QUERY * TABLE, SYSTEM TABLE, TABLE, TYPED TABLE, TYPED VIEW, and VIEW. * * ===Return Values * * Returns a statement resource with a result set containing rows describing the tables that match the specified parameters. * The rows are composed of the following columns: * * Column name:: Description * TABLE_CAT:: The catalog that contains the table. The value is NULL if this table does not have catalogs. * TABLE_SCHEMA:: Name of the schema that contains the table. * TABLE_NAME:: Name of the table. * TABLE_TYPE:: Table type identifier for the table. * REMARKS:: Description of the table. */ VALUE ibm_db_tables(int argc, VALUE *argv, VALUE self) { SQLCHAR *qualifier = NULL; SQLCHAR *owner = NULL; SQLCHAR *table_name = NULL; SQLCHAR *table_type = NULL; VALUE r_qualifier; VALUE r_owner; VALUE r_table_name; VALUE r_table_type; VALUE connection = Qnil; conn_handle *conn_res; stmt_handle *stmt_res; int rc; rb_scan_args(argc, argv, "14", &connection, &r_qualifier, &r_owner, &r_table_name, &r_table_type); if (!NIL_P(r_qualifier)) qualifier=(SQLCHAR*)STR2CSTR(r_qualifier); if (!NIL_P(r_owner)) owner=(SQLCHAR*)STR2CSTR(r_owner); if (!NIL_P(r_table_name)) table_name=(SQLCHAR*)STR2CSTR(r_table_name); if (!NIL_P(r_table_type)) table_type=(SQLCHAR*)STR2CSTR(r_table_type); if (!NIL_P(connection)) { Data_Get_Struct(connection, conn_handle, conn_res); if (!conn_res->handle_active) { rb_throw("Connection is not active", Qnil); } stmt_res = _ibm_db_new_stmt_struct(conn_res); rc = SQLAllocHandle(SQL_HANDLE_STMT, conn_res->hdbc, &(stmt_res->hstmt)); if (rc == SQL_ERROR) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } rc = SQLTables((SQLHSTMT)stmt_res->hstmt, qualifier, SQL_NTS, owner, SQL_NTS, table_name, SQL_NTS, table_type, SQL_NTS); if (rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); return Qfalse; } return Data_Wrap_Struct(le_stmt_struct, _ruby_ibm_db_mark_stmt_struct, _ruby_ibm_db_free_stmt_struct, stmt_res); } else { return Qfalse; } } /* */ /* * IBM_DB::commit -- Commits a transaction * ===Description * bool IBM_DB::commit ( resource connection ) * * Commits an in-progress transaction on the specified connection resource and begins a new transaction. * Ruby applications normally default to AUTOCOMMIT mode, so IBM_DB::commit() is not necessary unless * AUTOCOMMIT has been turned off for the connection resource. * * Note: If the specified connection resource is a persistent connection, all transactions * in progress for all applications using that persistent connection will be committed. For this reason, * persistent connections are not recommended for use in applications that require transactions. * * ===Parameters * * connection * A valid database connection resource variable as returned from IBM_DB::connect() or IBM_DB::pconnect(). * * ===Return Values * * Returns TRUE on success or FALSE on failure. */ VALUE ibm_db_commit(int argc, VALUE *argv, VALUE self) { VALUE connection = Qnil; conn_handle *conn_res; int rc; rb_scan_args(argc, argv, "1", &connection); if (!NIL_P(connection)) { Data_Get_Struct(connection, conn_handle, conn_res); if (!conn_res->handle_active) { rb_throw("Connection is not active", Qnil); } rc = SQLEndTran(SQL_HANDLE_DBC, conn_res->hdbc, SQL_COMMIT); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } else { return Qtrue; } } return Qfalse; } /* */ /* static int _ruby_ibm_db_do_prepare(SQLHANDLE hdbc, VALUE stmt, stmt_handle *stmt_res, VALUE options) */ static int _ruby_ibm_db_do_prepare(SQLHANDLE hdbc, VALUE stmt, stmt_handle *stmt_res, VALUE options) { SQLCHAR *stmt_string = NULL; long stmt_string_len; int rc; /* alloc handle and return only if it errors */ rc = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &(stmt_res->hstmt)); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); } /* get the string and its length */ if (!NIL_P(stmt)) { stmt_string=(SQLCHAR*)rb_str2cstr(stmt, &stmt_string_len); } else { rb_throw("Supplied parameter is invalid", Qnil); } if ( rc < SQL_SUCCESS ) { _ruby_ibm_db_check_sql_errors(hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return rc; } if (!NIL_P(options)) { rc = _ruby_ibm_db_parse_options( options, SQL_HANDLE_STMT, stmt_res ); if ( rc == SQL_ERROR ) { rb_throw("Options Array must have string indexes", Qnil); } } /* Prepare the stmt. The cursor type requested has already been set in _ruby_ibm_db_assign_options */ rc = SQLPrepare((SQLHSTMT)stmt_res->hstmt, stmt_string, (SQLINTEGER)stmt_string_len); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); } return rc; } /* */ /* * IBM_DB::exec -- Executes an SQL statement directly * ===Description * resource IBM_DB::exec ( resource connection, string statement [, array options] ) * * Prepares and executes an SQL statement. * * If you plan to interpolate Ruby variables into the SQL statement, understand that this is one of * the more common security exposures. Consider calling IBM_DB::prepare() to prepare an SQL statement with * parameter markers for input values. Then you can call IBM_DB::execute() to pass in the input values * and avoid SQL injection attacks. * * If you plan to repeatedly issue the same SQL statement with different parameters, consider calling * IBM_DB::prepare() and IBM_DB::execute() to enable the database server to reuse its access plan and increase * the efficiency of your database access. * ===Parameters * * connection * A valid database connection resource variable as returned from IBM_DB::connect() or IBM_DB::pconnect(). * * statement * An SQL statement. The statement cannot contain any parameter markers. * * options * An associative array containing statement options. You can use this parameter to request * a scrollable cursor on database servers that support this functionality. * * cursor * Passing the SQL_SCROLL_FORWARD_ONLY value requests a forward-only cursor for this SQL statement. * This is the default type of cursor, and it is supported by all database servers. * It is also much faster than a scrollable cursor. * Passing the SQL_CURSOR_KEYSET_DRIVEN value requests a scrollable cursor for this SQL statement. * This type of cursor enables you to fetch rows non-sequentially from the database server. * However, it is only supported by DB2 servers, and is much slower than forward-only cursors. * * ===Return Values * * Returns a statement resource if the SQL statement was issued successfully, or FALSE if the database failed to execute the SQL statement. */ VALUE ibm_db_exec(int argc, VALUE *argv, VALUE self) { VALUE stmt = Qnil; VALUE connection = Qnil; VALUE options = Qnil; stmt_handle *stmt_res; conn_handle *conn_res; int rc; long stmt_string_len; SQLCHAR *stmt_string = NULL; char* return_str = NULL; /* This variable is used by _ruby_ibm_db_check_sql_errors to return err strings */ SQLINTEGER vParam; /* This function basically is a wrap of the _ruby_ibm_db_do_prepare and _ruby_ibm_db_execute_stmt */ /* After completing statement execution, it returns the statement resource */ rb_scan_args(argc, argv, "21", &connection, &stmt, &options); if (!NIL_P(stmt)) { stmt_string=(SQLCHAR*)rb_str2cstr(stmt, &stmt_string_len); } else { rb_throw("Supplied parameter is invalid", Qnil); } if (!NIL_P(connection)) { Data_Get_Struct(connection, conn_handle, conn_res); if (!conn_res->handle_active) { rb_throw("Connection is not active", Qnil); } return_str = ALLOC_N(char, DB2_MAX_ERR_MSG_LEN); memset(return_str, 0, DB2_MAX_ERR_MSG_LEN); _ruby_ibm_db_clear_stmt_err_cache(); stmt_res = _ibm_db_new_stmt_struct(conn_res); /* Allocates the stmt handle */ /* returns the stat_handle back to the calling function */ rc = SQLAllocHandle(SQL_HANDLE_STMT, conn_res->hdbc, &(stmt_res->hstmt)); if ( rc < SQL_SUCCESS ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); ruby_xfree(return_str); return rc; } if (!NIL_P(options)) { rc = _ruby_ibm_db_parse_options( options, SQL_HANDLE_STMT, stmt_res ); if ( rc == SQL_ERROR ) { rb_throw("Options Array must have string indexes", Qnil); } } rc = SQLExecDirect((SQLHSTMT)stmt_res->hstmt, stmt_string, (SQLINTEGER)stmt_string_len); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, -1, 1, return_str, DB2_ERRMSG, stmt_res->errormsg_recno_tracker); } if ( rc < SQL_SUCCESS ) { _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, -1, 0, return_str, DB2_ERRMSG, stmt_res->errormsg_recno_tracker); SQLFreeHandle( SQL_HANDLE_STMT, stmt_res->hstmt ); ruby_xfree(stmt_res); ruby_xfree(return_str); return Qfalse; } ruby_xfree(return_str); return Data_Wrap_Struct(le_stmt_struct, _ruby_ibm_db_mark_stmt_struct, _ruby_ibm_db_free_stmt_struct, stmt_res); } return Qnil; } /* */ /* * IBM_DB::free_result -- Frees resources associated with a result set * * ===Description * bool IBM_DB::free_result ( resource stmt ) * * Frees the system and database resources that are associated with a result set. These resources * are freed implicitly when a script finishes, but you can call IBM_DB::free_result() to explicitly * free the result set resources before the end of the script. * * ===Parameters * * stmt * A valid statement resource. * * ===Return Values * * Returns TRUE on success or FALSE on failure. */ VALUE ibm_db_free_result(int argc, VALUE *argv, VALUE self) { VALUE stmt = Qnil; stmt_handle *stmt_res; int rc = 0; rb_scan_args(argc, argv, "1", &stmt); if (!NIL_P(stmt)) { Data_Get_Struct(stmt, stmt_handle, stmt_res); if ( stmt_res->hstmt ) { /* Free any cursors that might have been allocated in a previous call to SQLExecute */ rc = SQLFreeHandle( SQL_HANDLE_STMT, stmt_res->hstmt); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); } stmt_res->hstmt = 0; } _ruby_ibm_db_free_result_struct(stmt_res); } else { rb_throw("Supplied parameter is invalid", Qnil); } return Qtrue; } /* */ /* * IBM_DB::prepare -- Prepares an SQL statement to be executed * * ===Description * resource IBM_DB::prepare ( resource connection, string statement [, array options] ) * * IBM_DB::prepare() creates a prepared SQL statement which can include 0 or more parameter markers * (? characters) representing parameters for input, output, or input/output. You can pass parameters * to the prepared statement using IBM_DB::bind_param(), or for input values only, as an array passed to * IBM_DB::execute(). * * There are three main advantages to using prepared statements in your application: * * Performance: when you prepare a statement, the database server creates an optimized access plan * for retrieving data with that statement. Subsequently issuing the prepared statement with * IBM_DB::execute() enables the statements to reuse that access plan and avoids the overhead of dynamically * creating a new access plan for every statement you issue. * * Security: when you prepare a statement, you can include parameter markers for input values. * When you execute a prepared statement with input values for placeholders, the database server checks * each input value to ensure that the type matches the column definition or parameter definition. * * Advanced functionality: Parameter markers not only enable you to pass input values to prepared * SQL statements, they also enable you to retrieve OUT and INOUT parameters from stored procedures * using IBM_DB::bind_param(). * * ===Parameters * connection * A valid database connection resource variable as returned from IBM_DB::connect() or IBM_DB::pconnect(). * * statement * An SQL statement, optionally containing one or more parameter markers.. * * options * An associative array containing statement options. You can use this parameter to request a * scrollable cursor on database servers that support this functionality. * * cursor * Passing the SQL_SCROLL_FORWARD_ONLY value requests a forward-only cursor for this SQL statement. * This is the default type of cursor, and it is supported by all database servers. It is also * much faster than a scrollable cursor. * Passing the SQL_CURSOR_KEYSET_DRIVEN value requests a scrollable cursor for this SQL statement. * This type of cursor enables you to fetch rows non-sequentially from the database server. * However, it is only supported by DB2 servers, and is much slower than forward-only cursors. * * ===Return Values * * Returns a statement resource if the SQL statement was successfully parsed and prepared by the database server. Returns FALSE if the database server returned an error. You can determine which error was returned by calling IBM_DB::stmt_error() or IBM_DB::stmt_errormsg(). */ VALUE ibm_db_prepare(int argc, VALUE *argv, VALUE self) { VALUE stmt = Qnil; VALUE connection = Qnil; VALUE options = Qnil; conn_handle *conn_res; stmt_handle *stmt_res; int rc; char error[DB2_MAX_ERR_MSG_LEN]; rb_scan_args(argc, argv, "21", &connection, &stmt, &options); if (!NIL_P(connection)) { Data_Get_Struct(connection, conn_handle, conn_res); if (!conn_res->handle_active) { rb_throw("Connection is not active", Qnil); } _ruby_ibm_db_clear_stmt_err_cache(); /* Initialize stmt resource members with default values. */ /* Parsing will update options if needed */ stmt_res = _ibm_db_new_stmt_struct(conn_res); /* Allocates the stmt handle */ /* Prepares the statement */ /* returns the stat_handle back to the calling function */ rc = _ruby_ibm_db_do_prepare(conn_res->hdbc, stmt, stmt_res, options); if ( rc < SQL_SUCCESS ) { sprintf(error, "Statement Prepare Failed: %s", IBM_DB_G(__ruby_stmt_err_msg)); rb_throw(error, Qnil); return Qfalse; } return Data_Wrap_Struct(le_stmt_struct, _ruby_ibm_db_mark_stmt_struct, _ruby_ibm_db_free_stmt_struct, stmt_res); } return Qnil; } /* */ /* static param_node* build_list( stmt_res, param_no, data_type, precision, scale, nullable ) */ static param_node* build_list( stmt_handle *stmt_res, int param_no, SQLSMALLINT data_type, SQLUINTEGER precision, SQLSMALLINT scale, SQLSMALLINT nullable ) { param_node *tmp_curr = NULL, *curr = stmt_res->head_cache_list, *prev = NULL; /* Allocate memory and make new node to be added */ tmp_curr = ALLOC(param_node); memset(tmp_curr,0,sizeof(param_node)); /* assign values */ tmp_curr->data_type = data_type; tmp_curr->param_size = precision; tmp_curr->nullable = nullable; tmp_curr->scale = scale; tmp_curr->param_num = param_no; tmp_curr->file_options = SQL_FILE_READ; tmp_curr->param_type = SQL_PARAM_INPUT; while ( curr != NULL ) { prev = curr; curr = curr->next; } if (stmt_res->head_cache_list == NULL) { stmt_res->head_cache_list = tmp_curr; } else { prev->next = tmp_curr; } tmp_curr->next = curr; return tmp_curr; } /* */ /* static int _ruby_ibm_db_bind_data( stmt_handle *stmt_res, param_node *curr, VALUE *bind_data ) */ static int _ruby_ibm_db_bind_data( stmt_handle *stmt_res, param_node *curr, VALUE *bind_data) { int rc; SQLSMALLINT valueType; SQLPOINTER paramValuePtr; int nullterm = 0; int origlen = 0; /* Have to use SQLBindFileToParam if PARAM is type PARAM_FILE */ if ( curr->param_type == PARAM_FILE) { /* Only string types can be bound */ if ( TYPE(*bind_data) != T_STRING) { return SQL_ERROR; } curr->bind_indicator = 0; curr->svalue = rb_str2cstr(*bind_data, &curr->ivalue); valueType = curr->ivalue; /* Bind file name string */ rc = SQLBindFileToParam((SQLHSTMT)stmt_res->hstmt, curr->param_num, curr->data_type, (SQLCHAR*)curr->svalue, (SQLSMALLINT*)&(curr->ivalue), &(curr->file_options), curr->ivalue, &(curr->bind_indicator)); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); } return rc; } switch(TYPE(*bind_data)) { case T_FIXNUM: curr->ivalue = FIX2INT(*bind_data); rc = SQLBindParameter(stmt_res->hstmt, curr->param_num, curr->param_type, SQL_C_LONG, curr->data_type, curr->param_size, curr->scale, &curr->ivalue, 0, NULL); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); } curr->data_type = SQL_C_LONG; break; /* Convert BOOLEAN types to LONG for DB2 / Cloudscape */ case T_FALSE: curr->ivalue = 0; rc = SQLBindParameter(stmt_res->hstmt, curr->param_num, curr->param_type, SQL_C_LONG, curr->data_type, curr->param_size, curr->scale, &curr->ivalue, 0, NULL); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); } curr->data_type = SQL_C_LONG; break; case T_TRUE: curr->ivalue = 1; rc = SQLBindParameter(stmt_res->hstmt, curr->param_num, curr->param_type, SQL_C_LONG, curr->data_type, curr->param_size, curr->scale, &curr->ivalue, 0, NULL); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); } curr->data_type = SQL_C_LONG; break; case T_FLOAT: curr->fvalue = NUM2DBL(*bind_data); rc = SQLBindParameter(stmt_res->hstmt, curr->param_num, curr->param_type, SQL_C_DOUBLE, curr->data_type, curr->param_size, curr->scale, &curr->fvalue, 0, NULL); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); } curr->data_type = SQL_C_DOUBLE; break; case T_STRING: { int origlen = 0; curr->svalue = rb_str2cstr(*bind_data, &curr->ivalue); origlen = curr->ivalue; /* * An extra parameter is given by the client to pick the size of the string * returned. The string is then truncate past that size. If no size is * given then use BUFSIZ to return the string. */ if (curr->size != 0) { curr->ivalue = curr->size; } if (curr->param_type == SQL_PARAM_OUTPUT || curr->param_type == SQL_PARAM_INPUT_OUTPUT) { if (curr->size == 0) { if (curr->ivalue < curr->param_size) { curr->ivalue = curr->param_size; } } curr->svalue = memcpy(ALLOC_N(char, curr->ivalue+1), curr->svalue, origlen); curr->svalue[origlen] = '\0'; } else { curr->svalue = memcpy(ALLOC_N(char, curr->ivalue+1), curr->svalue, curr->ivalue); curr->svalue[curr->ivalue] = '\0'; } } switch ( curr->data_type ) { case SQL_CLOB: if (curr->param_type == SQL_PARAM_OUTPUT || curr->param_type == SQL_PARAM_INPUT_OUTPUT) { curr->bind_indicator = curr->ivalue; valueType = SQL_C_CHAR; paramValuePtr = (SQLPOINTER)curr->svalue; } else { curr->bind_indicator = SQL_DATA_AT_EXEC; valueType = SQL_C_CHAR; /* The correct dataPtr will be set during SQLPutData with the len from this struct */ #ifndef PASE paramValuePtr = (SQLPOINTER)(curr); #else paramValuePtr = (SQLPOINTER)&(curr); #endif } break; case SQL_BLOB: if (curr->param_type == SQL_PARAM_OUTPUT || curr->param_type == SQL_PARAM_INPUT_OUTPUT) { curr->bind_indicator = curr->ivalue; valueType = SQL_C_BINARY; paramValuePtr = (SQLPOINTER)curr; } else { curr->bind_indicator = SQL_DATA_AT_EXEC; valueType = SQL_C_BINARY; #ifndef PASE paramValuePtr = (SQLPOINTER)(curr); #else paramValuePtr = (SQLPOINTER)&(curr); #endif } break; case SQL_BINARY: #ifndef PASE /* i5/OS SQL_LONGVARBINARY is SQL_VARBINARY */ case SQL_LONGVARBINARY: #endif /* PASE */ case SQL_VARBINARY: case SQL_XML: /* account for bin_mode settings as well */ curr->bind_indicator = curr->ivalue; valueType = SQL_C_BINARY; paramValuePtr = (SQLPOINTER)curr->svalue; break; /* This option should handle most other types such as DATE, VARCHAR etc */ default: valueType = SQL_C_CHAR; curr->bind_indicator = curr->ivalue; if (curr->param_type == SQL_PARAM_OUTPUT || curr->param_type == SQL_PARAM_INPUT_OUTPUT) { curr->bind_indicator = SQL_NTS; } paramValuePtr = (SQLPOINTER)(curr->svalue); } rc = SQLBindParameter(stmt_res->hstmt, curr->param_num, curr->param_type, valueType, curr->data_type, curr->param_size, curr->scale, paramValuePtr, curr->ivalue, &(curr->bind_indicator)); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); } curr->data_type = valueType; break; case T_NIL: curr->ivalue = SQL_NULL_DATA; curr->bind_indicator = SQL_NULL_DATA; rc = SQLBindParameter(stmt_res->hstmt, curr->param_num, curr->param_type, SQL_C_DEFAULT, curr->data_type, curr->param_size, curr->scale, &curr->ivalue, 0, &curr->ivalue); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); } break; default: return SQL_ERROR; } return rc; } /* */ /* static int _ruby_ibm_db_execute_helper(stmt_res, data, int bind_cmp_list) */ static int _ruby_ibm_db_execute_helper(stmt_handle *stmt_res, VALUE *data, int bind_cmp_list, int bind_params) { int rc=SQL_SUCCESS; param_node *curr = NULL; /* To traverse the list */ VALUE bind_data; /* Data value from symbol table */ char error[DB2_MAX_ERR_MSG_LEN]; /* Used in call to SQLDescribeParam if needed */ SQLSMALLINT param_no; SQLSMALLINT data_type; SQLUINTEGER precision; SQLSMALLINT scale; SQLSMALLINT nullable; /* This variable means that we bind the complete list of params cached */ /* The values used are fetched from the active symbol table */ /* TODO: Enhance this part to check for stmt_res->file_param */ /* If this flag is set, then use SQLBindParam, else use SQLExtendedBind */ if ( bind_cmp_list ) { /* Bind the complete list sequentially */ /* Used when no parameters array is passed in */ curr = stmt_res->head_cache_list; while (curr != NULL ) { /* Fetch data from symbol table */ bind_data = rb_eval_string(curr->varname); rc = _ruby_ibm_db_bind_data( stmt_res, curr, &bind_data); if ( rc == SQL_ERROR ) { sprintf(error, "Binding Error 1: %s", IBM_DB_G(__ruby_stmt_err_msg)); rb_throw(error, Qnil); return rc; } curr = curr->next; } return 0; } else { /* Bind only the data value passed in to the Current Node */ if ( data != NULL ) { if ( bind_params ) { /* This condition applies if the parameter has not been bound using IBM_DB::bind_param. Need to describe the parameter and then bind it. */ param_no = ++stmt_res->num_params; rc = SQLDescribeParam((SQLHSTMT)stmt_res->hstmt, param_no, (SQLSMALLINT*)&data_type, &precision, (SQLSMALLINT*)&scale, (SQLSMALLINT*)&nullable); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); sprintf(error, "Describe Param Failed: %s", IBM_DB_G(__ruby_stmt_err_msg)); rb_throw(error, Qnil); return rc; } curr = build_list( stmt_res, param_no, data_type, precision, scale, nullable ); rc = _ruby_ibm_db_bind_data( stmt_res, curr, data); if ( rc == SQL_ERROR ) { sprintf(error, "Binding Error 2: %s", IBM_DB_G(__ruby_stmt_err_msg)); rb_throw(error, Qnil); return rc; } } else { /* This is always at least the head_cache_node -- assigned in IBM_DB::execute(), if params have been bound. */ curr = stmt_res->current_node; if ( curr != NULL ) { rc = _ruby_ibm_db_bind_data( stmt_res, curr, data); if ( rc == SQL_ERROR ) { sprintf(error, "Binding Error 2: %s", IBM_DB_G(__ruby_stmt_err_msg)); rb_throw(error, Qnil); return rc; } stmt_res->current_node = curr->next; } } return rc; } } return rc; } /* */ void var_assign(char *name, VALUE value) { #ifdef TODO /* this compiles and links, but fails to resolve at runtime */ rb_dvar_asgn(rb_intern(name_id), value); #else /* so we do it the hard way */ ID inspect; char *expr, *statement; long expr_len; inspect = rb_intern("inspect"); value = rb_funcall(value, inspect, 0); expr = rb_str2cstr(value, &expr_len); statement = ALLOC_N(char, strlen(name)+1+expr_len+1); strcpy(statement, name); strcat(statement, "="); strcat(statement, expr); rb_eval_string(statement); ruby_xfree(statement); #endif } /* * IBM_DB::execute -- Executes a prepared SQL statement * * ===Description * bool IBM_DB::execute ( resource stmt [, array parameters] ) * * IBM_DB::execute() executes an SQL statement that was prepared by IBM_DB::prepare(). * * If the SQL statement returns a result set, for example, a SELECT statement or a CALL to a stored procedure that returns one or more result sets, you can retrieve a row as an array from the stmt resource using IBM_DB::fetch_assoc(), IBM_DB::fetch_both(), or IBM_DB::fetch_array(). Alternatively, you can use IBM_DB::fetch_row() to move the result set pointer to the next row and fetch a column at a time from that row with IBM_DB::result(). * * Refer to IBM_DB::prepare() for a brief discussion of the advantages of using IBM_DB::prepare() and IBM_DB::execute() rather than IBM_DB::exec(). * * ===Parameters * stmt * A prepared statement returned from IBM_DB::prepare(). * * parameters * An array of input parameters matching any parameter markers contained in the prepared statement. * * ===Return Values * * Returns TRUE on success or FALSE on failure. */ VALUE ibm_db_execute(int argc, VALUE *argv, VALUE self) { VALUE stmt = Qnil; VALUE parameters_array = Qnil; stmt_handle *stmt_res; int rc, numOpts, i, bind_params = 0; char error[DB2_MAX_ERR_MSG_LEN]; SQLSMALLINT num; SQLPOINTER valuePtr; /* This is used to loop over the param cache */ param_node *tmp_curr, *prev_ptr, *curr_ptr; VALUE data; rb_scan_args(argc, argv, "11", &stmt, ¶meters_array); /* Get values from symbol tables */ /* Assign values into param nodes */ /* Check types/conversions */ /* Bind parameters */ /* Execute */ /* Return values back to symbol table for OUT params */ if (!NIL_P(stmt)) { Data_Get_Struct(stmt, stmt_handle, stmt_res); /* Free any cursors that might have been allocated in a previous call to SQLExecute */ SQLFreeStmt((SQLHSTMT)stmt_res->hstmt, SQL_CLOSE); /* This ensures that each call to IBM_DB::execute start from scratch */ stmt_res->current_node = stmt_res->head_cache_list; rc = SQLNumParams((SQLHSTMT)stmt_res->hstmt, (SQLSMALLINT*)&num); if ( num != 0 ) { /* Parameter Handling */ if ( !NIL_P(parameters_array) ) { /* Make sure IBM_DB::bind_param has been called */ /* If the param list is NULL -- ERROR */ if ( stmt_res->head_cache_list == NULL ) { bind_params = 1; } if (TYPE(parameters_array) != T_ARRAY) { rb_throw("Param is not an array", Qnil); return Qfalse; } numOpts = RARRAY(parameters_array)->len; if (numOpts > num) { /* More are passed in -- Warning - Use the max number present */ sprintf(error, "%d params bound not matching %d required", numOpts, num); rb_throw(error, Qnil); numOpts = stmt_res->num_params; } else if (numOpts < num) { /* If there are less params passed in, than are present -- Error */ sprintf(error, "%d params bound not matching %d required", numOpts, num); rb_throw(error, Qnil); return Qfalse; } for ( i = 0; i < numOpts; i++) { /* Bind values from the parameters_array to params */ data = rb_ary_entry(parameters_array,i); /* The 0 denotes that you work only with the current node. The 4th argument specifies whether the data passed in has been described. So we need to call SQLDescribeParam before binding depending on this. */ rc = _ruby_ibm_db_execute_helper(stmt_res, &data, 0, bind_params); if ( rc == SQL_ERROR) { sprintf(error, "Binding Error: %s", IBM_DB_G(__ruby_stmt_err_msg)); rb_throw(error, Qnil); return Qfalse; } } } else { /* No additional params passed in. Use values already bound. */ if ( num > stmt_res->num_params ) { /* More parameters than we expected */ sprintf(error, "%d params bound not matching %d required", stmt_res->num_params, num); rb_throw(error, Qnil); } else if ( num < stmt_res->num_params ) { /* Fewer parameters than we expected */ sprintf(error, "%d params bound not matching %d required", stmt_res->num_params, num); rb_throw(error, Qnil); return Qfalse; } /* Param cache node list is empty -- No params bound */ if ( stmt_res->head_cache_list == NULL ) { rb_throw("Parameters not bound", Qnil); return Qfalse; } else { /* The 1 denotes that you work with the whole list */ /* And bind sequentially */ rc = _ruby_ibm_db_execute_helper(stmt_res, NULL, 1, 0); if ( rc == SQL_ERROR ) { sprintf(error, "Binding Error 3: %s", IBM_DB_G(__ruby_stmt_err_msg)); rb_throw(error, Qnil); return Qfalse; } } } } else { /* No Parameters */ /* We just execute the statement. No additional work needed. */ rc = SQLExecute((SQLHSTMT)stmt_res->hstmt); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); sprintf(error, "Statement Execute Failed: %s", IBM_DB_G(__ruby_stmt_err_msg)); rb_throw(error, Qnil); return Qfalse; } return Qtrue; } /* Execute Stmt -- All parameters bound */ rc = SQLExecute((SQLHSTMT)stmt_res->hstmt); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); sprintf(error, "Statement Execute Failed: %s", IBM_DB_G(__ruby_stmt_err_msg)); rb_throw(error, Qnil); return Qfalse; } if ( rc == SQL_NEED_DATA ) { while ( (SQLParamData((SQLHSTMT)stmt_res->hstmt, (SQLPOINTER *)&valuePtr)) == SQL_NEED_DATA ) { /* passing data value for a parameter */ rc = SQLPutData((SQLHSTMT)stmt_res->hstmt, (SQLPOINTER)(((param_node*)valuePtr)->svalue), ((param_node*)valuePtr)->ivalue); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); sprintf(error, "Sending data failed: %s", IBM_DB_G(__ruby_stmt_err_msg)); rb_throw(error, Qnil); return Qfalse; } } } /* cleanup dynamic bindings if present */ if ( bind_params == 1 ) { /* Free param cache list */ curr_ptr = stmt_res->head_cache_list; prev_ptr = stmt_res->head_cache_list; while (curr_ptr != NULL) { curr_ptr = curr_ptr->next; /* Free Values */ if ( prev_ptr->svalue) { ruby_xfree(prev_ptr->svalue); } ruby_xfree(prev_ptr); prev_ptr = curr_ptr; } stmt_res->head_cache_list = NULL; stmt_res->num_params = 0; } else { /* Bind the IN/OUT Params back into the active symbol table */ tmp_curr = stmt_res->head_cache_list; while (tmp_curr != NULL) { switch(tmp_curr->param_type) { case SQL_PARAM_OUTPUT: case SQL_PARAM_INPUT_OUTPUT: if( (tmp_curr->bind_indicator != SQL_NULL_DATA && tmp_curr->bind_indicator != SQL_NO_TOTAL )){ switch (tmp_curr->data_type) { case SQL_SMALLINT: case SQL_INTEGER: case SQL_BIGINT: var_assign(tmp_curr->varname, INT2NUM(tmp_curr->ivalue)); break; case SQL_REAL: case SQL_FLOAT: case SQL_DOUBLE: case SQL_DECIMAL: case SQL_NUMERIC: var_assign(tmp_curr->varname, rb_float_new(tmp_curr->fvalue)); break; default: var_assign(tmp_curr->varname, rb_str_new2(tmp_curr->svalue)); break; } } default: break; } tmp_curr = tmp_curr->next; } } if ( rc != SQL_ERROR ) { return Qtrue; } } else { rb_throw("Supplied parameter is invalid", Qnil); } return Qnil; } /* */ /* * IBM_DB::conn_errormsg -- Returns the last connection error message and SQLCODE value * ===Description * string IBM_DB::conn_errormsg ( [resource connection] ) * * IBM_DB::conn_errormsg() returns an error message and SQLCODE value representing the reason * the last database connection attempt failed. As IBM_DB::connect() returns FALSE in the event * of a failed connection attempt, do not pass any parameters to IBM_DB::conn_errormsg() to retrieve * the associated error message and SQLCODE value. * * If, however, the connection was successful but becomes invalid over time, you can pass the connection * parameter to retrieve the associated error message and SQLCODE value for a specific connection. * ===Parameters * * connection * A connection resource associated with a connection that initially succeeded, but which over time * became invalid. * * ===Return Values * * Returns a string containing the error message and SQLCODE value resulting from a failed connection attempt. If there is no error associated with the last connection attempt, IBM_DB::conn_errormsg() returns an empty string. */ VALUE ibm_db_conn_errormsg(int argc, VALUE *argv, VALUE self) { VALUE connection = Qnil; conn_handle *conn_res; char* return_str = NULL; /* This variable is used by _ruby_ibm_db_check_sql_errors to return err strings */ rb_scan_args(argc, argv, "01", &connection); if (!NIL_P(connection)) { Data_Get_Struct(connection, conn_handle, conn_res); if (!conn_res->handle_active) { rb_throw("Connection is not active", Qnil); } return_str = ALLOC_N(char, DB2_MAX_ERR_MSG_LEN); memset(return_str, 0, DB2_MAX_ERR_MSG_LEN); _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, -1, 0, return_str, DB2_ERRMSG, conn_res->errormsg_recno_tracker); if(conn_res->errormsg_recno_tracker - conn_res->error_recno_tracker >= 1) conn_res->error_recno_tracker = conn_res->errormsg_recno_tracker; conn_res->errormsg_recno_tracker++; return rb_str_new2(return_str); } else { return rb_str_new2(IBM_DB_G(__ruby_conn_err_msg)); } } /* */ /* * IBM_DB::stmt_errormsg -- Returns a string containing the last SQL statement error message * * ===Description * string IBM_DB::stmt_errormsg ( [resource stmt] ) * * Returns a string containing the last SQL statement error message. * * If you do not pass a statement resource as an argument to IBM_DB::stmt_errormsg(), the driver returns the error message associated with the last attempt to return a statement resource, for example, from IBM_DB::prepare() or IBM_DB::exec(). * * ===Parameters * * stmt * A valid statement resource. * * ===Return Values * * Returns a string containing the error message and SQLCODE value for the last error that occurred issuing an SQL statement. */ VALUE ibm_db_stmt_errormsg(int argc, VALUE *argv, VALUE self) { VALUE stmt = Qnil; stmt_handle *stmt_res; char* return_str = NULL; /* This variable is used by _ruby_ibm_db_check_sql_errors to return err strings */ rb_scan_args(argc, argv, "01", &stmt); if (!NIL_P(stmt)) { Data_Get_Struct(stmt, stmt_handle, stmt_res); return_str = ALLOC_N(char, DB2_MAX_ERR_MSG_LEN); memset(return_str, 0, DB2_MAX_ERR_MSG_LEN); _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, -1, 0, return_str, DB2_ERRMSG, stmt_res->errormsg_recno_tracker); if(stmt_res->errormsg_recno_tracker - stmt_res->error_recno_tracker >= 1) stmt_res->error_recno_tracker = stmt_res->errormsg_recno_tracker; stmt_res->errormsg_recno_tracker++; return rb_str_new2(return_str); } else { return rb_str_new2(IBM_DB_G(__ruby_stmt_err_msg)); } } /* */ /* * IBM_DB::conn_error -- Returns a string containing the SQLSTATE returned by the last connection attempt * ===Description * string IBM_DB::conn_error ( [resource connection] ) * * IBM_DB::conn_error() returns an SQLSTATE value representing the reason the last attempt to connect * to a database failed. As IBM_DB::connect() returns FALSE in the event of a failed connection attempt, * you do not pass any parameters to IBM_DB::conn_error() to retrieve the SQLSTATE value. * * If, however, the connection was successful but becomes invalid over time, you can pass the connection * parameter to retrieve the SQLSTATE value for a specific connection. * * To learn what the SQLSTATE value means, you can issue the following command at a DB2 Command Line * Processor prompt: db2 '? sqlstate-value'. You can also call IBM_DB::conn_errormsg() to retrieve * an explicit error message and the associated SQLCODE value. * * ===Parameters * * connection * A connection resource associated with a connection that initially succeeded, but which over time * became invalid. * * ===Return Values * * Returns the SQLSTATE value resulting from a failed connection attempt. * Returns an empty string if there is no error associated with the last connection attempt. */ VALUE ibm_db_conn_error(int argc, VALUE *argv, VALUE self) { VALUE connection = Qnil; conn_handle *conn_res; char *return_str = NULL; /* This variable is used by _ruby_ibm_db_check_sql_errors to return err strings */ rb_scan_args(argc, argv, "01", &connection); if (!NIL_P(connection)) { Data_Get_Struct(connection, conn_handle, conn_res); return_str = ALLOC_N(char, SQL_SQLSTATE_SIZE + 1); memset(return_str, 0, SQL_SQLSTATE_SIZE + 1); _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, -1, 0, return_str, DB2_ERR, conn_res->error_recno_tracker); if (conn_res->error_recno_tracker - conn_res->errormsg_recno_tracker >= 1) { conn_res->errormsg_recno_tracker = conn_res->error_recno_tracker; } conn_res->error_recno_tracker++; return rb_str_new2(return_str); } else { return rb_str_new2(IBM_DB_G(__ruby_conn_err_state)); } } /* */ /* * IBM_DB::stmt_error -- Returns a string containing the SQLSTATE returned by an SQL statement * * ===Description * string IBM_DB::stmt_error ( [resource stmt] ) * * Returns a string containing the SQLSTATE value returned by an SQL statement. * * If you do not pass a statement resource as an argument to IBM_DB::stmt_error(), the driver returns the SQLSTATE value associated with the last attempt to return a statement resource, for example, from IBM_DB::prepare() or IBM_DB::exec(). * * To learn what the SQLSTATE value means, you can issue the following command at a DB2 Command Line Processor prompt: db2 '? sqlstate-value'. You can also call IBM_DB::stmt_errormsg() to retrieve an explicit error message and the associated SQLCODE value. * * ===Parameters * * stmt * A valid statement resource. * * ===Return Values * * Returns a string containing an SQLSTATE value. */ VALUE ibm_db_stmt_error(int argc, VALUE *argv, VALUE self) { VALUE stmt = Qnil; stmt_handle *stmt_res; char* return_str = NULL; /* This variable is used by _ruby_ibm_db_check_sql_errors to return err strings */ rb_scan_args(argc, argv, "01", &stmt); if (!NIL_P(stmt)) { Data_Get_Struct(stmt, stmt_handle, stmt_res); return_str = ALLOC_N(char, DB2_MAX_ERR_MSG_LEN); memset(return_str, 0, DB2_MAX_ERR_MSG_LEN); _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, -1, 0, return_str, DB2_ERR, stmt_res->error_recno_tracker); if (stmt_res->error_recno_tracker - stmt_res->errormsg_recno_tracker >= 1) { stmt_res->errormsg_recno_tracker = stmt_res->error_recno_tracker; } stmt_res->error_recno_tracker++; return rb_str_new2(return_str); } else { return rb_str_new2(IBM_DB_G(__ruby_stmt_err_state)); } } /* */ /* * IBM_DB::next_result -- Requests the next result set from a stored procedure * * ===Description * resource IBM_DB::next_result ( resource stmt ) * * A stored procedure can return zero or more result sets. While you handle the first result set in exactly the same way you would handle the results returned by a simple SELECT statement, to fetch the second and subsequent result sets from a stored procedure you must call the IBM_DB::next_result() function and return the result to a uniquely named Ruby variable. * * ===Parameters * stmt * A prepared statement returned from IBM_DB::exec() or IBM_DB::execute(). * * ===Return Values * * Returns a new statement resource containing the next result set if the stored procedure returned another result set. Returns FALSE if the stored procedure did not return another result set. */ VALUE ibm_db_next_result(int argc, VALUE *argv, VALUE self) { VALUE stmt = Qnil; stmt_handle *stmt_res, *new_stmt_res=NULL; int rc = 0; SQLHANDLE new_hstmt; rb_scan_args(argc, argv, "01", &stmt); if (!NIL_P(stmt)) { Data_Get_Struct(stmt, stmt_handle, stmt_res); _ruby_ibm_db_clear_stmt_err_cache(); /* alloc handle and return only if it errors */ rc = SQLAllocHandle(SQL_HANDLE_STMT, stmt_res->hdbc, &new_hstmt); if ( rc < SQL_SUCCESS ) { _ruby_ibm_db_check_sql_errors(stmt_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } rc = SQLNextResult((SQLHSTMT)stmt_res->hstmt, (SQLHSTMT)new_hstmt); if( rc != SQL_SUCCESS ) { if(rc < SQL_SUCCESS) { _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); } SQLFreeHandle(SQL_HANDLE_STMT, new_hstmt); return Qfalse; } /* Initialize stmt resource members with default values. */ /* Parsing will update options if needed */ new_stmt_res = ALLOC(stmt_handle); memset(new_stmt_res, 0, sizeof(stmt_handle)); new_stmt_res->s_bin_mode = stmt_res->s_bin_mode; new_stmt_res->cursor_type = stmt_res->cursor_type; new_stmt_res->s_case_mode = stmt_res->s_case_mode; new_stmt_res->head_cache_list = NULL; new_stmt_res->current_node = NULL; new_stmt_res->num_params = 0; new_stmt_res->file_param = 0; new_stmt_res->column_info = NULL; new_stmt_res->num_columns = 0; new_stmt_res->row_data = NULL; new_stmt_res->hstmt = new_hstmt; new_stmt_res->hdbc = stmt_res->hdbc; return Data_Wrap_Struct(le_stmt_struct, _ruby_ibm_db_mark_stmt_struct, _ruby_ibm_db_free_stmt_struct, new_stmt_res); } else { rb_throw("Supplied parameter is invalid", Qnil); return Qfalse; } } /* */ /* * IBM_DB::num_fields -- Returns the number of fields contained in a result set * * ===Description * int IBM_DB::num_fields ( resource stmt ) * * Returns the number of fields contained in a result set. This is most useful for handling the * result sets returned by dynamically generated queries, or for result sets returned by stored procedures, * where your application cannot otherwise know how to retrieve and use the results. * * ===Parameters * * stmt * A valid statement resource containing a result set. * * ===Return Values * * Returns an integer value representing the number of fields in the result set associated with the * specified statement resource. Returns FALSE if the statement resource is not a valid input value. */ VALUE ibm_db_num_fields(int argc, VALUE *argv, VALUE self) { VALUE stmt = Qnil; stmt_handle *stmt_res; int rc = 0; SQLSMALLINT indx = 0; char error[DB2_MAX_ERR_MSG_LEN]; rb_scan_args(argc, argv, "1", &stmt); if (!NIL_P(stmt)) { Data_Get_Struct(stmt, stmt_handle, stmt_res); rc = SQLNumResultCols((SQLHSTMT)stmt_res->hstmt, &indx); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); sprintf(error, "SQLNumResultCols failed: %s", IBM_DB_G(__ruby_stmt_err_msg)); rb_throw(error, Qnil); return Qfalse; } return INT2NUM(indx); } else { rb_throw("Supplied parameter is invalid", Qnil); } return Qnil; } /* */ /* * IBM_DB::num_rows -- Returns the number of rows affected by an SQL statement * * ===Description * int IBM_DB::num_rows ( resource stmt ) * * Returns the number of rows deleted, inserted, or updated by an SQL statement. * * To determine the number of rows that will be returned by a SELECT statement, issue SELECT COUNT(*) * with the same predicates as your intended SELECT statement and retrieve the value. * If your application logic checks the number of rows returned by a SELECT statement and branches * if the number of rows is 0, consider modifying your application to attempt to return the first row * with one of IBM_DB::fetch_assoc(), IBM_DB::fetch_both(), IBM_DB::fetch_array(), or IBM_DB::fetch_row(), and branch * if the fetch function returns FALSE. * * Note: If you issue a SELECT statement using a scrollable cursor, IBM_DB::num_rows() returns the * number of rows returned by the SELECT statement. However, the overhead associated with scrollable cursors * significantly degrades the performance of your application, so if this is the only reason you are * considering using scrollable cursors, you should use a forward-only cursor and either * call SELECT COUNT(*) or rely on the boolean return value of the fetch functions to achieve the * equivalent functionality with much better performance. * * ===Parameters * * stmt * A valid stmt resource containing a result set. * * ===Return Values * * Returns the number of rows affected by the last SQL statement issued by the specified statement handle. */ VALUE ibm_db_num_rows(int argc, VALUE *argv, VALUE self) { VALUE stmt = Qnil; stmt_handle *stmt_res; int rc = 0; SQLINTEGER count = 0; char error[DB2_MAX_ERR_MSG_LEN]; rb_scan_args(argc, argv, "1", &stmt); if (!NIL_P(stmt)) { Data_Get_Struct(stmt, stmt_handle, stmt_res); rc = SQLRowCount((SQLHSTMT)stmt_res->hstmt, &count); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); sprintf(error, "SQLRowCount failed: %s", IBM_DB_G(__ruby_stmt_err_msg)); rb_throw(error, Qnil); return Qfalse; } return INT2NUM(count); } else { rb_throw("Supplied parameter is invalid", Qnil); } return Qnil; } /* */ /* static int _ruby_ibm_db_get_column_by_name(stmt_handle *stmt_res, char *col_name, int col) */ static int _ruby_ibm_db_get_column_by_name(stmt_handle *stmt_res, char *col_name, int col) { int i; /* get column header info*/ if ( stmt_res->column_info == NULL ) { if (_ruby_ibm_db_get_result_set_info(stmt_res)<0) { return -1; } } if ( col_name == NULL ) { if ( col >= 0 && col < stmt_res->num_columns) { return col; } else { return -1; } } /* should start from 0 */ i=0; while (i < stmt_res->num_columns) { if (strcmp((char*)stmt_res->column_info[i].name,col_name) == 0) { return i; } i++; } return -1; } /* */ /* * IBM_DB::field_name -- Returns the name of the column in the result set * * ===Description * string IBM_DB::field_name ( resource stmt, mixed column ) * * Returns the name of the specified column in the result set. * * ===Parameters * * stmt * Specifies a statement resource containing a result set. * * column * Specifies the column in the result set. This can either be an integer representing the 0-indexed position of the column, or a string containing the name of the column. * * ===Return Values * * Returns a string containing the name of the specified column. If the specified column does not exist in the result set, IBM_DB::field_name() returns FALSE. */ VALUE ibm_db_field_name(int argc, VALUE *argv, VALUE self) { VALUE stmt = Qnil; VALUE column = Qnil; stmt_handle* stmt_res = NULL; char *col_name = NULL; int col = -1; rb_scan_args(argc, argv, "2", &stmt, &column); if (!NIL_P(stmt)) { Data_Get_Struct(stmt, stmt_handle, stmt_res); } else { rb_throw("Supplied parameter is invalid", Qnil); } if ( TYPE(column)==T_FIXNUM ) { col = FIX2LONG(column); } else if (RTEST(column)) { col_name = STR2CSTR(column); } col = _ruby_ibm_db_get_column_by_name(stmt_res,col_name, col); if ( col < 0 ) { return Qfalse; } return rb_str_new2((char*)stmt_res->column_info[col].name); } /* */ /* * IBM_DB::field_display_size -- Returns the maximum number of bytes required to display a column * * ===Description * int IBM_DB::field_display_size ( resource stmt, mixed column ) * * Returns the maximum number of bytes required to display a column in a result set. * * ===Parameters * stmt * Specifies a statement resource containing a result set. * * column * Specifies the column in the result set. This can either be an integer representing the * 0-indexed position of the column, or a string containing the name of the column. * * ===Return Values * * Returns an integer value with the maximum number of bytes required to display the specified column. * If the column does not exist in the result set, IBM_DB::field_display_size() returns FALSE. */ VALUE ibm_db_field_display_size(int argc, VALUE *argv, VALUE self) { VALUE stmt = Qnil; VALUE column = Qnil; int col =- 1; char *col_name = NULL; stmt_handle *stmt_res = NULL; int rc; SQLINTEGER colDataDisplaySize; rb_scan_args(argc, argv, "2", &stmt, &column); if (!NIL_P(stmt)) { Data_Get_Struct(stmt, stmt_handle, stmt_res); } else { rb_throw("Supplied parameter is invalid", Qnil); } if ( TYPE(column)==T_FIXNUM ) { col = FIX2LONG(column); } else if (RTEST(column)) { col_name = STR2CSTR(column); } col = _ruby_ibm_db_get_column_by_name(stmt_res,col_name, col); if ( col < 0 ) { return Qfalse; } rc = SQLColAttributes((SQLHSTMT)stmt_res->hstmt,(SQLSMALLINT)col+1, SQL_DESC_DISPLAY_SIZE,NULL,0, NULL,&colDataDisplaySize); if ( rc < SQL_SUCCESS ) { _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); return Qfalse; } return INT2NUM(colDataDisplaySize); } /* */ /* * IBM_DB::field_num -- Returns the position of the named column in a result set * * ===Description * int IBM_DB::field_num ( resource stmt, mixed column ) * * Returns the position of the named column in a result set. * * ===Parameters * * stmt * Specifies a statement resource containing a result set. * * column * Specifies the column in the result set. This can either be an integer representing the * 0-indexed position of the column, or a string containing the name of the column. * * ===Return Values * * Returns an integer containing the 0-indexed position of the named column in the result set. * If the specified column does not exist in the result set, IBM_DB::field_num() returns FALSE. */ VALUE ibm_db_field_num(int argc, VALUE *argv, VALUE self) { VALUE stmt = Qnil; VALUE column = Qnil; stmt_handle* stmt_res = NULL; char *col_name = NULL; int col = -1; rb_scan_args(argc, argv, "2", &stmt, &column); if (!NIL_P(stmt)) { Data_Get_Struct(stmt, stmt_handle, stmt_res); } else { rb_throw("Supplied parameter is invalid", Qnil); } if ( TYPE(column)==T_FIXNUM ) { col = FIX2LONG(column); } else if (RTEST(column)) { col_name = STR2CSTR(column); } col = _ruby_ibm_db_get_column_by_name(stmt_res,col_name, col); if ( col < 0 ) { return Qfalse; } return INT2NUM(col); } /* */ /* * IBM_DB::field_precision -- Returns the precision of the indicated column in a result set * * ===Description * int IBM_DB::field_precision ( resource stmt, mixed column ) * * Returns the precision of the indicated column in a result set. * * ===Parameters * * stmt * Specifies a statement resource containing a result set. * * column * Specifies the column in the result set. This can either be an integer representing the * 0-indexed position of the column, or a string containing the name of the column. * * ===Return Values * * Returns an integer containing the precision of the specified column. If the specified column * does not exist in the result set, IBM_DB::field_precision() returns FALSE. */ VALUE ibm_db_field_precision(int argc, VALUE *argv, VALUE self) { VALUE stmt = Qnil; VALUE column = Qnil; stmt_handle* stmt_res = NULL; char *col_name = NULL; int col = -1; rb_scan_args(argc, argv, "2", &stmt, &column); if (!NIL_P(stmt)) { Data_Get_Struct(stmt, stmt_handle, stmt_res); } else { rb_throw("Supplied parameter is invalid", Qnil); } if ( TYPE(column)==T_FIXNUM ) { col = FIX2LONG(column); } else if (RTEST(column)) { col_name = STR2CSTR(column); } col = _ruby_ibm_db_get_column_by_name(stmt_res,col_name, col); if ( col < 0 ) { return Qfalse; } return INT2NUM(stmt_res->column_info[col].size); } /* */ /* * IBM_DB::field_scale -- Returns the scale of the indicated column in a result set * * ===Description * int IBM_DB::field_scale ( resource stmt, mixed column ) * * Returns the scale of the indicated column in a result set. * * ===Parameters * stmt * Specifies a statement resource containing a result set. * * column * Specifies the column in the result set. This can either be an integer representing the * 0-indexed position of the column, or a string containing the name of the column. * * ===Return Values * * Returns an integer containing the scale of the specified column. If the specified column * does not exist in the result set, IBM_DB::field_scale() returns FALSE. */ VALUE ibm_db_field_scale(int argc, VALUE *argv, VALUE self) { VALUE stmt = Qnil; VALUE column = Qnil; stmt_handle* stmt_res = NULL; char *col_name = NULL; int col = -1; rb_scan_args(argc, argv, "2", &stmt, &column); if (!NIL_P(stmt)) { Data_Get_Struct(stmt, stmt_handle, stmt_res); } else { rb_throw("Supplied parameter is invalid", Qnil); } if ( TYPE(column)==T_FIXNUM ) { col = FIX2LONG(column); } else if (RTEST(column)) { col_name = STR2CSTR(column); } col = _ruby_ibm_db_get_column_by_name(stmt_res,col_name, col); if ( col < 0 ) { return Qfalse; } return INT2NUM(stmt_res->column_info[col].scale); } /* */ /* * IBM_DB::field_type -- Returns the data type of the indicated column in a result set * * ===Description * string IBM_DB::field_type ( resource stmt, mixed column ) * * Returns the data type of the indicated column in a result set. * * ===Parameters * stmt * Specifies a statement resource containing a result set. * * column * Specifies the column in the result set. This can either be an integer representing the * 0-indexed position of the column, or a string containing the name of the column. * * ===Return Values * * Returns a string containing the defined data type of the specified column. If the specified column * does not exist in the result set, IBM_DB::field_type() returns FALSE. */ VALUE ibm_db_field_type(int argc, VALUE *argv, VALUE self) { VALUE stmt = Qnil; VALUE column = Qnil; stmt_handle* stmt_res = NULL; char *col_name = NULL; char *str_val = ""; int col = -1; rb_scan_args(argc, argv, "2", &stmt, &column); if (!NIL_P(stmt)) { Data_Get_Struct(stmt, stmt_handle, stmt_res); } else { rb_throw("Supplied parameter is invalid", Qnil); } if ( TYPE(column)==T_FIXNUM ) { col = FIX2LONG(column); } else if (RTEST(column)) { col_name = STR2CSTR(column); } col = _ruby_ibm_db_get_column_by_name(stmt_res,col_name, col); if ( col < 0 ) { return Qfalse; } col = _ruby_ibm_db_get_column_by_name(stmt_res,col_name, col); if ( col < 0 ) { return Qfalse; } switch (stmt_res->column_info[col].type) { case SQL_SMALLINT: case SQL_INTEGER: case SQL_BIGINT: str_val = "int"; break; case SQL_REAL: case SQL_FLOAT: case SQL_DOUBLE: case SQL_DECIMAL: case SQL_NUMERIC: str_val = "real"; break; case SQL_CLOB: str_val = "clob"; break; case SQL_BLOB: str_val = "blob"; break; case SQL_XML: str_val = "xml"; break; case SQL_TYPE_DATE: str_val = "date"; break; case SQL_TYPE_TIME: str_val = "time"; break; case SQL_TYPE_TIMESTAMP: str_val = "timestamp"; break; default: str_val = "string"; break; } return rb_str_new2(str_val); } /* */ /* * IBM_DB::field_width -- Returns the width of the current value of the indicated column in a result set * * ===Description * int IBM_DB::field_width ( resource stmt, mixed column ) * * Returns the width of the current value of the indicated column in a result set. This is the maximum * width of the column for a fixed-length data type, or the actual width of the column for a * variable-length data type. * * ===Parameters * * stmt * Specifies a statement resource containing a result set. * * column * Specifies the column in the result set. This can either be an integer representing the * 0-indexed position of the column, or a string containing the name of the column. * * ===Return Values * * Returns an integer containing the width of the specified character or binary data type column * in a result set. If the specified column does not exist in the result set, IBM_DB::field_width() * returns FALSE. */ VALUE ibm_db_field_width(int argc, VALUE *argv, VALUE self) { VALUE stmt = Qnil; VALUE column = Qnil; int col=-1; char *col_name = NULL; stmt_handle *stmt_res = NULL; int rc; SQLINTEGER colDataSize; rb_scan_args(argc, argv, "2", &stmt, &column); if (!NIL_P(stmt)) { Data_Get_Struct(stmt, stmt_handle, stmt_res); } else { rb_throw("Supplied parameter is invalid", Qnil); } if ( TYPE(column)==T_FIXNUM ) { col = FIX2LONG(column); } else if (RTEST(column)) { col_name = STR2CSTR(column); } col = _ruby_ibm_db_get_column_by_name(stmt_res,col_name, col); if ( col < 0 ) { return Qfalse; } rc = SQLColAttributes((SQLHSTMT)stmt_res->hstmt,(SQLSMALLINT)col+1, SQL_DESC_LENGTH,NULL,0, NULL,&colDataSize); if ( rc != SQL_SUCCESS ) { _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); return Qfalse; } return INT2NUM(colDataSize); } /* */ /* * IBM_DB::cursor_type -- Returns the cursor type used by a statement resource * * ===Description * int IBM_DB::cursor_type ( resource stmt ) * * Returns the cursor type used by a statement resource. Use this to determine if you are working with a forward-only cursor or scrollable cursor. * * ===Parameters * stmt * A valid statement resource. * * ===Return Values * * Returns either SQL_SCROLL_FORWARD_ONLY if the statement resource uses a forward-only cursor or SQL_CURSOR_KEYSET_DRIVEN if the statement resource uses a scrollable cursor. */ VALUE ibm_db_cursor_type(int argc, VALUE *argv, VALUE self) { VALUE stmt = Qnil; stmt_handle *stmt_res = NULL; rb_scan_args(argc, argv, "1", &stmt); if (!NIL_P(stmt)) { Data_Get_Struct(stmt, stmt_handle, stmt_res); } else { rb_throw("Supplied parameter is invalid", Qnil); } return INT2NUM(stmt_res->cursor_type != SQL_SCROLL_FORWARD_ONLY); } /* */ /* * IBM_DB::rollback -- Rolls back a transaction * * ===Description * bool IBM_DB::rollback ( resource connection ) * * Rolls back an in-progress transaction on the specified connection resource and begins a new transaction. Ruby * applications normally default to AUTOCOMMIT mode, so IBM_DB::rollback() normally has no effect unless AUTOCOMMIT * has been turned off for the connection resource. * * Note: If the specified connection resource is a persistent connection, all transactions in progress for all * applications using that persistent connection will be rolled back. For this reason, persistent connections are not * recommended for use in applications that require transactions. * * ===Parameters * * connection * A valid database connection resource variable as returned from IBM_DB::connect() or IBM_DB::pconnect(). * * ===Return Values * * Returns TRUE on success or FALSE on failure. */ VALUE ibm_db_rollback(int argc, VALUE *argv, VALUE self) { VALUE connection = Qnil; conn_handle *conn_res; int rc; rb_scan_args(argc, argv, "1", &connection); if (!NIL_P(connection)) { Data_Get_Struct(connection, conn_handle, conn_res); if (!conn_res->handle_active) { rb_throw("Connection is not active", Qnil); } rc = SQLEndTran(SQL_HANDLE_DBC, conn_res->hdbc, SQL_ROLLBACK); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } else { return Qtrue; } } return Qfalse; } /* */ /* * IBM_DB::free_stmt -- Frees resources associated with the indicated statement resource * * ===Description * bool IBM_DB::free_stmt ( resource stmt ) * * Frees the system and database resources that are associated with a statement resource. These resources * are freed implicitly when a script finishes, but you can call IBM_DB::free_stmt() to explicitly free * the statement resources before the end of the script. * * ===Parameters * stmt * A valid statement resource. * * ===Return Values * * Returns TRUE on success or FALSE on failure. * * ===DEPRECATED */ VALUE ibm_db_free_stmt(int argc, VALUE *argv, VALUE self) { return Qtrue; } /* */ /* static RETCODE _ruby_ibm_db_get_data(stmt_handle *stmt_res, int col_num, short ctype, void *buff, int in_length, SQLINTEGER *out_length) */ static RETCODE _ruby_ibm_db_get_data(stmt_handle *stmt_res, int col_num, short ctype, void *buff, int in_length, SQLINTEGER *out_length) { RETCODE rc=SQL_SUCCESS; rc = SQLGetData((SQLHSTMT)stmt_res->hstmt, col_num, ctype, buff, in_length, out_length); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); } return rc; } /* */ /* {{{ static RETCODE _ruby_ibm_db_get_length(stmt_handle* stmt_res, SQLUSMALLINT col_num, SQLINTEGER *sLength) */ static RETCODE _ruby_ibm_db_get_length(stmt_handle* stmt_res, SQLUSMALLINT col_num, SQLINTEGER *sLength) { RETCODE rc=SQL_SUCCESS; SQLHANDLE new_hstmt; rc = SQLAllocHandle(SQL_HANDLE_STMT, stmt_res->hdbc, &new_hstmt); if ( rc < SQL_SUCCESS ) { _ruby_ibm_db_check_sql_errors(stmt_res->hdbc, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); return SQL_ERROR; } rc = SQLGetLength((SQLHSTMT)new_hstmt, stmt_res->column_info[col_num-1].loc_type, stmt_res->column_info[col_num-1].lob_loc, sLength, &stmt_res->column_info[col_num-1].loc_ind); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors((SQLHSTMT)new_hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); } SQLFreeHandle(SQL_HANDLE_STMT, new_hstmt); return rc; } /* }}} */ /* {{{ static RETCODE _ruby_ibm_db_get_data2(stmt_handle *stmt_res, int col_num, short ctype, void *buff, int in_length, SQLINTEGER *out_length) */ static RETCODE _ruby_ibm_db_get_data2(stmt_handle *stmt_res, SQLUSMALLINT col_num, SQLSMALLINT ctype, SQLPOINTER buff, SQLLEN read_length, SQLLEN buff_length, SQLINTEGER *out_length) { RETCODE rc=SQL_SUCCESS; SQLHANDLE new_hstmt; SQLSMALLINT locType = ctype; SQLSMALLINT targetCType = ctype; rc = SQLAllocHandle(SQL_HANDLE_STMT, stmt_res->hdbc, &new_hstmt); if ( rc < SQL_SUCCESS ) { return SQL_ERROR; } rc = SQLGetSubString((SQLHSTMT)new_hstmt, stmt_res->column_info[col_num-1].loc_type, stmt_res->column_info[col_num-1].lob_loc, 1, read_length, targetCType, buff, buff_length, out_length, &stmt_res->column_info[col_num-1].loc_ind); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors((SQLHSTMT)new_hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); } SQLFreeHandle(SQL_HANDLE_STMT, new_hstmt); return rc; } /*Function to check if the value of the LOB in the column is null or not*/ int isNullLOB(VALUE *return_value,int i,stmt_handle *stmt_res,int op) { if (stmt_res->column_info[i].loc_ind == SQL_NULL_DATA) { if ( op & FETCH_ASSOC ) { rb_hash_aset(*return_value, rb_str_new2((char*)stmt_res->column_info[i].name), Qnil); } if ( op == FETCH_INDEX ) { rb_ary_store(*return_value, i, Qnil); } else if ( op == FETCH_BOTH ) { rb_hash_aset(*return_value, INT2NUM(i), Qnil); } return 1; } return 0; } /* }}} */ /* * IBM_DB::result -- Returns a single column from a row in the result set * * ===Description * mixed IBM_DB::result ( resource stmt, mixed column ) * * Use IBM_DB::result() to return the value of a specified column in the current row of a result set. You must call IBM_DB::fetch_row() before calling IBM_DB::result() to set the location of the result set pointer. * * ===Parameters * * stmt * A valid stmt resource. * * column * Either an integer mapping to the 0-indexed field in the result set, or a string matching the name of the column. * * ===Return Values * * Returns the value of the requested field if the field exists in the result set. Returns NULL if the field does not exist, and issues a warning. */ VALUE ibm_db_result(int argc, VALUE *argv, VALUE self) { VALUE stmt = Qnil; VALUE column = Qnil; stmt_handle *stmt_res; long col_num; RETCODE rc; void *out_ptr; char *out_char_ptr; char error[DB2_MAX_ERR_MSG_LEN]; SQLINTEGER in_length, out_length=-10; /*Initialize out_length to some meaningless value*/ SQLSMALLINT column_type, lob_bind_type= SQL_C_BINARY; double double_val; SQLINTEGER long_val; VALUE return_value = Qnil; rb_scan_args(argc, argv, "2", &stmt, &column); if (!NIL_P(stmt)) { Data_Get_Struct(stmt, stmt_handle, stmt_res); if(TYPE(column) == T_STRING) { col_num = _ruby_ibm_db_get_column_by_name(stmt_res, STR2CSTR(column), -1); } else { col_num = NUM2INT(column); } /* get column header info*/ if ( stmt_res->column_info == NULL ) { if (_ruby_ibm_db_get_result_set_info(stmt_res)<0) { sprintf(error, "Column information cannot be retrieved: %s", IBM_DB_G(__ruby_stmt_err_msg)); rb_throw(error, Qnil); return Qfalse; } } if(col_num < 0 || col_num >= stmt_res->num_columns) { rb_throw("Column ordinal out of range", Qnil); } /* get the data */ column_type = stmt_res->column_info[col_num].type; switch(column_type) { case SQL_CHAR: case SQL_VARCHAR: #ifndef PASE /* i5/OS SQL_LONGVARCHAR is SQL_VARCHAR */ case SQL_LONGVARCHAR: #endif /* PASE */ case SQL_TYPE_DATE: case SQL_TYPE_TIME: case SQL_TYPE_TIMESTAMP: case SQL_BIGINT: case SQL_DECIMAL: case SQL_NUMERIC: if (column_type == SQL_DECIMAL || column_type == SQL_NUMERIC) in_length = stmt_res->column_info[col_num].size + stmt_res->column_info[col_num].scale + 2 + 1; else in_length = stmt_res->column_info[col_num].size+1; out_ptr = (SQLPOINTER)ALLOC_N(char,in_length); if ( out_ptr == NULL ) { rb_throw("Failed to Allocate Memory", Qnil); return Qfalse; } rc = _ruby_ibm_db_get_data(stmt_res, col_num+1, SQL_C_CHAR, out_ptr, in_length, &out_length); if ( rc == SQL_ERROR ) { return Qfalse; } if (out_length == SQL_NULL_DATA) { ruby_xfree(out_ptr); return Qnil; } else { return_value = rb_str_new2((char*)out_ptr); ruby_xfree(out_ptr); return return_value; } break; case SQL_SMALLINT: case SQL_INTEGER: rc = _ruby_ibm_db_get_data(stmt_res, col_num+1, SQL_C_LONG, &long_val, sizeof(long_val), &out_length); if ( rc == SQL_ERROR ) { return Qfalse; } if (out_length == SQL_NULL_DATA) { return Qnil; } else { return INT2NUM(long_val); } break; case SQL_REAL: case SQL_FLOAT: case SQL_DOUBLE: rc = _ruby_ibm_db_get_data(stmt_res, col_num+1, SQL_C_DOUBLE, &double_val, sizeof(double_val), &out_length); if ( rc == SQL_ERROR ) { return Qfalse; } if (out_length == SQL_NULL_DATA) { return Qnil; } else { return rb_float_new(double_val); } break; case SQL_CLOB: rc = _ruby_ibm_db_get_length(stmt_res, col_num+1, &in_length); if ( rc == SQL_ERROR ) { return Qfalse; } if (in_length == SQL_NULL_DATA) { return Qnil; } out_char_ptr = (char*)ALLOC_N(char,in_length+1); if ( out_char_ptr == NULL ) { rb_throw("Failed to Allocate Memory for LOB Data", Qnil); return Qfalse; } rc = _ruby_ibm_db_get_data2(stmt_res, col_num+1, SQL_C_CHAR, (void*)out_char_ptr, in_length, in_length+1, &out_length); if (rc == SQL_ERROR) { return Qfalse; } return rb_str_new2(out_char_ptr); break; case SQL_BLOB: case SQL_BINARY: #ifndef PASE /* i5/OS SQL_LONGVARCHAR is SQL_VARCHAR */ case SQL_LONGVARBINARY: #endif /* PASE */ case SQL_VARBINARY: rc = _ruby_ibm_db_get_length(stmt_res, col_num+1, &in_length); if ( rc == SQL_ERROR ) { return Qfalse; } if (in_length == SQL_NULL_DATA) { return Qnil; } switch (stmt_res->s_bin_mode) { case PASSTHRU: return rb_str_new("",0); break; /* returns here */ case CONVERT: in_length *= 2; lob_bind_type = SQL_C_CHAR; /* fall-through */ case BINARY: out_ptr = (SQLPOINTER)ALLOC_N(char,in_length); if ( out_ptr == NULL ) { rb_throw("Failed to Allocate Memory for LOB Data", Qnil); return Qfalse; } rc = _ruby_ibm_db_get_data2(stmt_res, col_num+1, lob_bind_type, (char *)out_ptr, in_length, in_length, &out_length); if (rc == SQL_ERROR) { return Qfalse; } return rb_str_new((char*)out_ptr,out_length); default: break; } break; case SQL_XML: rc = _ruby_ibm_db_get_length(stmt_res, col_num+1, &in_length); if ( rc == SQL_ERROR ) { return Qfalse; } if (in_length == SQL_NULL_DATA) { return Qnil; } out_ptr = (SQLPOINTER)ALLOC_N(char, in_length); if ( out_ptr == NULL ) { rb_throw("Failed to Allocate Memory for XML Data", Qnil); return Qfalse; } rc = _ruby_ibm_db_get_data2(stmt_res, col_num+1, SQL_C_BINARY, (SQLPOINTER)out_ptr, in_length, in_length, &out_length); if (rc == SQL_ERROR) { return Qfalse; } return rb_str_new((char*)out_ptr,out_length); default: break; } } else { rb_throw("Supplied parameter is invalid", Qnil); } return Qfalse; } /* */ /* static void _ruby_ibm_db_bind_fetch_helper(INTERNAL_FUNCTION_PARAMETERS, int op) */ static VALUE _ruby_ibm_db_bind_fetch_helper(int argc, VALUE *argv, int op) { int rc = -1, i; SQLINTEGER row_number=-1; VALUE stmt = Qnil; stmt_handle *stmt_res = NULL; SQLSMALLINT column_type, lob_bind_type = SQL_C_BINARY; ibm_db_row_data_type *row_data; SQLINTEGER out_length, tmp_length; unsigned char *out_ptr; VALUE return_value = Qnil; char error[DB2_MAX_ERR_MSG_LEN]; VALUE r_row_number = Qnil; rb_scan_args(argc, argv, "11", &stmt, &r_row_number); if (!NIL_P(r_row_number)) row_number = NUM2LONG(r_row_number); if (!NIL_P(stmt)) { Data_Get_Struct(stmt, stmt_handle, stmt_res); } else { rb_throw("Supplied parameter is invalid", Qnil); } _ruby_ibm_db_init_error_info(stmt_res); /* get column header info*/ if ( stmt_res->column_info == NULL ) { if (_ruby_ibm_db_get_result_set_info(stmt_res)<0) { sprintf(error, "Column information cannot be retrieved: %s", IBM_DB_G(__ruby_stmt_err_msg)); rb_throw(error, Qnil); return Qfalse; } } /* bind the data */ if ( stmt_res->row_data == NULL ) { rc = _ruby_ibm_db_bind_column_helper(stmt_res); if ( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO ) { sprintf(error, "Column binding cannot be done: %s", IBM_DB_G(__ruby_stmt_err_msg)); rb_throw(error, Qnil); return Qfalse; } } /* check if row_number is present */ if (argc == 2 && row_number > 0) { #ifndef PASE /* i5/OS problem with SQL_FETCH_ABSOLUTE (temporary until fixed) */ if (is_systemi) { rc = SQLFetchScroll((SQLHSTMT)stmt_res->hstmt, SQL_FETCH_FIRST, row_number); if (row_number>1 && (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO)) rc = SQLFetchScroll((SQLHSTMT)stmt_res->hstmt, SQL_FETCH_RELATIVE, row_number-1); } else { rc = SQLFetchScroll((SQLHSTMT)stmt_res->hstmt, SQL_FETCH_ABSOLUTE, row_number); } #else /*PASE */ rc = SQLFetchScroll((SQLHSTMT)stmt_res->hstmt, SQL_FETCH_FIRST, row_number); if (row_number>1 && (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO)) rc = SQLFetchScroll((SQLHSTMT)stmt_res->hstmt, SQL_FETCH_RELATIVE, row_number-1); #endif /*PASE*/ } else if (argc == 2 && row_number < 0) { rb_throw("Requested row number must be a positive value", Qnil); return Qfalse; } else { /*row_number is NULL or 0; just fetch next row*/ rc = SQLFetch((SQLHSTMT)stmt_res->hstmt); } if (rc == SQL_NO_DATA_FOUND) { return Qfalse; } else if ( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); sprintf(error, "Fetch Failure: %s", IBM_DB_G(__ruby_stmt_err_msg)); rb_throw(error, Qnil); return Qfalse; } /* copy the data over return_value */ if ( op & FETCH_ASSOC ) { return_value = rb_hash_new(); } else if ( op == FETCH_INDEX ) { return_value = rb_ary_new(); } for (i=0; inum_columns; i++) { column_type = stmt_res->column_info[i].type; row_data = &stmt_res->row_data[i].data; out_length = stmt_res->row_data[i].out_length; switch(stmt_res->s_case_mode) { case CASE_LOWER: stmt_res->column_info[i].name = (SQLCHAR*)strtolower((char*)stmt_res->column_info[i].name, strlen((char*)stmt_res->column_info[i].name)); break; case CASE_UPPER: stmt_res->column_info[i].name = (SQLCHAR*)strtoupper((char*)stmt_res->column_info[i].name, strlen((char*)stmt_res->column_info[i].name)); break; case CASE_NATURAL: default: break; } if (out_length == SQL_NULL_DATA) { if ( op & FETCH_ASSOC ) { rb_hash_aset(return_value, rb_str_new2((char*)stmt_res->column_info[i].name), Qnil); } if ( op == FETCH_INDEX ) { rb_ary_store(return_value, i, Qnil); } else if ( op == FETCH_BOTH ) { rb_hash_aset(return_value, INT2NUM(i), Qnil); } } else { switch(column_type) { case SQL_CHAR: case SQL_VARCHAR: #ifndef PASE /* i5/OS SQL_LONGVARCHAR is SQL_VARCHAR */ case SQL_LONGVARCHAR: #else /* PASE */ /* i5/OS will xlate from EBCIDIC to ASCII (via SQLGetData) */ tmp_length = stmt_res->column_info[i].size; out_ptr = (SQLPOINTER)malloc(tmp_length+1); memset(out_ptr,0,tmp_length+1); if ( out_ptr == NULL ) { rb_throw("Failed to Allocate Memory", Qnil); return Qfalse; } rc = _ruby_ibm_db_get_data(stmt_res, i+1, SQL_C_CHAR, out_ptr, tmp_length+1, &out_length); if ( rc == SQL_ERROR ) { ruby_xfree(out_ptr); return Qfalse; } if (out_length == SQL_NULL_DATA) { if ( op & FETCH_ASSOC ) { rb_hash_aset(return_value, rb_str_new2((char*)stmt_res->column_info[i].name), Qnil); } if ( op & FETCH_INDEX ) { rb_ary_store(return_value, i, Qnil); } } else { out_ptr[tmp_length] = '\0'; if ( op & FETCH_ASSOC ) { rb_hash_aset(return_value, rb_str_new2((char *)stmt_res->column_info[i].name), rb_str_new2((char *)out_ptr)); } if ( op & FETCH_INDEX ) { rb_ary_store(return_value, i, rb_str_new2((char *)out_ptr)); } } break; #endif /* PASE */ case SQL_TYPE_DATE: case SQL_TYPE_TIME: case SQL_TYPE_TIMESTAMP: case SQL_BIGINT: case SQL_DECIMAL: case SQL_NUMERIC: if ( op & FETCH_ASSOC ) { rb_hash_aset(return_value, rb_str_new2((char *)stmt_res->column_info[i].name), rb_str_new2((char *)row_data->str_val)); } if ( op == FETCH_INDEX ) { rb_ary_store(return_value, i, rb_str_new2((char *)row_data->str_val)); } else if ( op == FETCH_BOTH ) { rb_hash_aset(return_value, INT2NUM(i), rb_str_new2((char *)row_data->str_val)); } break; case SQL_SMALLINT: if ( op & FETCH_ASSOC ) { rb_hash_aset(return_value, rb_str_new2((char*)stmt_res->column_info[i].name), INT2NUM(row_data->s_val)); } if ( op == FETCH_INDEX ) { rb_ary_store(return_value, i, INT2NUM(row_data->s_val)); } else if ( op == FETCH_BOTH ) { rb_hash_aset(return_value, INT2NUM(i), INT2NUM(row_data->s_val)); } break; case SQL_INTEGER: if ( op & FETCH_ASSOC ) { rb_hash_aset(return_value, rb_str_new2((char*)stmt_res->column_info[i].name), INT2NUM(row_data->i_val)); } if ( op == FETCH_INDEX ) { rb_ary_store(return_value, i, INT2NUM(row_data->i_val)); } else if ( op == FETCH_BOTH ) { rb_hash_aset(return_value, INT2NUM(i), INT2NUM(row_data->i_val)); } break; case SQL_REAL: case SQL_FLOAT: if ( op & FETCH_ASSOC ) { rb_hash_aset(return_value, rb_str_new2((char*)stmt_res->column_info[i].name), rb_float_new(row_data->f_val)); } if ( op == FETCH_INDEX ) { rb_ary_store(return_value, i, rb_float_new(row_data->f_val)); } else if ( op == FETCH_BOTH ) { rb_hash_aset(return_value, INT2NUM(i), rb_float_new(row_data->f_val)); } break; case SQL_DOUBLE: if ( op & FETCH_ASSOC ) { rb_hash_aset(return_value, rb_str_new2((char*)stmt_res->column_info[i].name), rb_float_new(row_data->d_val)); } if ( op == FETCH_INDEX ) { rb_ary_store(return_value, i, rb_float_new(row_data->d_val)); } else if ( op == FETCH_BOTH ) { rb_hash_aset(return_value, INT2NUM(i), rb_float_new(row_data->d_val)); } break; case SQL_BINARY: #ifndef PASE /* i5/OS SQL_LONGVARBINARY is SQL_VARBINARY */ case SQL_LONGVARBINARY: #endif /* PASE */ case SQL_VARBINARY: if ( stmt_res->s_bin_mode == PASSTHRU ) { if ( op & FETCH_ASSOC ) { rb_hash_aset(return_value, rb_str_new2((char*)stmt_res->column_info[i].name), rb_str_new("",0)); } if ( op == FETCH_INDEX ) { rb_ary_store(return_value, i, rb_str_new("",0)); } else if ( op == FETCH_BOTH ) { rb_hash_aset(return_value, INT2NUM(i), rb_str_new("",0)); } } else { if ( op & FETCH_ASSOC ) { rb_hash_aset(return_value, rb_str_new2((char*)stmt_res->column_info[i].name), rb_str_new2((char *)row_data->str_val)); } if ( op == FETCH_INDEX ) { rb_ary_store(return_value, i, rb_str_new2((char *)row_data->str_val)); } else if ( op == FETCH_BOTH ) { rb_hash_aset(return_value, INT2NUM(i), rb_str_new2((char *)row_data->str_val)); } } break; case SQL_BLOB: /*Check if the data value in the column is null*/ if(isNullLOB(&return_value,i,stmt_res,op)) { break; } out_ptr = NULL; rc=_ruby_ibm_db_get_length(stmt_res, i+1, &tmp_length); if (tmp_length == SQL_NULL_DATA) { if ( op & FETCH_ASSOC ) { rb_hash_aset(return_value, rb_str_new2((char*)stmt_res->column_info[i].name), Qnil); } if ( op == FETCH_INDEX ) { rb_ary_store(return_value, i, Qnil); } else if ( op == FETCH_BOTH ) { rb_hash_aset(return_value, INT2NUM(i), Qnil); } } else { if (rc == SQL_ERROR) tmp_length = 0; switch (stmt_res->s_bin_mode) { case PASSTHRU: if ( op & FETCH_ASSOC ) { rb_hash_aset(return_value, rb_str_new2((char*)stmt_res->column_info[i].name), Qnil); } if ( op == FETCH_INDEX ) { rb_ary_store(return_value, i, Qnil); } else if ( op == FETCH_BOTH ) { rb_hash_aset(return_value, INT2NUM(i), Qnil); } break; case CONVERT: tmp_length = 2*tmp_length + 1; lob_bind_type = SQL_C_CHAR; /* fall-through */ case BINARY: out_ptr = (SQLPOINTER)ALLOC_N(char, tmp_length); rc = _ruby_ibm_db_get_data2(stmt_res, i+1, lob_bind_type, (char *)out_ptr, tmp_length, tmp_length, &out_length); if (rc == SQL_ERROR) { ruby_xfree(out_ptr); out_length = 0; } if ( op & FETCH_ASSOC ) { rb_hash_aset(return_value, rb_str_new2((char*)stmt_res->column_info[i].name), rb_str_new((char*)out_ptr, out_length)); } if ( op == FETCH_INDEX ) { rb_ary_store(return_value, i, rb_str_new((char*)out_ptr, out_length)); } else if ( op == FETCH_BOTH ) { rb_hash_aset(return_value, INT2NUM(i), rb_str_new((char*)out_ptr, out_length)); } break; default: break; } } break; case SQL_XML: /*Check if the data value in the column is null*/ if(isNullLOB(&return_value,i,stmt_res,op)) { break; } out_ptr = NULL; rc = _ruby_ibm_db_get_data(stmt_res, i+1, SQL_C_BINARY, NULL, 0, (SQLINTEGER *)&tmp_length); if ( rc == SQL_ERROR ) { sprintf(error, "Failed to Determine XML Size: %s", IBM_DB_G(__ruby_stmt_err_msg)); rb_throw(error, Qnil); return Qfalse; } if (tmp_length == SQL_NULL_DATA) { if ( op & FETCH_ASSOC ) { rb_hash_aset(return_value, rb_str_new2((char *)stmt_res->column_info[i].name), Qnil); } if ( op & FETCH_INDEX ) { rb_ary_store(return_value, i, Qnil); } else if ( op == FETCH_BOTH) { rb_hash_aset(return_value, INT2NUM(i), Qnil); } } else { out_ptr = (SQLPOINTER)ALLOC_N(char, tmp_length); if ( out_ptr == NULL ) { rb_throw("Failed to Allocate Memory for XML Data", Qnil); return Qfalse; } rc = _ruby_ibm_db_get_data(stmt_res, i+1, SQL_C_BINARY, out_ptr, tmp_length, &out_length); if (rc == SQL_ERROR) { ruby_xfree(out_ptr); return Qfalse; } if ( op & FETCH_ASSOC ) { rb_hash_aset(return_value, rb_str_new2((char *)stmt_res->column_info[i].name), rb_str_new((char *)out_ptr, out_length)); } if ( op == FETCH_INDEX ) { rb_ary_store(return_value, i, rb_str_new((char *)out_ptr, out_length)); } else if ( op == FETCH_BOTH ) { rb_hash_aset(return_value, INT2NUM(i), rb_str_new((char *)out_ptr, out_length)); } } break; case SQL_CLOB: /*Check if the data value in the column is null*/ if(isNullLOB(&return_value,i,stmt_res,op)) { break; } out_ptr = NULL; rc = _ruby_ibm_db_get_length(stmt_res, i+1, &tmp_length); if (tmp_length == SQL_NULL_DATA) { if ( op & FETCH_ASSOC ) { rb_hash_aset(return_value, rb_str_new2((char*)stmt_res->column_info[i].name), Qnil); } if ( op == FETCH_INDEX ) { rb_ary_store(return_value, i, Qnil); } else if ( op == FETCH_BOTH ) { rb_hash_aset(return_value, INT2NUM(i), Qnil); } } else { if (rc == SQL_ERROR) tmp_length = 0; out_ptr = (SQLPOINTER)ALLOC_N(char, tmp_length+1); if ( out_ptr == NULL ) { rb_throw("Failed to Allocate Memory for LOB Data", Qnil); return Qfalse; } rc = _ruby_ibm_db_get_data2(stmt_res, i+1, SQL_C_CHAR, out_ptr, tmp_length, tmp_length+1, &out_length); if (rc == SQL_ERROR) { ruby_xfree(out_ptr); out_length = 0; } else { out_ptr[tmp_length] = '\0'; } if ( op & FETCH_ASSOC ) { rb_hash_aset(return_value, rb_str_new2((char*)stmt_res->column_info[i].name), rb_str_new((char*)out_ptr, out_length)); } if ( op == FETCH_INDEX ) { rb_ary_store(return_value, i, rb_str_new((char*)out_ptr, out_length)); } else if ( op == FETCH_BOTH ) { rb_hash_aset(return_value, INT2NUM(i), rb_str_new((char*)out_ptr, out_length)); } } break; default: break; } } } return return_value; } /* */ /* * IBM_DB::fetch_row -- Sets the result set pointer to the next row or requested row * * ===Description * bool IBM_DB::fetch_row ( resource stmt [, int row_number] ) * * Use IBM_DB::fetch_row() to iterate through a result set, or to point to a specific row in a result set * if you requested a scrollable cursor. * * To retrieve individual fields from the result set, call the IBM_DB::result() function. Rather than calling * IBM_DB::fetch_row() and IBM_DB::result(), most applications will call one of IBM_DB::fetch_assoc(), * IBM_DB::fetch_both(), or IBM_DB::fetch_array() to advance the result set pointer and return a complete * row as an array. * * ===Parameters * stmt * A valid stmt resource. * * row_number * With scrollable cursors, you can request a specific row number in the result set. Row numbering * is 1-indexed. * * ===Return Values * * Returns TRUE if the requested row exists in the result set. Returns FALSE if the requested row * does not exist in the result set. */ VALUE ibm_db_fetch_row(int argc, VALUE *argv, VALUE self) { VALUE row_number; VALUE stmt = Qnil; stmt_handle* stmt_res = NULL; int rc; char error[DB2_MAX_ERR_MSG_LEN]; rb_scan_args(argc, argv, "11", &stmt, &row_number); if (!NIL_P(stmt)) { Data_Get_Struct(stmt, stmt_handle, stmt_res); } else { rb_throw("Supplied parameter is invalid", Qnil); } /* get column header info*/ if ( stmt_res->column_info == NULL ) { if (_ruby_ibm_db_get_result_set_info(stmt_res)<0) { sprintf(error, "Column information cannot be retrieved: %s", IBM_DB_G(__ruby_stmt_err_msg)); rb_throw(error, Qnil); return Qfalse; } } /*check if row_number is present*/ if (argc == 2 && NUM2LONG(row_number) > 0) { #ifndef PASE /* i5/OS problem with SQL_FETCH_ABSOLUTE */ rc = SQLFetchScroll((SQLHSTMT)stmt_res->hstmt, SQL_FETCH_ABSOLUTE, NUM2LONG(row_number)); #else /*PASE */ rc = SQLFetchScroll((SQLHSTMT)stmt_res->hstmt, SQL_FETCH_FIRST, row_number); if (row_number>1 && (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO)) rc = SQLFetchScroll((SQLHSTMT)stmt_res->hstmt, SQL_FETCH_RELATIVE, row_number-1); #endif /*PASE*/ } else if (argc == 2 && NUM2LONG(row_number) < 0) { rb_throw("Requested row number must be a positive value", Qnil); return Qfalse; } else { /*row_number is NULL or 0; just fetch next row*/ rc = SQLFetch((SQLHSTMT)stmt_res->hstmt); } if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) { return Qtrue; } else { _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); return Qfalse; } } /* */ /* * IBM_DB::fetch_assoc -- Returns an array, indexed by column name, representing a row in a result set * * ===Description * array IBM_DB::fetch_assoc ( resource stmt [, int row_number] ) * * Returns an array, indexed by column name, representing a row in a result set. * * ===Parameters * stmt * A valid stmt resource containing a result set. * * row_number * * Requests a specific 1-indexed row from the result set. Passing this parameter results in a * Ruby warning if the result set uses a forward-only cursor. * * ===Return Values * * Returns an associative array with column values indexed by the column name representing the next * or requested row in the result set. Returns FALSE if there are no rows left in the result set, * or if the row requested by row_number does not exist in the result set. */ VALUE ibm_db_fetch_assoc(int argc, VALUE *argv, VALUE self) { return _ruby_ibm_db_bind_fetch_helper(argc, argv, FETCH_ASSOC); } /* */ /* * IBM_DB::fetch_object -- Returns an object with properties representing columns in the fetched row * * ===Description * object IBM_DB::fetch_object ( resource stmt [, int row_number] ) * * Returns an object in which each property represents a column returned in the row fetched from a result set. * * ===Parameters * * stmt * A valid stmt resource containing a result set. * * row_number * Requests a specific 1-indexed row from the result set. Passing this parameter results in a * Ruby warning if the result set uses a forward-only cursor. * * ===Return Values * * Returns an object representing a single row in the result set. The properties of the object map * to the names of the columns in the result set. * * The IBM DB2, Cloudscape, and Apache Derby database servers typically fold column names to upper-case, * so the object properties will reflect that case. * * If your SELECT statement calls a scalar function to modify the value of a column, the database servers * return the column number as the name of the column in the result set. If you prefer a more * descriptive column name and object property, you can use the AS clause to assign a name * to the column in the result set. * * Returns FALSE if no row was retrieved. */ VALUE ibm_db_fetch_object(int argc, VALUE *argv, VALUE self) { row_hash_struct *row_res; row_res = ALLOC(row_hash_struct); row_res->hash = _ruby_ibm_db_bind_fetch_helper(argc, argv, FETCH_ASSOC); if (RTEST(row_res->hash)) { return Data_Wrap_Struct(le_row_struct, _ruby_ibm_db_mark_row_struct, _ruby_ibm_db_free_row_struct, row_res); } else { ruby_xfree(row_res); return Qfalse; } } /* */ /* * IBM_DB::fetch_array -- Returns an array, indexed by column position, representing a row in a result set * * ===Description * * array IBM_DB::fetch_array ( resource stmt [, int row_number] ) * * Returns an array, indexed by column position, representing a row in a result set. The columns are 0-indexed. * * ===Parameters * * stmt * A valid stmt resource containing a result set. * * row_number * Requests a specific 1-indexed row from the result set. Passing this parameter results in a * Ruby warning if the result set uses a forward-only cursor. * * ===Return Values * * Returns a 0-indexed array with column values indexed by the column position representing the next * or requested row in the result set. Returns FALSE if there are no rows left in the result set, * or if the row requested by row_number does not exist in the result set. */ VALUE ibm_db_fetch_array(int argc, VALUE *argv, VALUE self) { return _ruby_ibm_db_bind_fetch_helper(argc, argv, FETCH_INDEX); } /* */ /* * IBM_DB::fetch_both -- Returns an array, indexed by both column name and position, representing a row * in a result set * * ===Description * array IBM_DB::fetch_both ( resource stmt [, int row_number] ) * * Returns an array, indexed by both column name and position, representing a row in a result set. * Note that the row returned by IBM_DB::fetch_both() requires more memory than the single-indexed * arrays returned by IBM_DB::fetch_assoc() or IBM_DB::fetch_array(). * * ===Parameters * * stmt * A valid stmt resource containing a result set. * * row_number * Requests a specific 1-indexed row from the result set. Passing this parameter results in a * Ruby warning if the result set uses a forward-only cursor. * * ===Return Values * * Returns an associative array with column values indexed by both the column name and 0-indexed column number. * The array represents the next or requested row in the result set. Returns FALSE if there are no rows * left in the result set, or if the row requested by row_number does not exist in the result set. */ VALUE ibm_db_fetch_both(int argc, VALUE *argv, VALUE self) { return _ruby_ibm_db_bind_fetch_helper(argc, argv, FETCH_BOTH); } /* */ /* * IBM_DB::set_option -- Sets the specified option in the resource. * * ===Description * bool IBM_DB::set_option ( resource resc, array options, int type ) * * Sets options for a connection or statement resource. You cannot set options for result set resources. * * ===Parameters * * resc * A valid connection or statement resource. * * options * The options to be set * * type * A field that specifies the resource type (1 = Connection, NON-1 = Statement) * * ===Return Values * * Returns TRUE on success or FALSE on failure */ VALUE ibm_db_set_option(int argc, VALUE *argv, VALUE self) { VALUE conn_or_stmt = Qnil; VALUE r_options; VALUE r_type; int options = 0; stmt_handle *stmt_res = NULL; conn_handle *conn_res; int rc = 0; long type = 0; rb_scan_args(argc, argv, "3", &conn_or_stmt, &r_options, &r_type); if (!NIL_P(r_type)) type = NUM2LONG(r_type); if (!NIL_P(conn_or_stmt)) { if ( type == 1 ) { Data_Get_Struct(conn_or_stmt, conn_handle, conn_res); if ( !NIL_P(r_options) ) { rc = _ruby_ibm_db_parse_options( r_options, SQL_HANDLE_DBC, conn_res ); if (rc == SQL_ERROR) { rb_throw("Options Array must have string indexes", Qnil); return Qfalse; } } } else { Data_Get_Struct(conn_or_stmt, stmt_handle, stmt_res); if ( !NIL_P(r_options) ) { rc = _ruby_ibm_db_parse_options( r_options, SQL_HANDLE_STMT, stmt_res ); if (rc == SQL_ERROR) { rb_throw("Options Array must have string indexes", Qnil); return Qfalse; } } } return Qtrue; } else { return Qfalse; } } /* */ /* * IBM_DB::server_info -- Returns an object with properties that describe the DB2 database server * * ===Description * object IBM_DB::server_info ( resource connection ) * * This function returns a read-only object with information about the IBM DB2, Cloudscape, or Apache Derby database server. * The following table lists the database server properties: * * ===Table 1. Database server properties * Property name:: Description (Return type) * DBMS_NAME:: The name of the database server to which you are connected. For DB2 servers this is a combination * of DB2 followed by the operating system on which the database server is running. (string) * DBMS_VER:: The version of the database server, in the form of a string "MM.mm.uuuu" where MM is the major version, * mm is the minor version, and uuuu is the update. For example, "08.02.0001" represents major version 8, * minor version 2, update 1. (string) * DB_CODEPAGE:: The code page of the database to which you are connected. (int) * DB_NAME:: The name of the database to which you are connected. (string) * DFT_ISOLATION:: The default transaction isolation level supported by the server: (string) * * UR:: Uncommitted read: changes are immediately visible by all concurrent transactions. * * CS:: Cursor stability: a row read by one transaction can be altered and committed by a second concurrent transaction. * * RS:: Read stability: a transaction can add or remove rows matching a search condition or a pending transaction. * * RR:: Repeatable read: data affected by pending transaction is not available to other transactions. * * NC:: No commit: any changes are visible at the end of a successful operation. Explicit commits and rollbacks are not allowed. * * IDENTIFIER_QUOTE_CHAR:: The character used to delimit an identifier. (string) * INST_NAME:: The instance on the database server that contains the database. (string) * ISOLATION_OPTION:: An array of the isolation options supported by the database server. The isolation options are described * in the DFT_ISOLATION property. (array) * KEYWORDS:: An array of the keywords reserved by the database server. (array) * LIKE_ESCAPE_CLAUSE:: TRUE if the database server supports the use of % and _ wildcard characters. FALSE if the database server * does not support these wildcard characters. (bool) * MAX_COL_NAME_LEN:: Maximum length of a column name supported by the database server, expressed in bytes. (int) * MAX_IDENTIFIER_LEN:: Maximum length of an SQL identifier supported by the database server, expressed in characters. (int) * MAX_INDEX_SIZE:: Maximum size of columns combined in an index supported by the database server, expressed in bytes. (int) * MAX_PROC_NAME_LEN:: Maximum length of a procedure name supported by the database server, expressed in bytes. (int) * MAX_ROW_SIZE:: Maximum length of a row in a base table supported by the database server, expressed in bytes. (int) * MAX_SCHEMA_NAME_LEN:: Maximum length of a schema name supported by the database server, expressed in bytes. (int) * MAX_STATEMENT_LEN:: Maximum length of an SQL statement supported by the database server, expressed in bytes. (int) * MAX_TABLE_NAME_LEN:: Maximum length of a table name supported by the database server, expressed in bytes. (bool) * NON_NULLABLE_COLUMNS:: TRUE if the database server supports columns that can be defined as NOT NULL, FALSE if the database * server does not support columns defined as NOT NULL. (bool) * PROCEDURES:: TRUE if the database server supports the use of the CALL statement to call stored procedures, FALSE if the * database server does not support the CALL statement. (bool) * SPECIAL_CHARS:: A string containing all of the characters other than a-Z, 0-9, and underscore that can be used in an * identifier name. (string) * SQL_CONFORMANCE:: The level of conformance to the ANSI/ISO SQL-92 specification offered by the database server: (string) * * ENTRY:: Entry-level SQL-92 compliance. * * FIPS127:: FIPS-127-2 transitional compliance. * * FULL:: Full level SQL-92 compliance. * * INTERMEDIATE:: Intermediate level SQL-92 compliance. * * ===Parameters * * connection * Specifies an active DB2 client connection. * * ===Return Values * * Returns an object on a successful call. Returns FALSE on failure. */ VALUE ibm_db_server_info(int argc, VALUE *argv, VALUE self) { VALUE connection = Qnil; conn_handle *conn_res; int rc = 0; char buffer11[11]; char buffer255[255]; char buffer2k[2048]; SQLSMALLINT bufferint16; SQLUINTEGER bufferint32; SQLINTEGER bitmask; VALUE return_value = rb_funcall(le_server_info, id_new, 0); rb_scan_args(argc, argv, "1", &connection); if (!NIL_P(connection)) { Data_Get_Struct(connection, conn_handle, conn_res); if (!conn_res->handle_active) { rb_throw("Connection is not active", Qnil); } /* DBMS_NAME */ memset(buffer255, 0, sizeof(buffer255)); rc = SQLGetInfo(conn_res->hdbc, SQL_DBMS_NAME, (SQLPOINTER)buffer255, sizeof(buffer255), NULL); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } else { rb_iv_set(return_value, "@DBMS_NAME", rb_str_new2(buffer255)); } /* DBMS_VER */ memset(buffer11, 0, sizeof(buffer11)); rc = SQLGetInfo(conn_res->hdbc, SQL_DBMS_VER, (SQLPOINTER)buffer11, sizeof(buffer11), NULL); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } else { rb_iv_set(return_value, "@DBMS_VER", rb_str_new2(buffer11)); } #ifndef PASE /* i5/OS DB_CODEPAGE handled natively */ /* DB_CODEPAGE */ bufferint32 = 0; rc = SQLGetInfo(conn_res->hdbc, SQL_DATABASE_CODEPAGE, &bufferint32, sizeof(bufferint32), NULL); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } else { rb_iv_set(return_value, "@DB_CODEPAGE", INT2NUM(bufferint32)); } #endif /* PASE */ /* DB_NAME */ memset(buffer255, 0, sizeof(buffer255)); rc = SQLGetInfo(conn_res->hdbc, SQL_DATABASE_NAME, (SQLPOINTER)buffer255, sizeof(buffer255), NULL); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } else { rb_iv_set(return_value, "@DB_NAME", rb_str_new2(buffer255)); } #ifndef PASE /* i5/OS INST_NAME handled natively */ /* INST_NAME */ memset(buffer255, 0, sizeof(buffer255)); rc = SQLGetInfo(conn_res->hdbc, SQL_SERVER_NAME, (SQLPOINTER)buffer255, sizeof(buffer255), NULL); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } else { rb_iv_set(return_value, "@INST_NAME", rb_str_new2(buffer255)); } /* SPECIAL_CHARS */ memset(buffer255, 0, sizeof(buffer255)); rc = SQLGetInfo(conn_res->hdbc, SQL_SPECIAL_CHARACTERS, (SQLPOINTER)buffer255, sizeof(buffer255), NULL); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } else { rb_iv_set(return_value, "@SPECIAL_CHARS", rb_str_new2(buffer255)); } #endif /* PASE */ /* KEYWORDS */ memset(buffer2k, 0, sizeof(buffer2k)); rc = SQLGetInfo(conn_res->hdbc, SQL_KEYWORDS, (SQLPOINTER)buffer2k, sizeof(buffer2k), NULL); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } else { char *keyword, *last; VALUE karray; karray = rb_ary_new(); for (keyword = last = buffer2k; *last; last++) { if (*last == ',') { *last = '\0'; rb_ary_push(karray, rb_str_new2(keyword)); keyword = last+1; } } if (*keyword) rb_ary_push(karray, rb_str_new2(keyword)); rb_iv_set(return_value, "@KEYWORDS", karray); } /* DFT_ISOLATION */ bitmask = 0; memset(buffer11, 0, sizeof(buffer11)); rc = SQLGetInfo(conn_res->hdbc, SQL_DEFAULT_TXN_ISOLATION, &bitmask, sizeof(bitmask), NULL); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } else { if( bitmask & SQL_TXN_READ_UNCOMMITTED ) { strcpy((char *)buffer11, "UR"); } if( bitmask & SQL_TXN_READ_COMMITTED ) { strcpy((char *)buffer11, "CS"); } if( bitmask & SQL_TXN_REPEATABLE_READ ) { strcpy((char *)buffer11, "RS"); } if( bitmask & SQL_TXN_SERIALIZABLE ) { strcpy((char *)buffer11, "RR"); } if( bitmask & SQL_TXN_NOCOMMIT ) { strcpy((char *)buffer11, "NC"); } rb_iv_set(return_value, "@DFT_ISOLATION", rb_str_new2(buffer11)); } #ifndef PASE /* i5/OS ISOLATION_OPTION handled natively */ /* ISOLATION_OPTION */ bitmask = 0; memset(buffer11, 0, sizeof(buffer11)); rc = SQLGetInfo(conn_res->hdbc, SQL_TXN_ISOLATION_OPTION, &bitmask, sizeof(bitmask), NULL); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } else { VALUE array; array = rb_ary_new(); if( bitmask & SQL_TXN_READ_UNCOMMITTED ) { rb_ary_push(array, rb_str_new2("UR")); } if( bitmask & SQL_TXN_READ_COMMITTED ) { rb_ary_push(array, rb_str_new2("CS")); } if( bitmask & SQL_TXN_REPEATABLE_READ ) { rb_ary_push(array, rb_str_new2("RS")); } if( bitmask & SQL_TXN_SERIALIZABLE ) { rb_ary_push(array, rb_str_new2("RR")); } if( bitmask & SQL_TXN_NOCOMMIT ) { rb_ary_push(array, rb_str_new2("NC")); } rb_iv_set(return_value, "@ISOLATION_OPTION", array); } #endif /* PASE */ /* SQL_CONFORMANCE */ bufferint32 = 0; memset(buffer255, 0, sizeof(buffer255)); rc = SQLGetInfo(conn_res->hdbc, SQL_ODBC_SQL_CONFORMANCE, &bufferint32, sizeof(bufferint32), NULL); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } else { switch (bufferint32) { case SQL_SC_SQL92_ENTRY: strcpy((char *)buffer255, "ENTRY"); break; case SQL_SC_FIPS127_2_TRANSITIONAL: strcpy((char *)buffer255, "FIPS127"); break; case SQL_SC_SQL92_FULL: strcpy((char *)buffer255, "FULL"); break; case SQL_SC_SQL92_INTERMEDIATE: strcpy((char *)buffer255, "INTERMEDIATE"); break; default: break; } rb_iv_set(return_value, "@SQL_CONFORMANCE", rb_str_new2(buffer255)); } /* PROCEDURES */ memset(buffer11, 0, sizeof(buffer11)); rc = SQLGetInfo(conn_res->hdbc, SQL_PROCEDURES, (SQLPOINTER)buffer11, sizeof(buffer11), NULL); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } else { if( strcmp((char *)buffer11, "Y") == 0 ) { rb_iv_set(return_value, "@PROCEDURES", Qtrue); } else { rb_iv_set(return_value, "@PROCEDURES", Qfalse); } } /* IDENTIFIER_QUOTE_CHAR */ memset(buffer11, 0, sizeof(buffer11)); rc = SQLGetInfo(conn_res->hdbc, SQL_IDENTIFIER_QUOTE_CHAR, (SQLPOINTER)buffer11, sizeof(buffer11), NULL); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } else { rb_iv_set(return_value, "@IDENTIFIER_QUOTE_CHAR", rb_str_new2(buffer11)); } /* LIKE_ESCAPE_CLAUSE */ memset(buffer11, 0, sizeof(buffer11)); rc = SQLGetInfo(conn_res->hdbc, SQL_LIKE_ESCAPE_CLAUSE, (SQLPOINTER)buffer11, sizeof(buffer11), NULL); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } else { if( strcmp(buffer11, "Y") == 0 ) { rb_iv_set(return_value, "@LIKE_ESCAPE_CLAUSE", Qtrue); } else { rb_iv_set(return_value, "@LIKE_ESCAPE_CLAUSE", Qfalse); } } /* MAX_COL_NAME_LEN */ bufferint16 = 0; rc = SQLGetInfo(conn_res->hdbc, SQL_MAX_COLUMN_NAME_LEN, &bufferint16, sizeof(bufferint16), NULL); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } else { rb_iv_set(return_value, "@MAX_COL_NAME_LEN", INT2NUM(bufferint16)); } /* MAX_ROW_SIZE */ bufferint32 = 0; rc = SQLGetInfo(conn_res->hdbc, SQL_MAX_ROW_SIZE, &bufferint32, sizeof(bufferint32), NULL); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } else { rb_iv_set(return_value, "@MAX_ROW_SIZE", INT2NUM(bufferint32)); } #ifndef PASE /* i5/OS MAX_IDENTIFIER_LEN handled natively */ /* MAX_IDENTIFIER_LEN */ bufferint16 = 0; rc = SQLGetInfo(conn_res->hdbc, SQL_MAX_IDENTIFIER_LEN, &bufferint16, sizeof(bufferint16), NULL); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } else { rb_iv_set(return_value, "@MAX_IDENTIFIER_LEN", INT2NUM(bufferint16)); } /* MAX_INDEX_SIZE */ bufferint32 = 0; rc = SQLGetInfo(conn_res->hdbc, SQL_MAX_INDEX_SIZE, &bufferint32, sizeof(bufferint32), NULL); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } else { rb_iv_set(return_value, "@MAX_INDEX_SIZE", INT2NUM(bufferint32)); } /* MAX_PROC_NAME_LEN */ bufferint16 = 0; rc = SQLGetInfo(conn_res->hdbc, SQL_MAX_PROCEDURE_NAME_LEN, &bufferint16, sizeof(bufferint16), NULL); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } else { rb_iv_set(return_value, "@MAX_PROC_NAME_LEN", INT2NUM(bufferint16)); } #endif /* PASE */ /* MAX_SCHEMA_NAME_LEN */ bufferint16 = 0; rc = SQLGetInfo(conn_res->hdbc, SQL_MAX_SCHEMA_NAME_LEN, &bufferint16, sizeof(bufferint16), NULL); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } else { rb_iv_set(return_value, "@MAX_SCHEMA_NAME_LEN", INT2NUM(bufferint16)); } /* MAX_STATEMENT_LEN */ bufferint32 = 0; rc = SQLGetInfo(conn_res->hdbc, SQL_MAX_STATEMENT_LEN, &bufferint32, sizeof(bufferint32), NULL); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } else { rb_iv_set(return_value, "@MAX_STATEMENT_LEN", INT2NUM(bufferint32)); } /* MAX_TABLE_NAME_LEN */ bufferint16 = 0; rc = SQLGetInfo(conn_res->hdbc, SQL_MAX_TABLE_NAME_LEN, &bufferint16, sizeof(bufferint16), NULL); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } else { rb_iv_set(return_value, "@MAX_TABLE_NAME_LEN", INT2NUM(bufferint16)); } /* NON_NULLABLE_COLUMNS */ bufferint16 = 0; rc = SQLGetInfo(conn_res->hdbc, SQL_NON_NULLABLE_COLUMNS, &bufferint16, sizeof(bufferint16), NULL); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } else { VALUE rv = Qnil; switch (bufferint16) { case SQL_NNC_NON_NULL: rv = Qtrue; break; case SQL_NNC_NULL: rv = Qfalse; break; default: break; } rb_iv_set(return_value, "@NON_NULLABLE_COLUMNS", rv); } return return_value; } return Qnil; } /* */ /* * IBM_DB::client_info -- Returns an object with properties that describe the DB2 database client * * ===Description * object IBM_DB::client_info ( resource connection ) * * This function returns a read-only object with information about the DB2 database client. The following table lists the DB2 client properties: * * ====Table 1. DB2 client properties * * Property name:: Description (Return type) * * APPL_CODEPAGE:: The application code page. (int) * * CONN_CODEPAGE:: The code page for the current connection. (int) * * DATA_SOURCE_NAME:: The data source name (DSN) used to create the current connection to the database. (string) * * DRIVER_NAME:: The name of the library that implements the DB2 Call Level Interface (CLI) specification. (string) * * DRIVER_ODBC_VER:: The version of ODBC that the DB2 client supports. This returns a string "MM.mm" where MM is the major version and mm is the minor version. The DB2 client always returns "03.51". (string) * * DRIVER_VER:: The version of the client, in the form of a string "MM.mm.uuuu" where MM is the major version, mm is the minor version, and uuuu is the update. For example, "08.02.0001" represents major version 8, minor version 2, update 1. (string) * * ODBC_SQL_CONFORMANCE:: There are three levels of ODBC SQL grammar supported by the client: MINIMAL (Supports the minimum ODBC SQL grammar), CORE (Supports the core ODBC SQL grammar), EXTENDED (Supports extended ODBC SQL grammar). (string) * * ODBC_VER:: The version of ODBC that the ODBC driver manager supports. This returns a string "MM.mm.rrrr" where MM is the major version, mm is the minor version, and rrrr is the release. The DB2 client always returns "03.01.0000". (string) * * ===Parameters * * connection * * Specifies an active DB2 client connection. * * ===Return Values * * Returns an object on a successful call. Returns FALSE on failure. */ VALUE ibm_db_client_info(int argc, VALUE *argv, VALUE self) { VALUE connection = Qnil; conn_handle *conn_res; int rc = 0; char buffer255[255]; SQLSMALLINT bufferint16; SQLUINTEGER bufferint32; VALUE return_value = rb_funcall(le_client_info, id_new, 0); rb_scan_args(argc, argv, "1", &connection); if (!NIL_P(connection)) { Data_Get_Struct(connection, conn_handle, conn_res); if (!conn_res->handle_active) { rb_throw("Connection is not active", Qnil); } /* DRIVER_NAME */ memset(buffer255, 0, sizeof(buffer255)); rc = SQLGetInfo(conn_res->hdbc, SQL_DRIVER_NAME, (SQLPOINTER)buffer255, sizeof(buffer255), NULL); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } else { rb_iv_set(return_value, "@DRIVER_NAME", rb_str_new2(buffer255)); } /* DRIVER_VER */ memset(buffer255, 0, sizeof(buffer255)); rc = SQLGetInfo(conn_res->hdbc, SQL_DRIVER_VER, (SQLPOINTER)buffer255, sizeof(buffer255), NULL); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } else { rb_iv_set(return_value, "@DRIVER_VER", rb_str_new2(buffer255)); } /* DATA_SOURCE_NAME */ memset(buffer255, 0, sizeof(buffer255)); rc = SQLGetInfo(conn_res->hdbc, SQL_DATA_SOURCE_NAME, (SQLPOINTER)buffer255, sizeof(buffer255), NULL); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } else { rb_iv_set(return_value, "@DATA_SOURCE_NAME", rb_str_new2(buffer255)); } /* DRIVER_ODBC_VER */ memset(buffer255, 0, sizeof(buffer255)); rc = SQLGetInfo(conn_res->hdbc, SQL_DRIVER_ODBC_VER, (SQLPOINTER)buffer255, sizeof(buffer255), NULL); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } else { rb_iv_set(return_value, "@DRIVER_ODBC_VER", rb_str_new2(buffer255)); } #ifndef PASE /* i5/OS ODBC_VER handled natively */ /* ODBC_VER */ memset(buffer255, 0, sizeof(buffer255)); rc = SQLGetInfo(conn_res->hdbc, SQL_ODBC_VER, (SQLPOINTER)buffer255, sizeof(buffer255), NULL); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } else { rb_iv_set(return_value, "@ODBC_VER", rb_str_new2(buffer255)); } #endif /* PASE */ /* ODBC_SQL_CONFORMANCE */ bufferint16 = 0; memset(buffer255, 0, sizeof(buffer255)); rc = SQLGetInfo(conn_res->hdbc, SQL_ODBC_SQL_CONFORMANCE, &bufferint16, sizeof(bufferint16), NULL); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } else { switch (bufferint16) { case SQL_OSC_MINIMUM: strcpy((char *)buffer255, "MINIMUM"); break; case SQL_OSC_CORE: strcpy((char *)buffer255, "CORE"); break; case SQL_OSC_EXTENDED: strcpy((char *)buffer255, "EXTENDED"); break; default: break; } rb_iv_set(return_value, "@ODBC_SQL_CONFORMANCE", rb_str_new2(buffer255)); } #ifndef PASE /* i5/OS APPL_CODEPAGE handled natively */ /* APPL_CODEPAGE */ bufferint32 = 0; rc = SQLGetInfo(conn_res->hdbc, SQL_APPLICATION_CODEPAGE, &bufferint32, sizeof(bufferint32), NULL); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } else { rb_iv_set(return_value, "@APPL_CODEPAGE", INT2NUM(bufferint32)); } /* CONN_CODEPAGE */ bufferint32 = 0; rc = SQLGetInfo(conn_res->hdbc, SQL_CONNECT_CODEPAGE, &bufferint32, sizeof(bufferint32), NULL); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors(conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); return Qfalse; } else { rb_iv_set(return_value, "@CONN_CODEPAGE", INT2NUM(bufferint32)); } #endif /* PASE */ return return_value; } return Qnil; } /* */ /* * IBM_DB::active -- Checks if the specified connection resource is active * * ===Description * object IBM_DB::active(resource connection) * * Returns true if the given connection resource is active * * ===Parameters * connection * The connection resource to be validated. * * ===Return Values * * Returns true if the given connection resource is active, otherwise it will return false */ VALUE ibm_db_active(int argc, VALUE *argv, VALUE self) { VALUE connection = Qnil; int rc; conn_handle *conn_res; SQLINTEGER conn_alive; conn_alive = 0; rb_scan_args(argc, argv, "1", &connection); if (!NIL_P(connection)) { Data_Get_Struct(connection, conn_handle, conn_res); #ifndef PASE rc = SQLGetConnectAttr(conn_res->hdbc, SQL_ATTR_PING_DB, (SQLPOINTER)&conn_alive, 0, NULL); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors( conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); } #endif /* PASE */ } /* * SQLGetConnectAttr with SQL_ATTR_PING_DB will return 0 on failure but will return * the ping time on success. We only want success or failure. */ if (conn_alive == 0) { return Qfalse; } else { return Qtrue; } } /* */ /* * IBM_DB::get_option -- Gets the specified option in the resource. * * ===Description * mixed IBM_DB::get_option ( resource resc, int options, int type ) * * Returns a value, that is the current setting of a connection or statement attribute. * * ===Parameters * * resc * A valid connection or statement resource containing a result set. * * options * The options to be retrieved * * type * A field that specifies the resource type (1 = Connection, non - 1 = Statement) * * ===Return Values * * Returns the current setting of the resource attribute provided. */ VALUE ibm_db_get_option(int argc, VALUE *argv, VALUE self) { VALUE conn_or_stmt = Qnil; VALUE option = Qnil; VALUE r_type = Qnil; SQLCHAR *value = NULL; SQLINTEGER value_int = 0; conn_handle *conn_res = NULL; stmt_handle *stmt_res = NULL; SQLCHAR *op_string = NULL; SQLINTEGER op_integer = 0; long type = 0; int rc; rb_scan_args(argc, argv, "3", &conn_or_stmt, &option, &r_type); if (!NIL_P(r_type)) type = NUM2LONG(r_type); if (!NIL_P(conn_or_stmt)) { /* Checking to see if we are getting a connection option (1) or a statement option (non - 1) */ if (type == 1) { Data_Get_Struct(conn_or_stmt, conn_handle, conn_res); /* Check to ensure the connection resource given is active */ if (!conn_res->handle_active) { rb_throw("Connection is not active", Qnil); } /* Check that the option given is not null */ if (!NIL_P(option)) { op_integer=(SQLINTEGER)FIX2INT(option); /* ACCTSTR_LEN is the largest possible length of the options to retrieve */ value = ALLOC_N(char, ACCTSTR_LEN + 1); rc = SQLGetConnectAttr((SQLHDBC)conn_res->hdbc, op_integer, (SQLPOINTER)value, ACCTSTR_LEN, NULL); if (rc == SQL_ERROR) { _ruby_ibm_db_check_sql_errors( conn_res->hdbc, SQL_HANDLE_DBC, rc, 1, NULL, -1, 1); } return rb_str_new2((char *)value); } else { rb_throw("Supplied parameter is invalid", Qnil); } /* At this point we know we are to retreive a statement option */ } else { Data_Get_Struct(conn_or_stmt, stmt_handle, stmt_res); /* Check that the option given is not null */ if (!NIL_P(option)) { op_integer=(SQLINTEGER)FIX2INT(option); /* Checking that the option to get is the cursor type because that is what we support here */ if (op_integer == SQL_ATTR_CURSOR_TYPE) { rc = SQLGetStmtAttr((SQLHSTMT)stmt_res->hstmt, op_integer, &value_int, SQL_IS_INTEGER, NULL); if (rc == SQL_ERROR) { _ruby_ibm_db_check_sql_errors(stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); } return INT2NUM(value_int); } else { rb_throw("Supplied parameter is invalid", Qnil); } } else { rb_throw("Supplied parameter is invalid", Qnil); } } } } /* * IBM_DB::get_last_serial_value -- Gets the last inserted serial value from IDS * * ===Description * string IBM_DB::get_last_serial_value ( resource stmt ) * * Returns a string, that is the last inserted value for a serial column for IDS. * The last inserted value could be auto-generated or entered explicitly by the user * This function is valid for IDS (Informix Dynamic Server only) * * ===Parameters * * stmt * A valid statement resource. * * ===Return Values * * Returns a string representation of last inserted serial value on a successful call. * Returns FALSE on failure. */ VALUE ibm_db_get_last_serial_value(int argc, VALUE *argv, VALUE self) { VALUE stmt = Qnil; SQLCHAR *value = NULL; stmt_handle *stmt_res; int rc = 0; rb_scan_args(argc, argv, "1", &stmt); if (!NIL_P(stmt)) { Data_Get_Struct(stmt, stmt_handle, stmt_res); /* We allocate a buffer of size 31 as per recommendations from the CLI IDS team */ value = ALLOC_N(char, 31); rc = SQLGetStmtAttr((SQLHSTMT)stmt_res->hstmt, SQL_ATTR_GET_GENERATED_VALUE, (SQLPOINTER)value, 31, NULL); if ( rc == SQL_ERROR ) { _ruby_ibm_db_check_sql_errors( (SQLHSTMT)stmt_res->hstmt, SQL_HANDLE_STMT, rc, 1, NULL, -1, 1); return Qfalse; } return INT2NUM(atoi(value)); } else { rb_throw("Supplied statement handle is invalid", Qnil); return Qfalse; } } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */