diff --git a/lib/odbc/c_src/Makefile.in b/lib/odbc/c_src/Makefile.in index 3c16e7e29493..85efb541de7b 100644 --- a/lib/odbc/c_src/Makefile.in +++ b/lib/odbc/c_src/Makefile.in @@ -54,7 +54,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/odbc-$(VSN) EI_ROOT = $(ERL_TOP)/lib/erl_interface EI_INCLUDE = -I$(EI_ROOT)/include -I$(EI_ROOT)/include/$(TARGET) ifeq ($(findstring win32,$(TARGET)),win32) -EI_LIB = -lei_md +EI_LIB = -lei_md ENTRY_OBJ=$(ERL_TOP)/erts/obj/$(TARGET)/port_entry.o PORT_ENTRY_POINT=erl_port_entry ENTRY_LDFLAGS=-entry:$(PORT_ENTRY_POINT) @@ -66,7 +66,7 @@ UNIX_TARGET = $(BIN_DIR)/odbcserver EXE_TARGET = $(UNIX_TARGET) endif -C_FILES = odbcserver.c +C_FILES = odbcserver.c H_FILES = odbcserver.h # ---------------------------------------------------- @@ -93,13 +93,13 @@ TARGET_FLAGS = @TARGET_FLAGS@ _create_dirs := $(shell mkdir -p $(OBJ_DIR) $(BIN_DIR)) ifdef EXE_TARGET -opt debug: $(EXE_TARGET) +opt debug: $(EXE_TARGET) else opt debug: endif -clean: - rm -f core *~ +clean: + rm -f core *~ rm -f $(OBJ_DIR)/* docs: @@ -123,7 +123,7 @@ $(OBJ_DIR)/odbcserver.o: odbcserver.c # ---------------------------------------------------- # Release Target -# ---------------------------------------------------- +# ---------------------------------------------------- include $(ERL_TOP)/make/otp_release_targets.mk diff --git a/lib/odbc/c_src/odbcserver.c b/lib/odbc/c_src/odbcserver.c index b22a6cb7afaa..b2f3ac347fa7 100644 --- a/lib/odbc/c_src/odbcserver.c +++ b/lib/odbc/c_src/odbcserver.c @@ -33,13 +33,13 @@ After that c-process will communicate via sockets with erlang. The reason for this is that some odbc-drivers do unexpected things with stdin/stdout messing up the erlang-port communication. - - + + Command protocol between Erlang and C ------------------------------------- The requests from Erlang to C are byte lists composed as [CommandByte, Bytes, StringTerminator] - + CommandByte - constants between 0 and 255 identifing the request defined in odbc_internal.hrl and odbcserver.h @@ -61,17 +61,17 @@ [?OPEN_CONNECTION, C_AutoCommitMode, C_TraceDriver, C_SrollableCursors, C_TupelRow, BinaryStrings, ConnectionStr] - [?CLOSE_CONNECTION] + [?CLOSE_CONNECTION] [?COMMIT_TRANSACTION, CommitMode] [?QUERY, SQLQuery] [?SELECT_COUNT, SQLQuery] [?SELECT, ?SELECT_FIRST] - [?SELECT, ?SELECT_LAST] + [?SELECT, ?SELECT_LAST] [?SELECT, ?SELECT_NEXT] [?SELECT, ?SELECT_PREV] [?SELECT, CursorRelation, integer_to_list(OffSet), ";", integer_to_list(N), ";"] - [?PARAM_QUERY, Binary] + [?PARAM_QUERY, Binary] C_AutoCommitMode - ?ON | ?OFF C_TraceDriver - ?ON | ?OFF @@ -107,11 +107,11 @@ #ifdef UNIX #include #include -#endif +#endif #if defined WIN32 -#include -#include +#include +#include #include #include #include @@ -189,7 +189,7 @@ static byte * receive_erlang_port_msg(void); /* ------------- Socket communication functions --------------------------*/ #ifdef WIN32 -static SOCKET connect_to_erlang(const char *port); +static SOCKET connect_to_erlang(const char *port); static void send_msg(db_result_msg *msg, SOCKET socket); static byte *receive_msg(SOCKET socket); static Boolean receive_msg_part(SOCKET socket, @@ -203,7 +203,7 @@ static void send_msg(db_result_msg *msg, int socket); static byte *receive_msg(int socket); static Boolean receive_msg_part(int socket, byte * buffer, size_t msg_len); static Boolean send_msg_part(int socket, byte * buffer, size_t msg_len); -static void close_socket(int socket); +static void close_socket(int socket); static void tcp_nodelay(int sock); #endif static void clean_socket_lib(void); @@ -241,8 +241,15 @@ static param_array * bind_parameter_arrays(char *buffer, int *index, db_state *state); static void * retrive_param_values(param_array *Param); -static db_column retrive_binary_data(db_column column, int column_nr, - db_state *state); +static void retrive_long_data(db_column column, + int column_nr, + SQLSMALLINT target_type, + char** result_buf, + int* result_len, + int sizeof_element, + int term_bytes, + db_state* state); + static db_result_msg retrive_scrollable_cursor_support_info(db_state *state); static int num_out_params(int num_of_params, param_array* params); @@ -275,7 +282,7 @@ int main(void) #ifdef WIN32 _setmode(_fileno( stdin), _O_BINARY); #endif - + msg = receive_erlang_port_msg(); temp = strtok((char*)msg, ";"); @@ -291,7 +298,7 @@ int main(void) length = strlen(temp); odbc_port = safe_malloc(length + 1); strcpy(odbc_port, temp); - + free(msg); spawn_sup(supervisor_port); @@ -311,14 +318,14 @@ static void spawn_sup(const char *port) { pthread_t thread; int result; - + result = pthread_create(&thread, NULL, (void *(*)(void *))supervise, (void *)port); if (result != 0) DO_EXIT(EXIT_THREAD); } -#endif +#endif void supervise(const char *port) { byte *msg = NULL; @@ -327,9 +334,9 @@ void supervise(const char *port) { SOCKET socket; init_winsock(); #elif defined(UNIX) - int socket; + int socket; #endif - + socket = connect_to_erlang(port); msg = receive_msg(socket); @@ -342,20 +349,20 @@ void supervise(const char *port) { free(msg); close_socket(socket); clean_socket_lib(); - DO_EXIT(reason); + DO_EXIT(reason); } #ifdef WIN32 DWORD WINAPI database_handler(const char *port) #else - void database_handler(const char *port) + void database_handler(const char *port) #endif -{ +{ db_result_msg msg; byte *request_buffer = NULL; db_state state = {NULL, NULL, NULL, NULL, 0, {NULL, 0, 0}, - FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE}; + FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE}; byte request_id; #ifdef WIN32 SOCKET socket; @@ -365,36 +372,36 @@ DWORD WINAPI database_handler(const char *port) #endif socket = connect_to_erlang(port); - - do { + + do { request_buffer = receive_msg(socket); request_id = request_buffer[0]; msg = handle_db_request(request_buffer, &state); send_msg(&msg, socket); /* Send answer to erlang */ - + if (msg.dyn_alloc) { ei_x_free(&(state.dynamic_buffer)); } else { free(msg.buffer); msg.buffer = NULL; - } - + } + free(request_buffer); request_buffer = NULL; - + } while(request_id != CLOSE_CONNECTION); shutdown(socket, 2); close_socket(socket); clean_socket_lib(); - /* Exit will be done by suervisor thread */ + /* Exit will be done by suervisor thread */ #ifdef WIN32 return (DWORD)0; #endif } - + /* Description: Calls the appropriate function to handle the database request recived from the erlang-process. Returns a message to send back to erlang. */ @@ -402,15 +409,15 @@ static db_result_msg handle_db_request(byte *reqstring, db_state *state) { byte *args; byte request_id; - + /* First byte is an index that identifies the requested command the rest is the argument string. */ - request_id = reqstring[0]; + request_id = reqstring[0]; args = reqstring + sizeof(byte); - + switch(request_id) { case OPEN_CONNECTION: - return db_connect(args, state); + return db_connect(args, state); case CLOSE_CONNECTION: return db_close_connection(state); case COMMIT_TRANSACTION: @@ -431,11 +438,11 @@ static db_result_msg handle_db_request(byte *reqstring, db_state *state) return db_describe_table(args, state); default: DO_EXIT(EXIT_FAILURE); /* Should not happen */ - } + } } - + /* ----------------- ODBC-functions ----------------------------------*/ - + /* Description: Tries to open a connection to the database using , returns a message indicating the outcome. */ static db_result_msg db_connect(byte *args, db_state *state) @@ -459,31 +466,34 @@ static db_result_msg db_connect(byte *args, db_state *state) SQLSMALLINT stringlength2ptr = 0, connlen; db_result_msg msg; diagnos diagnos; - byte *connStrIn; + byte *connStrIn; int erl_auto_commit_mode, erl_trace_driver, - use_srollable_cursors, tuple_row_state, binary_strings, - extended_errors; - + use_srollable_cursors, tuple_row_state, binary_strings, + return_types, integer_bigints, + extended_errors; + erl_auto_commit_mode = args[0]; erl_trace_driver = args[1]; use_srollable_cursors = args[2]; tuple_row_state = args[3]; binary_strings = args[4]; extended_errors = args[5]; - connStrIn = args + 6 * sizeof(byte); + return_types = args[6]; + integer_bigints = args[7]; + connStrIn = args + 8 * sizeof(byte); if(tuple_row_state == ON) { - tuple_row(state) = TRUE; + tuple_row(state) = TRUE; } else { - tuple_row(state) = FALSE; + tuple_row(state) = FALSE; } - + if(binary_strings == ON) { - binary_strings(state) = TRUE; + binary_strings(state) = TRUE; } else { - binary_strings(state) = FALSE; + binary_strings(state) = FALSE; } - + if(use_srollable_cursors == ON) { use_srollable_cursors(state) = TRUE; } else { @@ -496,15 +506,27 @@ static db_result_msg db_connect(byte *args, db_state *state) extended_errors(state) = FALSE; } - init_driver(erl_auto_commit_mode, erl_trace_driver, state); - + if(return_types == ON) { + return_types(state) = TRUE; + } else { + return_types(state) = FALSE; + } + + if(integer_bigints == ON) { + integer_bigints(state) = TRUE; + } else { + integer_bigints(state) = FALSE; + } + + init_driver(erl_auto_commit_mode, erl_trace_driver, state); + connlen = (SQLSMALLINT)strlen((const char*)connStrIn); result = SQLDriverConnect(connection_handle(state), NULL, - (SQLCHAR *)connStrIn, - connlen, + (SQLCHAR *)connStrIn, + connlen, connStrOut, (SQLSMALLINT)MAX_CONN_STR_OUT, &stringlength2ptr, SQL_DRIVER_NOPROMPT); - + if (!sql_success(result)) { diagnos = get_diagnos(SQL_HANDLE_DBC, connection_handle(state), extended_errors(state)); strcat((char *)diagnos.error_msg, @@ -512,14 +534,14 @@ static db_result_msg db_connect(byte *args, db_state *state) msg = encode_error_message((char *)diagnos.error_msg, extended_error(state, diagnos.sqlState), diagnos.nativeError ); - + if(!sql_success(SQLFreeHandle(SQL_HANDLE_DBC, connection_handle(state)))) DO_EXIT(EXIT_FREE); if(!sql_success(SQLFreeHandle(SQL_HANDLE_ENV, environment_handle(state)))) DO_EXIT(EXIT_FREE); - + return msg; } @@ -562,7 +584,7 @@ static db_result_msg db_connect(byte *args, db_state *state) /* END */ msg = retrive_scrollable_cursor_support_info(state); - + return msg; } @@ -575,9 +597,9 @@ static db_result_msg db_close_connection(db_state *state) if (associated_result_set(state)) { clean_state(state); } - + result = SQLDisconnect(connection_handle(state)); - + if (!sql_success(result)) { diagnos = get_diagnos(SQL_HANDLE_DBC, connection_handle(state), extended_errors(state)); return encode_error_message((char *)diagnos.error_msg, @@ -594,7 +616,7 @@ static db_result_msg db_close_connection(db_state *state) return encode_atom_message("ok"); } - + /* Description: Requests a commit or rollback operation for all active operations on all statements associated with the connection @@ -616,7 +638,7 @@ static db_result_msg db_end_tran(byte compleationtype, db_state *state) return encode_atom_message("ok"); } } - + /* Description: Executes an sql query and encodes the result set as an erlang term into the message buffer of the returned message-struct. */ static db_result_msg db_query(byte *sql, db_state *state) @@ -625,43 +647,42 @@ static db_result_msg db_query(byte *sql, db_state *state) db_result_msg msg; diagnos diagnos; byte is_error[6]; - + if (associated_result_set(state)) { clean_state(state); } associated_result_set(state) = FALSE; msg = encode_empty_message(); - + if(!sql_success(SQLAllocHandle(SQL_HANDLE_STMT, connection_handle(state), &statement_handle(state)))) DO_EXIT(EXIT_ALLOC); result = SQLExecDirect(statement_handle(state), (SQLCHAR *)sql, SQL_NTS); - + /* SQL_SUCCESS_WITH_INFO at this point may indicate an error in user input. */ if (result != SQL_SUCCESS && result != SQL_NO_DATA_FOUND) { - diagnos = get_diagnos(SQL_HANDLE_STMT, statement_handle(state), extended_errors(state)); - if(strcmp((char *)diagnos.sqlState, INFO) == 0) { - is_error[0] = 0; - strncat((char *)is_error, (char *)diagnos.error_msg, - 5); - str_tolower((char *)&is_error, 5); - /* The ODBC error handling could have been more - predictable but alas ... we try to make the best of - it as we want a nice and clean Erlang API */ - if((strcmp((char *)is_error, "error") == 0)) - { - msg = encode_error_message((char *)diagnos.error_msg,extended_error(state, diagnos.sqlState), diagnos.nativeError); - clean_state(state); - return msg; - } - } else { - msg = encode_error_message((char *)diagnos.error_msg, extended_error(state, diagnos.sqlState), diagnos.nativeError); - clean_state(state); - return msg; - } + diagnos = get_diagnos(SQL_HANDLE_STMT, statement_handle(state), extended_errors(state)); + if(strcmp((char *)diagnos.sqlState, INFO) == 0) { + is_error[0] = 0; + strncat((char *)is_error, (char *)diagnos.error_msg, 5); + str_tolower((char *)&is_error, 5); + /* The ODBC error handling could have been more + predictable but alas ... we try to make the best of + it as we want a nice and clean Erlang API */ + if((strcmp((char *)is_error, "error") == 0)) + { + msg = encode_error_message((char *)diagnos.error_msg,extended_error(state, diagnos.sqlState), diagnos.nativeError); + clean_state(state); + return msg; + } + } else { + msg = encode_error_message((char *)diagnos.error_msg, extended_error(state, diagnos.sqlState), diagnos.nativeError); + clean_state(state); + return msg; + } } ei_x_new_with_version(&dynamic_buffer(state)); @@ -675,23 +696,23 @@ static db_result_msg db_query(byte *sql, db_state *state) ei_x_encode_list_header(&dynamic_buffer(state), 1); msg = encode_result(state); /* We don't want to continue if an error occured */ - if (msg.length != 0) { + if (msg.length != 0) { break; } msg = more_result_sets(state); /* We don't want to continue if an error occured */ - if (msg.length != 0) { + if (msg.length != 0) { break; } - } while (exists_more_result_sets(state)); - + } while (exists_more_result_sets(state)); + ei_x_encode_empty_list(&dynamic_buffer(state)); } - + clean_state(state); - + if (msg.length != 0) { /* An error has occurred */ - ei_x_free(&(dynamic_buffer(state))); + ei_x_free(&(dynamic_buffer(state))); return msg; } else { msg.buffer = (byte*)dynamic_buffer(state).buff; @@ -700,7 +721,7 @@ static db_result_msg db_query(byte *sql, db_state *state) return msg; } } - + /* Description: Executes an sql query. Returns number of rows in the result set. */ static db_result_msg db_select_count(byte *sql, db_state *state) @@ -713,7 +734,7 @@ static db_result_msg db_select_count(byte *sql, db_state *state) clean_state(state); } associated_result_set(state) = TRUE; - + if(!sql_success(SQLAllocHandle(SQL_HANDLE_STMT, connection_handle(state), &statement_handle(state)))) @@ -726,7 +747,7 @@ static db_result_msg db_select_count(byte *sql, db_state *state) (SQLINTEGER)SQL_ATTR_CURSOR_SCROLLABLE, (SQLPOINTER)SQL_SCROLLABLE, (SQLINTEGER)0); } - + if(!sql_success(SQLExecDirect(statement_handle(state), (SQLCHAR *)sql, SQL_NTS))) { diagnos = get_diagnos(SQL_HANDLE_STMT, statement_handle(state), extended_errors(state)); clean_state(state); @@ -734,17 +755,17 @@ static db_result_msg db_select_count(byte *sql, db_state *state) extended_error(state, diagnos.sqlState), diagnos.nativeError); } - + if(!sql_success(SQLNumResultCols(statement_handle(state), &num_of_columns))) DO_EXIT(EXIT_COLS); nr_of_columns(state) = (int)num_of_columns; columns(state) = alloc_column_buffer(nr_of_columns(state)); - + if(!sql_success(SQLRowCount(statement_handle(state), &num_of_rows))) DO_EXIT(EXIT_ROWS); - + return encode_row_count(num_of_rows, state); } @@ -801,7 +822,7 @@ static db_result_msg db_select(byte *args, db_state *state) } msg = encode_empty_message(); - + ei_x_new(&dynamic_buffer(state)); ei_x_new_with_version(&dynamic_buffer(state)); ei_x_encode_tuple_header(&dynamic_buffer(state), 3); @@ -809,19 +830,19 @@ static db_result_msg db_select(byte *args, db_state *state) num_of_columns = nr_of_columns(state); msg = encode_column_name_list(num_of_columns, state); - if (msg.length == 0) { /* If no error has occurred */ + if (msg.length == 0) { /* If no error has occurred */ msg = encode_value_list_scroll(num_of_columns, (SQLSMALLINT)orientation, (SQLINTEGER)offset, n, state); } - + if (msg.length != 0) { /* An error has occurred */ - ei_x_free(&(dynamic_buffer(state))); + ei_x_free(&(dynamic_buffer(state))); return msg; } else { msg.buffer = (byte*)dynamic_buffer(state).buff; - msg.length = dynamic_buffer(state).index; + msg.length = dynamic_buffer(state).index; msg.dyn_alloc = TRUE; return msg; } @@ -837,7 +858,7 @@ static db_result_msg db_param_query(byte *byte_buffer, db_state *state) SQLLEN num_param_values; int i, ver = 0, erl_type = 0, index = 0, size = 0, cols = 0; - long long_num_param_values; + long long_num_param_values; param_status param_status; diagnos diagnos; param_array *params; @@ -853,25 +874,25 @@ static db_result_msg db_param_query(byte *byte_buffer, db_state *state) msg = encode_empty_message(); ei_decode_version(buffer, &index, &ver); - + ei_decode_tuple_header(buffer, &index, &size); ei_get_type(buffer, &index, &erl_type, &size); sql = safe_malloc((sizeof(byte) * (size + 1))); - ei_decode_string(buffer, &index, sql); + ei_decode_string(buffer, &index, sql); ei_decode_long(buffer, &index, &long_num_param_values); num_param_values = (SQLLEN)long_num_param_values; ei_decode_list_header(buffer, &index, &cols); - + init_param_statement(cols, num_param_values, state, ¶m_status); - params = bind_parameter_arrays(buffer, &index, cols, - num_param_values, state); - + params = bind_parameter_arrays(buffer, &index, cols, + num_param_values, state); + if(params != NULL) { result = SQLExecDirect(statement_handle(state), (SQLCHAR *)sql, SQL_NTS); @@ -893,7 +914,7 @@ static db_result_msg db_param_query(byte *byte_buffer, db_state *state) /* SQL_PARAM_DIAG_UNAVAILABLE is entered when the * driver treats arrays of parameters as a monolithic * unit, so it does not generate this individual - * parameter level of error information. */ + * parameter level of error information. */ case SQL_PARAM_DIAG_UNAVAILABLE: break; default: @@ -915,27 +936,27 @@ static db_result_msg db_param_query(byte *byte_buffer, db_state *state) } if(msg.length == 0) { msg.buffer = (byte *)dynamic_buffer(state).buff; - msg.length = dynamic_buffer(state).index; + msg.length = dynamic_buffer(state).index; msg.dyn_alloc = TRUE; } else { /* Error occurred */ ei_x_free(&(dynamic_buffer(state))); } } } - + if(!sql_success(SQLFreeStmt(statement_handle(state), SQL_RESET_PARAMS))) { DO_EXIT(EXIT_FREE); - } + } } else { msg = encode_atom_message("param_badarg"); } - - free(sql); + + free(sql); free_params(¶ms, cols); - - free(param_status.param_status_array); + + free(param_status.param_status_array); if(!sql_success(SQLFreeHandle(SQL_HANDLE_STMT, statement_handle(state)))){ @@ -949,26 +970,26 @@ static db_result_msg db_param_query(byte *byte_buffer, db_state *state) static db_result_msg db_describe_table(byte *sql, db_state *state) { - db_result_msg msg; + db_result_msg msg; SQLSMALLINT num_of_columns; SQLCHAR name[MAX_NAME]; SQLSMALLINT name_len, sql_type, dec_digits, nullable; SQLULEN size; diagnos diagnos; int i; - + if (associated_result_set(state)) { clean_state(state); } associated_result_set(state) = FALSE; msg = encode_empty_message(); - + if(!sql_success(SQLAllocHandle(SQL_HANDLE_STMT, connection_handle(state), &statement_handle(state)))) DO_EXIT(EXIT_ALLOC); - + if (!sql_success(SQLPrepare(statement_handle(state), (SQLCHAR *)sql, SQL_NTS))){ diagnos = get_diagnos(SQL_HANDLE_STMT, statement_handle(state), extended_errors(state)); msg = encode_error_message((char *)diagnos.error_msg, @@ -977,7 +998,7 @@ static db_result_msg db_describe_table(byte *sql, db_state *state) clean_state(state); return msg; } - + if(!sql_success(SQLNumResultCols(statement_handle(state), &num_of_columns))) { diagnos = get_diagnos(SQL_HANDLE_STMT, statement_handle(state), extended_errors(state)); @@ -987,14 +1008,14 @@ static db_result_msg db_describe_table(byte *sql, db_state *state) clean_state(state); return msg; } - + ei_x_new_with_version(&dynamic_buffer(state)); ei_x_encode_tuple_header(&dynamic_buffer(state), 2); ei_x_encode_atom(&dynamic_buffer(state), "ok"); ei_x_encode_list_header(&dynamic_buffer(state), num_of_columns); - + for (i = 0; i < num_of_columns; ++i) { - + if(!sql_success(SQLDescribeCol(statement_handle(state), (SQLUSMALLINT)(i+1), name, sizeof(name), &name_len, @@ -1009,10 +1030,10 @@ static db_result_msg db_describe_table(byte *sql, db_state *state) } ei_x_encode_empty_list(&dynamic_buffer(state)); - + clean_state(state); msg.buffer = (byte *)dynamic_buffer(state).buff; - msg.length = dynamic_buffer(state).index; + msg.length = dynamic_buffer(state).index; msg.dyn_alloc = TRUE; return msg; } @@ -1036,7 +1057,7 @@ static db_result_msg encode_error_message(char *reason, char *errCode, SQLINTEGE { int index; db_result_msg msg; - + index = 0; ei_encode_version(NULL, &index); ei_encode_tuple_header(NULL, &index, 2); @@ -1048,11 +1069,11 @@ static db_result_msg encode_error_message(char *reason, char *errCode, SQLINTEGE ei_encode_long(NULL, &index, nativeError); } ei_encode_string(NULL, &index, reason); - + msg.length = index; msg.buffer = (byte *)safe_malloc(index); msg.dyn_alloc = FALSE; - + index = 0; ei_encode_version((char *)msg.buffer, &index); ei_encode_tuple_header((char *)msg.buffer, &index, 2); @@ -1064,7 +1085,7 @@ static db_result_msg encode_error_message(char *reason, char *errCode, SQLINTEGE ei_encode_long((char *)msg.buffer, &index, nativeError); } ei_encode_string((char *)msg.buffer, &index, reason); - + return msg; } @@ -1073,7 +1094,7 @@ static db_result_msg encode_atom_message(char* atom) { int index; db_result_msg msg; - + index = 0; ei_encode_version(NULL, &index); ei_encode_atom(NULL, &index, atom); @@ -1081,11 +1102,11 @@ static db_result_msg encode_atom_message(char* atom) msg.length = index; msg.buffer = (byte *)safe_malloc(index); msg.dyn_alloc = FALSE; - + index = 0; ei_encode_version((char *)msg.buffer, &index); ei_encode_atom((char *)msg.buffer, &index, atom); - + return msg; } @@ -1103,32 +1124,32 @@ static db_result_msg encode_result(db_state *state) diagnos diagnos; msg = encode_empty_message(); - - if(!sql_success(SQLNumResultCols(statement_handle(state), - &num_of_columns))) { + + if(!sql_success(SQLNumResultCols(statement_handle(state), + &num_of_columns))) { diagnos = get_diagnos(SQL_HANDLE_STMT, statement_handle(state), extended_errors(state)); msg = encode_error_message((char *)diagnos.error_msg, - extended_error(state, diagnos.sqlState), - diagnos.nativeError); + extended_error(state, diagnos.sqlState), + diagnos.nativeError); clean_state(state); return msg; - } - - if (num_of_columns == 0) { - elements = 2; - atom = "updated"; - update = TRUE; + } + + if (num_of_columns == 0) { + elements = 2; + atom = "updated"; + update = TRUE; } else { elements = 3; atom = "selected"; update = FALSE; } - - if(!sql_success(SQLRowCount(statement_handle(state), &RowCountPtr))) { - diagnos = get_diagnos(SQL_HANDLE_STMT, statement_handle(state), extended_errors(state)); + + if(!sql_success(SQLRowCount(statement_handle(state), &RowCountPtr))) { + diagnos = get_diagnos(SQL_HANDLE_STMT, statement_handle(state), extended_errors(state)); msg = encode_error_message((char *)diagnos.error_msg, - extended_error(state, diagnos.sqlState), - diagnos.nativeError); + extended_error(state, diagnos.sqlState), + diagnos.nativeError); clean_state(state); return msg; } @@ -1141,29 +1162,29 @@ static db_result_msg encode_result(db_state *state) NULL))) { DO_EXIT(EXIT_DRIVER_INFO); } - - if(paramBatch == SQL_PARC_BATCH ) { + + if(paramBatch == SQL_PARC_BATCH) { /* Individual row counts (one for each parameter set) are available, sum them up */ do { num_of_rows = num_of_rows + (int)RowCountPtr; msg = more_result_sets(state); /* We don't want to continue if an error occured */ - if (msg.length != 0) { + if (msg.length != 0) { return msg; } if(exists_more_result_sets(state)) { if(!sql_success(SQLRowCount(statement_handle(state), - &RowCountPtr))) { - DO_EXIT(EXIT_ROWS); + &RowCountPtr))) { + DO_EXIT(EXIT_ROWS); } } } while (exists_more_result_sets(state)); } else { /* Row counts are rolled up into one (SQL_PARC_NO_BATCH) */ num_of_rows = (int)RowCountPtr; - } - } else { + } + } else { num_of_rows = (int)RowCountPtr; } ei_x_encode_tuple_header(&dynamic_buffer(state), elements); @@ -1179,7 +1200,7 @@ static db_result_msg encode_result(db_state *state) } return msg; } - + static db_result_msg encode_out_params(db_state *state, int num_of_params, param_array* params, @@ -1192,7 +1213,7 @@ static db_result_msg encode_out_params(db_state *state, db_result_msg msg; TIMESTAMP_STRUCT* ts; msg = encode_empty_message(); - + ei_x_encode_tuple_header(&dynamic_buffer(state), 3); ei_x_encode_atom(&dynamic_buffer(state), "executed"); @@ -1201,14 +1222,14 @@ static db_result_msg encode_out_params(db_state *state, ei_x_encode_list_header(&dynamic_buffer(state), num_param_values); for(j =0; j < num_param_values; j ++){ - + if(tuple_row(state)) { ei_x_encode_tuple_header(&dynamic_buffer(state), num_of_columns); } else { ei_x_encode_list_header(&dynamic_buffer(state), num_of_columns); } - + for (i = 0; i< num_of_params; i++) { if(params[i].input_output_type==SQL_PARAM_INPUT){ continue; @@ -1263,15 +1284,15 @@ static db_result_msg encode_out_params(db_state *state, ei_x_encode_atom(&dynamic_buffer(state), "error"); break; } - } - } + } + } if(!tuple_row(state)) { ei_x_encode_empty_list(&dynamic_buffer(state)); } } ei_x_encode_empty_list(&dynamic_buffer(state)); return msg; -} +} static int num_out_params(int num_of_params, param_array* params) { @@ -1293,16 +1314,16 @@ static db_result_msg encode_result_set(SQLSMALLINT num_of_columns, db_result_msg msg; columns(state) = alloc_column_buffer(num_of_columns); - + msg = encode_column_name_list(num_of_columns, state); - if (msg.length == 0) { /* If no error has occurred */ + if (msg.length == 0) { /* If no error has occurred */ msg = encode_value_list(num_of_columns, state); } free_column_buffer(&(columns(state)), num_of_columns); - + return msg; -} +} /* Description: Encodes the list of column names into the "ei_x" - dynamic_buffer held by the state variable */ @@ -1314,13 +1335,14 @@ static db_result_msg encode_column_name_list(SQLSMALLINT num_of_columns, SQLCHAR name[MAX_NAME]; SQLSMALLINT name_len, sql_type, dec_digits, nullable; SQLULEN size; + SQLRETURN rc; msg = encode_empty_message(); - + ei_x_encode_list_header(&dynamic_buffer(state), num_of_columns); - - for (i = 0; i < num_of_columns; ++i) { + for (i = 0; i < num_of_columns; ++i) { + columns(state)[i].buffer = NULL; if(!sql_success(SQLDescribeCol(statement_handle(state), (SQLSMALLINT)(i+1), name, sizeof(name), &name_len, @@ -1330,42 +1352,49 @@ static db_result_msg encode_column_name_list(SQLSMALLINT num_of_columns, if(sql_type == SQL_LONGVARCHAR || sql_type == SQL_LONGVARBINARY || sql_type == SQL_WLONGVARCHAR) size = MAXCOLSIZE; - + (columns(state)[i]).type.decimal_digits = dec_digits; (columns(state)[i]).type.sql = sql_type; - (columns(state)[i]).type.col_size = size; - + // we are not using col_size anymore, because we would find the final + // buffer length in retrive_long_data + (columns(state)[i]).type.col_size = 0; + msg = map_sql_2_c_column(&columns(state)[i], state); - if (msg.length > 0) { + if (msg.length > 0) return msg; /* An error has occurred */ - } else { - if (columns(state)[i].type.len > 0) { + + if (columns(state)[i].type.len > 0) { + switch (columns(state)[i].type.c) { + case SQL_C_WCHAR: + case SQL_C_CHAR: + case SQL_C_BINARY: + // retrived later by retrive_long_data + break; + default: columns(state)[i].buffer = (char *)safe_malloc(columns(state)[i].type.len); - - if (columns(state)[i].type.c == SQL_C_BINARY) { - /* retrived later by retrive_binary_data */ - } else { - if(!sql_success( - SQLBindCol - (statement_handle(state), - (SQLSMALLINT)(i+1), - columns(state)[i].type.c, - columns(state)[i].buffer, - columns(state)[i].type.len, - &columns(state)[i].type.strlen_or_indptr))) - DO_EXIT(EXIT_BIND); + rc = SQLBindCol(statement_handle(state), + (SQLSMALLINT)(i+1), + columns(state)[i].type.c, + columns(state)[i].buffer, + columns(state)[i].type.len, + &columns(state)[i].type.strlen_or_indptr); + if (!sql_success(rc)) { + DO_EXIT(EXIT_BIND); } - ei_x_encode_string_len(&dynamic_buffer(state), - (char *)name, name_len); } - else { - columns(state)[i].type.len = 0; - columns(state)[i].buffer = NULL; - } - } - } - ei_x_encode_empty_list(&dynamic_buffer(state)); + if return_types(state) { + ei_x_encode_tuple_header(&dynamic_buffer(state), 2); + encode_data_type(sql_type, size, dec_digits, state); + } + ei_x_encode_string_len(&dynamic_buffer(state), (char *)name, name_len); + + } else { + columns(state)[i].type.len = 0; + columns(state)[i].buffer = NULL; + } + } // end of for loop + ei_x_encode_empty_list(&dynamic_buffer(state)); return msg; } @@ -1380,11 +1409,11 @@ static db_result_msg encode_value_list(SQLSMALLINT num_of_columns, db_result_msg msg; msg = encode_empty_message(); - + for (;;) { /* fetch the next row */ - result = SQLFetch(statement_handle(state)); - + result = SQLFetch(statement_handle(state)); + if (result == SQL_NO_DATA) /* Reached end of result set */ { break; @@ -1398,7 +1427,7 @@ static db_result_msg encode_value_list(SQLSMALLINT num_of_columns, } else { ei_x_encode_list_header(&dynamic_buffer(state), num_of_columns); } - + for (i = 0; i < num_of_columns; i++) { encode_column_dyn(columns(state)[i], i, state); } @@ -1406,7 +1435,8 @@ static db_result_msg encode_value_list(SQLSMALLINT num_of_columns, if(!tuple_row(state)) { ei_x_encode_empty_list(&dynamic_buffer(state)); } - } + + } ei_x_encode_empty_list(&dynamic_buffer(state)); return msg; } @@ -1424,19 +1454,19 @@ static db_result_msg encode_value_list_scroll(SQLSMALLINT num_of_columns, db_result_msg msg; msg = encode_empty_message(); - + for (j = 0; j < N; j++) { if((j > 0) && (Orientation == SQL_FETCH_ABSOLUTE)) { OffSet++; } - + if((j == 1) && (Orientation == SQL_FETCH_RELATIVE)) { OffSet = 1; } result = SQLFetchScroll(statement_handle(state), Orientation, - OffSet); - + OffSet); + if (result == SQL_NO_DATA) /* Reached end of result set */ { break; @@ -1455,7 +1485,7 @@ static db_result_msg encode_value_list_scroll(SQLSMALLINT num_of_columns, if(!tuple_row(state)) { ei_x_encode_empty_list(&dynamic_buffer(state)); } - } + } ei_x_encode_empty_list(&dynamic_buffer(state)); return msg; } @@ -1466,7 +1496,7 @@ static db_result_msg encode_row_count(SQLINTEGER num_of_rows, { db_result_msg msg; int index; - + index = 0; ei_encode_version(NULL, &index); ei_encode_tuple_header(NULL, &index, 2); @@ -1476,16 +1506,16 @@ static db_result_msg encode_row_count(SQLINTEGER num_of_rows, ei_encode_atom(NULL, &index, "undefined"); } else { ei_encode_long(NULL, &index, num_of_rows); - } + } msg.length = index; msg.buffer = (byte *)safe_malloc(index); msg.dyn_alloc = FALSE; - + index = 0; ei_encode_version((char *)msg.buffer, &index); ei_encode_tuple_header((char *)msg.buffer, &index, 2); ei_encode_atom((char *)msg.buffer, &index, "ok"); - + if(num_of_rows == -1) { ei_encode_atom((char *)msg.buffer, &index, "undefined"); @@ -1494,7 +1524,19 @@ static db_result_msg encode_row_count(SQLINTEGER num_of_rows, } return msg; } - + +void encode_binary_or_string(byte bin_strings, char* bufferptr, int result_len, db_state* state) { + if (bufferptr) { + if (bin_strings) { + ei_x_encode_binary(&dynamic_buffer(state), bufferptr, result_len); + } else { + ei_x_encode_string_len(&dynamic_buffer(state), bufferptr, result_len); + } + } else { + ei_x_encode_atom(&dynamic_buffer(state), "null"); + } +} + /* Description: Encodes the a column value into the "ei_x" - dynamic_buffer held by the state variable */ static void encode_column_dyn(db_column column, int column_nr, @@ -1504,32 +1546,71 @@ static void encode_column_dyn(db_column column, int column_nr, if (column.type.len == 0 || column.type.strlen_or_indptr == SQL_NULL_DATA) { ei_x_encode_atom(&dynamic_buffer(state), "null"); + } else if ((column.type.sql == SQL_BIGINT) && + (column.type.c == SQL_C_CHAR) && + integer_bigints(state)) { + char *bufferptr = NULL; + int result_len = 0; /* in bytes */ + retrive_long_data(column, column_nr, SQL_C_CHAR, &bufferptr, &result_len, 1, 1, state); + + if (result_len == 0) { + ei_x_encode_atom(&dynamic_buffer(state), "null"); + } else if (bufferptr[0] == 45) { // minus sign + char *endptr; + long long p = strtoll(bufferptr, &endptr, 10); + ei_x_encode_longlong(&dynamic_buffer(state), p); + } else { + char *endptr; + unsigned long long p = strtoull(bufferptr, &endptr, 10); + ei_x_encode_ulonglong(&dynamic_buffer(state), p); + } + if (bufferptr) + free(bufferptr); } else { switch(column.type.c) { case SQL_C_TYPE_TIMESTAMP: - ts = (TIMESTAMP_STRUCT*)column.buffer; - ei_x_encode_tuple_header(&dynamic_buffer(state), 2); - ei_x_encode_tuple_header(&dynamic_buffer(state), 3); - ei_x_encode_ulong(&dynamic_buffer(state), ts->year); - ei_x_encode_ulong(&dynamic_buffer(state), ts->month); - ei_x_encode_ulong(&dynamic_buffer(state), ts->day); - ei_x_encode_tuple_header(&dynamic_buffer(state), 3); - ei_x_encode_ulong(&dynamic_buffer(state), ts->hour); - ei_x_encode_ulong(&dynamic_buffer(state), ts->minute); - ei_x_encode_ulong(&dynamic_buffer(state), ts->second); - break; - case SQL_C_CHAR: - if binary_strings(state) { - ei_x_encode_binary(&dynamic_buffer(state), - column.buffer,column.type.strlen_or_indptr); - } else { - ei_x_encode_string(&dynamic_buffer(state), column.buffer); - } + ts = (TIMESTAMP_STRUCT*)column.buffer; + ei_x_encode_tuple_header(&dynamic_buffer(state), 2); + ei_x_encode_tuple_header(&dynamic_buffer(state), 3); + ei_x_encode_ulong(&dynamic_buffer(state), ts->year); + ei_x_encode_ulong(&dynamic_buffer(state), ts->month); + ei_x_encode_ulong(&dynamic_buffer(state), ts->day); + ei_x_encode_tuple_header(&dynamic_buffer(state), 3); + ei_x_encode_ulong(&dynamic_buffer(state), ts->hour); + ei_x_encode_ulong(&dynamic_buffer(state), ts->minute); + ei_x_encode_ulong(&dynamic_buffer(state), ts->second); + break; + case SQL_C_CHAR: { + char *bufferptr = NULL; + int result_len = 0; /* in bytes */ + retrive_long_data(column, column_nr, SQL_C_CHAR, &bufferptr, &result_len, 1, 1, state); + encode_binary_or_string(binary_strings(state), bufferptr, result_len, state); + if (bufferptr) + free(bufferptr); + break; + } + case SQL_C_BINARY: { + char *bufferptr = NULL; + int result_len = 0; /* in bytes */ + retrive_long_data(column, column_nr, SQL_C_BINARY, &bufferptr, &result_len, 1, 0, state); + encode_binary_or_string(binary_strings(state), bufferptr, result_len, state); + if (bufferptr) + free(bufferptr); break; - case SQL_C_WCHAR: - ei_x_encode_binary(&dynamic_buffer(state), - column.buffer,column.type.strlen_or_indptr); + } + case SQL_C_WCHAR: { + // Read chunks of data. + // There is pretty high change to get <<0,0,0...>> as a column data on some systems otherwise + // https://docs.microsoft.com/en-us/sql/relational-databases/native-client/features/odbc-driver-behavior-change-when-handling-character-conversions?view=sql-server-ver15 + char *bufferptr = NULL; + int result_len = 0; /* in bytes */ + const int sizeof_element = sizeof(SQLWCHAR); // 2 bytes + retrive_long_data(column, column_nr, SQL_C_WCHAR, &bufferptr, &result_len, sizeof_element, sizeof_element, state); + encode_binary_or_string(1, bufferptr, result_len, state); + if (bufferptr) + free(bufferptr); break; + } case SQL_C_SLONG: ei_x_encode_long(&dynamic_buffer(state), *(SQLINTEGER*)column.buffer); @@ -1540,22 +1621,13 @@ static void encode_column_dyn(db_column column, int column_nr, break; case SQL_C_BIT: ei_x_encode_atom(&dynamic_buffer(state), - column.buffer[0]?"true":"false"); - break; - case SQL_C_BINARY: - column = retrive_binary_data(column, column_nr, state); - if binary_strings(state) { - ei_x_encode_binary(&dynamic_buffer(state), - column.buffer,column.type.strlen_or_indptr); - } else { - ei_x_encode_string(&dynamic_buffer(state), (void *)column.buffer); - } + column.buffer[0]? "true" : "false"); break; default: ei_x_encode_atom(&dynamic_buffer(state), "error"); break; } - } + } } static void encode_data_type(SQLSMALLINT sql_type, SQLINTEGER size, @@ -1563,33 +1635,33 @@ static void encode_data_type(SQLSMALLINT sql_type, SQLINTEGER size, { switch(sql_type) { case SQL_CHAR: - ei_x_encode_tuple_header(&dynamic_buffer(state), 2); + ei_x_encode_tuple_header(&dynamic_buffer(state), 2); ei_x_encode_atom(&dynamic_buffer(state), "sql_char"); ei_x_encode_long(&dynamic_buffer(state), size); break; case SQL_VARCHAR: - ei_x_encode_tuple_header(&dynamic_buffer(state), 2); + ei_x_encode_tuple_header(&dynamic_buffer(state), 2); ei_x_encode_atom(&dynamic_buffer(state), "sql_varchar"); ei_x_encode_long(&dynamic_buffer(state), size); break; case SQL_WCHAR: - ei_x_encode_tuple_header(&dynamic_buffer(state), 2); + ei_x_encode_tuple_header(&dynamic_buffer(state), 2); ei_x_encode_atom(&dynamic_buffer(state), "sql_wchar"); ei_x_encode_long(&dynamic_buffer(state), size); break; case SQL_WVARCHAR: - ei_x_encode_tuple_header(&dynamic_buffer(state), 2); + ei_x_encode_tuple_header(&dynamic_buffer(state), 2); ei_x_encode_atom(&dynamic_buffer(state), "sql_wvarchar"); ei_x_encode_long(&dynamic_buffer(state), size); break; case SQL_NUMERIC: - ei_x_encode_tuple_header(&dynamic_buffer(state), 3); + ei_x_encode_tuple_header(&dynamic_buffer(state), 3); ei_x_encode_atom(&dynamic_buffer(state), "sql_numeric"); ei_x_encode_long(&dynamic_buffer(state), size); ei_x_encode_long(&dynamic_buffer(state), decimal_digits); break; case SQL_DECIMAL: - ei_x_encode_tuple_header(&dynamic_buffer(state), 3); + ei_x_encode_tuple_header(&dynamic_buffer(state), 3); ei_x_encode_atom(&dynamic_buffer(state), "sql_decimal"); ei_x_encode_long(&dynamic_buffer(state), size); ei_x_encode_long(&dynamic_buffer(state), decimal_digits); @@ -1607,7 +1679,7 @@ static void encode_data_type(SQLSMALLINT sql_type, SQLINTEGER size, ei_x_encode_atom(&dynamic_buffer(state), "sql_real"); break; case SQL_FLOAT: - ei_x_encode_tuple_header(&dynamic_buffer(state), 2); + ei_x_encode_tuple_header(&dynamic_buffer(state), 2); ei_x_encode_atom(&dynamic_buffer(state), "sql_float"); ei_x_encode_long(&dynamic_buffer(state), size); break; @@ -1684,7 +1756,7 @@ static Boolean decode_params(db_state *state, char *buffer, int *index, param_ar param_array* param; TIMESTAMP_STRUCT* ts; char atomarray[MAXATOMLEN+1]; - + ei_get_type(buffer, index, &erl_type, &size); param = &(*params)[i]; @@ -1719,6 +1791,10 @@ static Boolean decode_params(db_state *state, char *buffer, int *index, param_ar ei_decode_binary(buffer, index, &(param->values.string[param->offset]), &bin_size); param->offset += param->type.len; break; + case SQL_C_BINARY: + ei_decode_binary(buffer, index, &(param->values.string[param->offset]), &bin_size); + param->offset += param->type.len; + break; case SQL_C_TYPE_TIMESTAMP: ts = (TIMESTAMP_STRUCT*) param->values.string; ei_decode_tuple_header(buffer, index, &size); @@ -1743,28 +1819,28 @@ static Boolean decode_params(db_state *state, char *buffer, int *index, param_ar (erl_type == ERL_LARGE_BIG_EXT))) { return FALSE; } - + if(ei_decode_long(buffer, index, &l64)) { return FALSE; } - + /* For 64-bit platforms we downcast 8-byte long * to 4-byte SQLINTEGER, checking for overflow */ - + if(l64>INT_MAX || l64values.integer[j]=(SQLINTEGER)l64; break; - - case SQL_C_DOUBLE: - if((erl_type != ERL_FLOAT_EXT)) { + + case SQL_C_DOUBLE: + if((erl_type != ERL_FLOAT_EXT)) { return FALSE; - } - ei_decode_double(buffer, index, &(param->values.floating[j])); + } + ei_decode_double(buffer, index, &(param->values.floating[j])); break; - + case SQL_C_BIT: if((erl_type != ERL_ATOM_EXT)) { return FALSE; @@ -1779,21 +1855,21 @@ static Boolean decode_params(db_state *state, char *buffer, int *index, param_ar default: return FALSE; } - + return TRUE; -} +} /*------------- Erlang port communication functions ----------------------*/ -/* read from stdin */ +/* read from stdin */ #ifdef WIN32 static int read_exact(byte *buffer, int len) { HANDLE standard_input = GetStdHandle(STD_INPUT_HANDLE); - + unsigned read_result; unsigned sofar = 0; - + if (!len) { /* Happens for "empty packages */ return 0; } @@ -1810,18 +1886,18 @@ static int read_exact(byte *buffer, int len) return len; } } -} +} #elif defined(UNIX) static int read_exact(byte *buffer, int len) { int i, got = 0; - + do { if ((i = read(0, buffer + got, len - got)) <= 0) return(i); got += i; } while (got < len); return len; - + } #endif @@ -1853,13 +1929,13 @@ static byte * receive_erlang_port_msg(void) } len = length_buffer_to_size(lengthstr); - + if (len <= 0 || len > 1024) { DO_EXIT(EXIT_STDIN_HEADER); } buffer = (byte *)safe_malloc(len); - + if (read_exact(buffer, len) <= 0) { DO_EXIT(EXIT_STDIN_BODY); } @@ -1870,7 +1946,7 @@ static byte * receive_erlang_port_msg(void) return buffer; } - + /* ------------- Socket communication functions --------------------------*/ #if defined(WIN32) @@ -1895,7 +1971,7 @@ static int connect_to_erlang(const char *port) sin6.sin6_port = htons ((unsigned short)atoi(port)); sin6.sin6_family = AF_INET6; sin6.sin6_addr = in6addr_loopback; - + if (connect(sock, (struct sockaddr*)&sin6, sizeof(sin6)) == 0) { /* Enable TCP_NODELAY to disable Nagel's socket algorithm. (Removes ~40ms delay on Redhat ES 6). */ #ifdef UNIX @@ -1911,7 +1987,7 @@ static int connect_to_erlang(const char *port) sin.sin_port = htons ((unsigned short)atoi(port)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - + if (connect(sock, (struct sockaddr*)&sin, sizeof(sin)) != 0) { close_socket(sock); DO_EXIT(EXIT_SOCKET_CONNECT); @@ -1947,22 +2023,22 @@ static void close_socket(int socket) #endif #ifdef WIN32 -static byte * receive_msg(SOCKET socket) +static byte * receive_msg(SOCKET socket) #elif defined(UNIX) -static byte * receive_msg(int socket) +static byte * receive_msg(int socket) #endif { byte lengthstr[LENGTH_INDICATOR_SIZE]; size_t msg_len; byte *buffer = NULL; - + if(!receive_msg_part(socket, lengthstr, LENGTH_INDICATOR_SIZE)) { close_socket(socket); DO_EXIT(EXIT_SOCKET_RECV_HEADER); } - + msg_len = length_buffer_to_size(lengthstr); - + buffer = (byte *)safe_malloc(msg_len); if(!receive_msg_part(socket, buffer, msg_len)) { @@ -1975,37 +2051,37 @@ static byte * receive_msg(int socket) #ifdef WIN32 static Boolean receive_msg_part(SOCKET socket, byte * buffer, size_t msg_len) -#elif defined(UNIX) +#elif defined(UNIX) static Boolean receive_msg_part(int socket, byte * buffer, size_t msg_len) #endif { int nr_bytes_received = 0; - + nr_bytes_received = recv(socket, (void *)buffer, msg_len, 0); - + if(nr_bytes_received == msg_len) { - return TRUE; + return TRUE; } else if(nr_bytes_received > 0 && nr_bytes_received < msg_len) { return receive_msg_part(socket, buffer + nr_bytes_received, - msg_len - nr_bytes_received); - } else if(nr_bytes_received == -1) { - return FALSE; + msg_len - nr_bytes_received); + } else if(nr_bytes_received == -1) { + return FALSE; } else { /* nr_bytes_received > msg_len */ - close_socket(socket); - DO_EXIT(EXIT_SOCKET_RECV_MSGSIZE); + close_socket(socket); + DO_EXIT(EXIT_SOCKET_RECV_MSGSIZE); } } #ifdef WIN32 static void send_msg(db_result_msg *msg, SOCKET socket) -#elif defined(UNIX) +#elif defined(UNIX) static void send_msg(db_result_msg *msg, int socket) #endif { byte lengthstr[LENGTH_INDICATOR_SIZE]; int len; len = msg ->length; - + lengthstr[0] = (len >> 24) & 0x000000FF; lengthstr[1] = (len >> 16) & 0x000000FF; lengthstr[2] = (len >> 8) & 0x000000FF; @@ -2015,7 +2091,7 @@ static void send_msg(db_result_msg *msg, int socket) close_socket(socket); DO_EXIT(EXIT_SOCKET_SEND_HEADER); } - + if(!send_msg_part(socket, msg->buffer, len)) { close_socket(socket); DO_EXIT(EXIT_SOCKET_SEND_BODY); @@ -2024,24 +2100,24 @@ static void send_msg(db_result_msg *msg, int socket) #ifdef WIN32 static Boolean send_msg_part(SOCKET socket, byte * buffer, size_t msg_len) -#elif defined(UNIX) +#elif defined(UNIX) static Boolean send_msg_part(int socket, byte * buffer, size_t msg_len) #endif { int nr_bytes_sent = 0; - + nr_bytes_sent = send(socket, (void *)buffer, msg_len, 0); - + if(nr_bytes_sent == msg_len) { - return TRUE; + return TRUE; } else if(nr_bytes_sent > 0 && nr_bytes_sent < msg_len) { return send_msg_part(socket, buffer + nr_bytes_sent, - msg_len - nr_bytes_sent); - } else if(nr_bytes_sent == -1) { - return FALSE; + msg_len - nr_bytes_sent); + } else if(nr_bytes_sent == -1) { + return FALSE; } else { /* nr_bytes_sent > msg_len */ - close_socket(socket); - DO_EXIT(EXIT_SOCKET_SEND_MSGSIZE); + close_socket(socket); + DO_EXIT(EXIT_SOCKET_SEND_MSGSIZE); } } @@ -2051,9 +2127,9 @@ static void init_winsock(void) WORD wVersionRequested; WSADATA wsaData; int err; - + wVersionRequested = MAKEWORD( 2, 0 ); - + err = WSAStartup( wVersionRequested, &wsaData ); if ( err != 0 ) { DO_EXIT(EXIT_OLD_WINSOCK); @@ -2073,15 +2149,15 @@ static void clean_socket_lib(void) WSACleanup(); #endif } - + /*------------- Memmory handling funtions -------------------------------*/ static void *safe_malloc(int size) { void *memory; - + memory = (void *)malloc(size); - if (memory == NULL) + if (memory == NULL) DO_EXIT(EXIT_ALLOC); return memory; @@ -2100,20 +2176,20 @@ static void *safe_realloc(void *ptr, int size) } return memory; } - + /* Description: Allocate memory for n columns */ static db_column * alloc_column_buffer(int n) { int i; db_column *columns; - + columns = (db_column *)safe_malloc(n * sizeof(db_column)); for(i = 0; i < n; i++) columns[i].buffer = NULL; - + return columns; } - + /* Description: Deallocate memory allocated by alloc_column_buffer */ static void free_column_buffer(db_column **columns, int n) { @@ -2136,13 +2212,13 @@ static void free_params(param_array **params, int cols) for (i = 0; i < cols; i++) { if((*params)[i].type.strlen_or_indptr_array != NULL){ free((*params)[i].type.strlen_or_indptr_array); - } + } free(retrive_param_values(&((*params)[i]))); - } + } free(*params); *params = NULL; } -} +} /* Description: Frees resources associated with the current statement handle keeped in the state.*/ @@ -2159,7 +2235,7 @@ static void clean_state(db_state *state) columns(state) = NULL; nr_of_columns(state) = 0; } - + /* Allocates and fill with default value StrLen_or_IndPtr array */ static SQLLEN* alloc_strlen_indptr(int n, int val) { @@ -2178,9 +2254,9 @@ static SQLLEN* alloc_strlen_indptr(int n, int val) static void init_driver(int erl_auto_commit_mode, int erl_trace_driver, db_state *state) { - + SQLLEN auto_commit_mode, trace_driver; - + if(erl_auto_commit_mode == ON) { auto_commit_mode = SQL_AUTOCOMMIT_ON; } else { @@ -2192,7 +2268,7 @@ static void init_driver(int erl_auto_commit_mode, int erl_trace_driver, } else { trace_driver = SQL_OPT_TRACE_OFF; } - + if(!sql_success(SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &environment_handle(state)))) @@ -2220,18 +2296,19 @@ static void init_driver(int erl_auto_commit_mode, int erl_trace_driver, DO_EXIT(EXIT_CONNECTION); } +// The data from here is passed into SQLBindParameter function static void init_param_column(param_array *params, char *buffer, int *index, int num_param_values, db_state* state) { long user_type, precision, scale, length; long in_or_out; - + ei_decode_long(buffer, index, &user_type); params->type.strlen_or_indptr = (SQLLEN)NULL; params->type.strlen_or_indptr_array = NULL; params->type.decimal_digits = (SQLINTEGER)0; - + switch (user_type) { case USER_SMALL_INT: params->type.sql = SQL_SMALLINT; @@ -2279,7 +2356,7 @@ static void init_param_column(param_array *params, char *buffer, int *index, params->values.string = (byte *)safe_malloc(num_param_values * sizeof(byte)* params->type.len); - } + } break; case USER_CHAR: case USER_VARCHAR: @@ -2298,18 +2375,21 @@ static void init_param_column(param_array *params, char *buffer, int *index, params->values.string = (byte *)safe_malloc(num_param_values * sizeof(byte)* params->type.len); - + break; case USER_WCHAR: case USER_WVARCHAR: case USER_WLONGVARCHAR: switch (user_type) { case USER_WCHAR: - params->type.sql = SQL_WCHAR; break; + params->type.sql = SQL_WCHAR; + break; case USER_WVARCHAR: - params->type.sql = SQL_WVARCHAR; break; + params->type.sql = SQL_WVARCHAR; + break; default: - params->type.sql = SQL_WLONGVARCHAR; break; + params->type.sql = SQL_WLONGVARCHAR; + break; } ei_decode_long(buffer, index, &length); /* Max string length + string terminator */ @@ -2320,7 +2400,20 @@ static void init_param_column(param_array *params, char *buffer, int *index, = alloc_strlen_indptr(num_param_values, SQL_NTS); params->values.string = (byte *)safe_malloc(num_param_values * sizeof(byte) * params->type.len); - + + break; + // also SQL_BLOB + case USER_LONGVARBINARY: + // We need to properly setup and initialize StrLen_or_IndPtr argument. + // For binary buffers, the value of StrLen_or_IndPtr must be the length of the data held in the buffer. + ei_decode_long(buffer, index, &length); + params->type.c = SQL_C_BINARY; + params->type.sql = SQL_LONGVARBINARY; + params->type.col_size = length; + params->type.len = length; + params->type.strlen_or_indptr_array + = alloc_strlen_indptr(num_param_values, length); + params->values.string = (byte *)safe_malloc(num_param_values * length); break; case USER_TIMESTAMP: params->type.sql = SQL_TYPE_TIMESTAMP; @@ -2369,14 +2462,17 @@ static void init_param_column(param_array *params, char *buffer, int *index, ei_decode_long(buffer, index, &in_or_out); switch((in_or_out_type)in_or_out){ case(ERL_ODBC_OUT): - out_params(state) = TRUE; - params->input_output_type = SQL_PARAM_OUTPUT; break; + out_params(state) = TRUE; + params->input_output_type = SQL_PARAM_OUTPUT; + break; case(ERL_ODBC_INOUT): - out_params(state) = TRUE; - params->input_output_type = SQL_PARAM_INPUT_OUTPUT; break; + out_params(state) = TRUE; + params->input_output_type = SQL_PARAM_INPUT_OUTPUT; + break; case(ERL_ODBC_IN): default: - params->input_output_type = SQL_PARAM_INPUT; break; + params->input_output_type = SQL_PARAM_INPUT; + break; } } @@ -2394,13 +2490,13 @@ static void init_param_statement(int cols, SQLLEN num_param_values, } status -> params_processed = 0; - + if(!sql_success(SQLAllocHandle(SQL_HANDLE_STMT, connection_handle(state), &statement_handle(state)))) { DO_EXIT(EXIT_ALLOC); } - + if(num_param_values <= 1) return; if(!sql_success(SQLSetStmtAttr(statement_handle(state), @@ -2417,13 +2513,13 @@ static void init_param_statement(int cols, SQLLEN num_param_values, 0))) { DO_EXIT(EXIT_PARAM_ARRAY); } - + if(!sql_success(SQLSetStmtAttr(statement_handle(state), SQL_ATTR_PARAM_STATUS_PTR, (status -> param_status_array), 0))) { DO_EXIT(EXIT_PARAM_ARRAY); } - + if(!sql_success(SQLSetStmtAttr(statement_handle(state), SQL_ATTR_PARAMS_PROCESSED_PTR, &(status -> params_processed), 0))) { @@ -2436,14 +2532,11 @@ static void map_dec_num_2_c_column(col_type *type, int precision, int scale) type -> col_size = (SQLINTEGER)precision; type -> decimal_digits = (SQLSMALLINT)scale; - if(precision >= 0 && precision <= 4 && scale == 0) { - type->len = sizeof(SQLINTEGER); - type->c = SQL_C_SLONG; - } else if(precision >= 5 && precision <= 9 && scale == 0) { + if(precision >= 0 && precision <= 9 && scale == 0) { type->len = sizeof(SQLINTEGER); type->c = SQL_C_SLONG; - } else if((precision >= 10 && precision <= 15 && scale == 0) - || (precision <= 15 && scale > 0)) { + } else if((precision >= 10 && precision <= 15 && scale == 0) + || (precision <= 15 && scale > 0)) { type->len = sizeof(double); type->c = SQL_C_DOUBLE; } else { @@ -2459,7 +2552,7 @@ static db_result_msg map_sql_2_c_column(db_column* column, db_state *state) db_result_msg msg; msg = encode_empty_message(); - + switch(column -> type.sql) { case SQL_CHAR: case SQL_VARCHAR: @@ -2476,7 +2569,7 @@ static db_result_msg map_sql_2_c_column(db_column* column, db_state *state) case SQL_WCHAR: case SQL_WVARCHAR: case SQL_WLONGVARCHAR: - column -> type.len = (column -> type.col_size + 1)*sizeof(SQLWCHAR); + column -> type.len = (column -> type.col_size + 1)*sizeof(SQLWCHAR); column -> type.c = SQL_C_WCHAR; column -> type.strlen_or_indptr = SQL_NTS; break; @@ -2540,11 +2633,11 @@ static param_array * bind_parameter_arrays(char *buffer, int *index, long dummy; void *Values; param_array *params; - - params = (param_array *)safe_malloc(cols * sizeof(param_array)); - + + params = (param_array *)safe_malloc(cols * sizeof(param_array)); + for (i = 0; i < cols; i++) { - + ei_get_type(buffer, index, &erl_type, &size); if(erl_type == ERL_NIL_EXT) { @@ -2563,18 +2656,18 @@ static param_array * bind_parameter_arrays(char *buffer, int *index, of all integer parameter value lists. This is to avoid that the list will be encoded as a string if all values are less than 256 */ - ei_decode_long(buffer, index, &dummy); + ei_decode_long(buffer, index, &dummy); } - + for (j = 0; j < num_param_values; j++) { if(!decode_params(state, buffer, index, ¶ms, i, j, num_param_values)) { - /* An input parameter was not of the expected type */ + /* An input parameter was not of the expected type */ free_params(¶ms, i); - return params; + return NULL; } } - Values = retrive_param_values(¶ms[i]); + Values = retrive_param_values(¶ms[i]); if(!sql_success( SQLBindParameter(statement_handle(state), i + 1, @@ -2591,17 +2684,18 @@ static param_array * bind_parameter_arrays(char *buffer, int *index, return params; } - + static void * retrive_param_values(param_array *Param) { switch(Param->type.c) { case SQL_C_CHAR: case SQL_C_WCHAR: case SQL_C_TYPE_TIMESTAMP: + case SQL_C_BINARY: return (void *)Param->values.string; case SQL_C_SLONG: return (void *)Param->values.integer; - case SQL_C_DOUBLE: + case SQL_C_DOUBLE: return (void *)Param->values.floating; case SQL_C_BIT: return (void *)Param->values.boolean; @@ -2610,63 +2704,18 @@ static void * retrive_param_values(param_array *Param) } } -/* Description: More than one call to SQLGetData may be required to retrieve - data from a single column with binary data. SQLGetData then returns - SQL_SUCCESS_WITH_INFO nd the SQLSTATE will have the value 01004 (Data - truncated). The application can then use the same column number to - retrieve subsequent parts of the data until SQLGetData returns - SQL_SUCCESS, indicating that all data for the column has been retrieved. -*/ - -static db_column retrive_binary_data(db_column column, int column_nr, - db_state *state) -{ - char *outputptr; - int blocklen, outputlen, result; - diagnos diagnos; - - blocklen = column.type.len; - outputptr = column.buffer; - result = SQLGetData(statement_handle(state), (SQLSMALLINT)(column_nr+1), - SQL_C_CHAR, outputptr, - blocklen, &column.type.strlen_or_indptr); - - while (result == SQL_SUCCESS_WITH_INFO) { - - diagnos = get_diagnos(SQL_HANDLE_STMT, statement_handle(state), extended_errors(state)); - - if(strcmp((char *)diagnos.sqlState, TRUNCATED) == 0) { - outputlen = column.type.len - 1; - column.type.len = outputlen + blocklen; - column.buffer = - safe_realloc((void *)column.buffer, column.type.len); - outputptr = column.buffer + outputlen; - result = SQLGetData(statement_handle(state), - (SQLSMALLINT)(column_nr+1), SQL_C_CHAR, - outputptr, blocklen, - &column.type.strlen_or_indptr); - } - } - - if (result == SQL_SUCCESS) { - return column; - } else { - DO_EXIT(EXIT_BIN); - } -} - -/* Description: Returns information about support for scrollable cursors */ +/* Description: Returns information about support for scrollable cursors */ static db_result_msg retrive_scrollable_cursor_support_info(db_state *state) { db_result_msg msg; SQLUINTEGER supportMask; - + ei_x_new_with_version(&dynamic_buffer(state)); ei_x_encode_tuple_header(&dynamic_buffer(state), 3); ei_x_encode_atom(&dynamic_buffer(state), "ok"); if(use_srollable_cursors(state)) { - + if(!sql_success(SQLGetInfo(connection_handle(state), SQL_DYNAMIC_CURSOR_ATTRIBUTES1, (SQLPOINTER)&supportMask, @@ -2674,30 +2723,30 @@ static db_result_msg retrive_scrollable_cursor_support_info(db_state *state) NULL))) { DO_EXIT(EXIT_DRIVER_INFO); } - + if ((supportMask & SQL_CA1_ABSOLUTE)) { ei_x_encode_atom(&dynamic_buffer(state), "true"); } else { - ei_x_encode_atom(&dynamic_buffer(state), "false"); + ei_x_encode_atom(&dynamic_buffer(state), "false"); } - + if ((supportMask & SQL_CA1_RELATIVE)) { - ei_x_encode_atom(&dynamic_buffer(state), "true"); + ei_x_encode_atom(&dynamic_buffer(state), "true"); } else { ei_x_encode_atom(&dynamic_buffer(state), "false"); } } else { /* Scrollable cursors disabled by the user */ - ei_x_encode_atom(&dynamic_buffer(state), "false"); ei_x_encode_atom(&dynamic_buffer(state), "false"); - } + ei_x_encode_atom(&dynamic_buffer(state), "false"); + } msg.buffer = (byte *)dynamic_buffer(state).buff; - msg.length = dynamic_buffer(state).index; + msg.length = dynamic_buffer(state).index; msg.dyn_alloc = TRUE; return msg; } - + /* ------------- Boolean functions ---------------------------------------*/ /* Check if there are any more result sets */ @@ -2709,7 +2758,8 @@ static db_result_msg more_result_sets(db_state *state) msg = encode_empty_message(); result = SQLMoreResults(statement_handle(state)); - + + if(sql_success(result)){ exists_more_result_sets(state) = TRUE; return msg; @@ -2729,7 +2779,7 @@ static db_result_msg more_result_sets(db_state *state) return msg; } } - + static Boolean sql_success(SQLRETURN result) { return result == SQL_SUCCESS || result == SQL_SUCCESS_WITH_INFO; @@ -2753,16 +2803,16 @@ static diagnos get_diagnos(SQLSMALLINT handleType, SQLHANDLE handle, Boolean ext SQLRETURN result; diagnos.error_msg[0] = 0; - + current_errmsg_pos = (byte *)diagnos.error_msg; /* number bytes free in error message buffer */ - errmsg_buffer_size = MAX_ERR_MSG - ERRMSG_HEADR_SIZE; + errmsg_buffer_size = MAX_ERR_MSG - ERRMSG_HEADR_SIZE; acc_errmsg_size = 0; /* number bytes used in the error message buffer */ /* Foreach diagnostic record in the current set of diagnostic records the error message is obtained */ - for(record_nr = 1; ;record_nr++) { + for(record_nr = 1; ;record_nr++) { result = SQLGetDiagRec(handleType, handle, record_nr, current_sql_state, &nativeError, (SQLCHAR *)current_errmsg_pos, (SQLSMALLINT)errmsg_buffer_size, &errmsg_size); @@ -2783,7 +2833,7 @@ static diagnos get_diagnos(SQLSMALLINT handleType, SQLHANDLE handle, Boolean ext break; } } - + if(acc_errmsg_size == 0) { strcat((char *)diagnos.error_msg, "No SQL-driver information available."); @@ -2798,8 +2848,88 @@ static diagnos get_diagnos(SQLSMALLINT handleType, SQLHANDLE handle, Boolean ext static void str_tolower(char *str, int len) { int i; - + for(i = 0; i <= len; i++) { str[i] = tolower(str[i]); } } + +// Extracts one field value in chunks. +// This function allocates and sets bufferptr. +// This function sets result_len. +// The caller must release the memory in bufferptr. +void retrive_long_data(db_column column, + int column_nr, + SQLSMALLINT target_type, + char** result_buf, + int* result_len, + int sizeof_element, + int term_bytes, + db_state* state) { + // That moment, when IBM has better docs than MS + // https://www.ibm.com/support/knowledgecenter/ssw_ibm_i_74/cli/rzadpfnbndpm.htm + SQLRETURN rc; + // Should be odd to avoid different alignment issues + const int chunk_bytes = sizeof_element * 50 + 1; + int buff_bytes = chunk_bytes; + int offset_bytes = 0; + SQLLEN byte_len_or_ind = 0; + int truncated; + int got_bytes; + diagnos diagnos; + + // It is a pointer to the beginning of the data + char* bufferptr = (void*) safe_malloc(buff_bytes); + + while (1) { + // This condition applies: + // int chunk_bytes = buff_bytes - offset_bytes; + byte_len_or_ind = 0; + // Retrieve data in parts. + // Sets byte_len_or_ind. + // Writes chunk_bytes bytes into a buffer. + // Though term_bytes last bytes are a null terminator. + // So, it's (chunk_bytes-2) actual bytes + rc = SQLGetData(statement_handle(state), + (SQLSMALLINT)(column_nr + 1), + target_type, + bufferptr + offset_bytes, // where to append data + chunk_bytes, + &byte_len_or_ind); + if (byte_len_or_ind == SQL_NULL_DATA) { + // Data is NULL + // Since we don't return the buffer here, make sure we free it first + free(bufferptr); + *result_buf = NULL; + *result_len = 0; + return; + } + if (!sql_success(rc)) { + diagnos = get_diagnos(SQL_HANDLE_STMT, statement_handle(state), extended_errors(state)); + break; + } + + diagnos = get_diagnos(SQL_HANDLE_STMT, statement_handle(state), extended_errors(state)); + truncated = (strcmp((char *)diagnos.sqlState, TRUNCATED) == 0); + + // Number of written bytes without a NULL terminator + got_bytes = (byte_len_or_ind >= chunk_bytes || byte_len_or_ind == SQL_NO_TOTAL) + ? (chunk_bytes - term_bytes) + : byte_len_or_ind; + offset_bytes += got_bytes; + + + // Loop exit condition + if (!truncated) { + break; + } + + // Enlarge buffer by a number of written bytes + buff_bytes += got_bytes; + + bufferptr = safe_realloc((void *) bufferptr, buff_bytes); + } + + *result_len = offset_bytes; + *result_buf = bufferptr; +} \ No newline at end of file diff --git a/lib/odbc/c_src/odbcserver.h b/lib/odbc/c_src/odbcserver.h index 4dc950f67efc..613a883c5d77 100644 --- a/lib/odbc/c_src/odbcserver.h +++ b/lib/odbc/c_src/odbcserver.h @@ -39,11 +39,11 @@ /* 0 in this case (SQL_ATTR_CONNECTION_TIMEOUT) corresponds to erlang infinity. Erlang will handle all timeouts so we do not want any in the portprogram. */ -#define TIME_OUT 0 +#define TIME_OUT 0 /* Constats defining the command protocol between the Erlang control process and the port program. These constants must also be defined in the same - way in Erlang. */ + way in Erlang. */ #define OPEN_CONNECTION 1 #define CLOSE_CONNECTION 2 #define COMMIT_TRANSACTION 3 @@ -74,13 +74,13 @@ #define EXIT_ENV 3 #define EXIT_CONNECTION 4 #define EXIT_FREE 5 -#define EXIT_STDIN_HEADER 6 +#define EXIT_STDIN_HEADER 6 #define EXIT_STDIN_BODY 7 #define EXIT_BIN 8 #define EXIT_THREAD 9 #define EXIT_PARAM_ARRAY 10 #define EXIT_OLD_WINSOCK 11 -#define EXIT_SOCKET_CONNECT 12 +#define EXIT_SOCKET_CONNECT 12 #define EXIT_SOCKET_SEND_HEADER 13 #define EXIT_SOCKET_SEND_BODY 14 #define EXIT_SOCKET_RECV_MSGSIZE 15 @@ -91,7 +91,8 @@ #define EXIT_ROWS 20 #define EXIT_DESC 21 #define EXIT_BIND 22 -#define EXIT_DRIVER_INFO 23 +#define EXIT_DRIVER_INFO 23 +#define EXIT_LONG_DATA 40 /* COL_SIZE */ #define COL_SQL_SMALLINT 5 @@ -117,6 +118,7 @@ #define USER_WVARCHAR 13 #define USER_TIMESTAMP 14 #define USER_WLONGVARCHAR 15 +#define USER_LONGVARBINARY 16 /*------------------------ TYPDEFS ----------------------------------*/ @@ -130,7 +132,7 @@ typedef struct { SQLSMALLINT decimal_digits; SQLLEN len; SQLLEN strlen_or_indptr; - SQLLEN *strlen_or_indptr_array; + SQLLEN *strlen_or_indptr_array; } col_type; typedef struct { @@ -141,7 +143,7 @@ typedef struct { typedef struct { int length; byte *buffer; - Boolean dyn_alloc; + Boolean dyn_alloc; } db_result_msg; typedef struct { @@ -164,12 +166,12 @@ typedef struct { typedef struct { SQLUSMALLINT params_processed; - SQLUSMALLINT *param_status_array; -} param_status; + SQLUSMALLINT *param_status_array; +} param_status; typedef struct { - SQLHDBC connection_handle; - SQLHENV environment_handle; + SQLHDBC connection_handle; + SQLHENV environment_handle; SQLHSTMT statement_handle; db_column *columns; int number_of_columns; @@ -182,6 +184,8 @@ typedef struct { Boolean param_query; Boolean out_params; Boolean extended_errors; + Boolean return_types; + Boolean integer_bigints; } db_state; typedef enum { @@ -198,6 +202,8 @@ typedef enum { #define use_srollable_cursors(db_state) (db_state -> use_srollable_cursors) #define tuple_row(db_state) (db_state -> tuple_row) #define binary_strings(db_state) (db_state -> binary_strings) +#define return_types(db_state) (db_state -> return_types) +#define integer_bigints(db_state) (db_state -> integer_bigints) #define exists_more_result_sets(db_state) (db_state -> exists_more_result_sets) #define param_query(db_state) (db_state -> param_query) #define out_params(db_state) (db_state -> out_params) diff --git a/lib/odbc/doc/src/odbc.xml b/lib/odbc/doc/src/odbc.xml index f7d623d97402..aa9714ab202e 100644 --- a/lib/odbc/doc/src/odbc.xml +++ b/lib/odbc/doc/src/odbc.xml @@ -11,7 +11,7 @@ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - + http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software @@ -153,7 +153,7 @@ Options = [] | [option()] All options has default values. option() = {auto_commit, on | off} | {timeout, milliseconds()} - | {binary_strings, on | off} | {tuple_row, on | off} | {scrollable_cursors, on | off} | + | {binary_strings, on | off} | {tuple_row, on | off} | {integer_bigints, on | off} | {scrollable_cursors, on | off} | {trace_driver, on | off} | {extended_errors, on | off} Ref = connection_reference() - should be used to access the connection. Reason = port_program_executable_not_found | common_reason() @@ -165,7 +165,7 @@ to handle the connection. These processes will terminate if the process that created the connection dies or if you call disconnect/1.

- +

If automatic commit mode is turned on, each query will be considered as an individual transaction and will be automatically committed after it has been executed. If you want @@ -174,19 +174,23 @@ commit/3 explicitly to end a transaction.

The default timeout is infinity

- +

>If the option binary_strings is turned on all strings will be returned as binaries and strings inputed to param_query will be expected to be binaries. The user needs to ensure that the binary is in an encoding that the database expects. By default this option is turned off.

- +

As default result sets are returned as a lists of tuples. The TupleMode option still exists to keep some degree of backwards compatibility. If the option is set to off, result sets will be returned as a lists of lists instead of a lists of tuples.

- + +

>If the option integer_bigints is turned on, bigints will be + returned as integers instead of strings. By default this option is + turned off.

+

Scrollable cursors are nice but causes some overhead. For some connections speed might be more important than flexible data access and then you can disable scrollable cursor for a @@ -194,7 +198,7 @@

Turning the scrollable_cursors option off is noted to make old odbc-drivers able to connect that will otherwhise fail.

- +

If trace mode is turned on this tells the ODBC driver to write a trace log to the file SQL.LOG that is placed in the current directory of the erlang emulator. This information @@ -202,7 +206,7 @@ erlang ODBC application, and it might be relevant for you to send this file to our support. Otherwise you will probably not have much use of this.

- +

For more information about the ConnectStr see description of the function SQLDriverConnect in [1].

@@ -244,7 +248,7 @@

Closes a connection to a database. This will also - terminate all processes that may have been spawned + terminate all processes that may have been spawned when the connection was opened. This call will always succeed. If the connection cannot be disconnected gracefully it will be brutally killed. However you may receive an error message @@ -339,7 +343,7 @@

Executes a parameterized SQL query. For an - example see the "Using the Erlang API" in the Erlang ODBC + example see the "Using the Erlang API" in the Erlang ODBC User's Guide.

Use the function describe_table/[2,3] to find out which @@ -374,12 +378,12 @@ start() -> start(Type) -> ok | {error, Reason} Starts the odb application. - + Type = permanent | transient | temporary - +

Starts the odbc application. Default type is temporary. @@ -391,14 +395,14 @@ stop() -> ok Stops the odbc application. - +

Stops the odbc application. See application(3)

- + sql_query(Ref, SQLQuery) -> sql_query(Ref, SQLQuery, TimeOut) -> ResultTuple | [ResultTuple] |{error, Reason} @@ -492,7 +496,7 @@ See also http://msdn.microsoft.com/

- + diff --git a/lib/odbc/src/odbc.erl b/lib/odbc/src/odbc.erl index 8871acd3f4bc..29cce226d878 100644 --- a/lib/odbc/src/odbc.erl +++ b/lib/odbc/src/odbc.erl @@ -42,27 +42,27 @@ -export([start_link_sup/1]). %% gen_server callbacks --export([init/1, handle_call/3, handle_cast/2, handle_info/2, +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). %%-------------------------------------------------------------------------- %% Internal state -record(state, {erlang_port, % The port to the c-program - reply_to, % gen_server From parameter + reply_to, % gen_server From parameter owner, % Pid of the connection owner result_set = undefined, % exists | undefined auto_commit_mode = on, % on | off %% Indicates if first, last and "select absolut" %% is supported by the odbc driver. - absolute_pos, % true | false + absolute_pos, % true | false %% Indicates if prev and "select relative" %% is supported by the odbc driver. relative_pos, % true | false scrollable_cursors, % on | off %% connecting | connected | disconnecting - state = connecting, + state = connecting, %% For timeout handling - pending_request, + pending_request, num_timeouts = 0, listen_sockets, sup_socket, @@ -84,10 +84,10 @@ %% Description: Starts the inets application. Default type %% is temporary. see application(3) %%-------------------------------------------------------------------- -start() -> +start() -> application:start(odbc). -start(Type) -> +start(Type) -> application:start(odbc, Type). %%-------------------------------------------------------------------- @@ -95,7 +95,7 @@ start(Type) -> %% %% Description: Stops the odbc application. %%-------------------------------------------------------------------- -stop() -> +stop() -> application:stop(odbc). %%------------------------------------------------------------------------- @@ -103,10 +103,10 @@ stop() -> %% {error, Reason} %% Description: Spawns an erlang control process that will open a port %% to a c-process that uses the ODBC API to open a connection -%% to the database. +%% to the database. %%------------------------------------------------------------------------- connect(ConnectionStr, Options) when is_list(ConnectionStr), is_list(Options) -> - + %% Spawn the erlang control process. try supervisor:start_child(odbc_sup, [[{client, self()}]]) of {ok, Pid} -> @@ -120,17 +120,17 @@ connect(ConnectionStr, Options) when is_list(ConnectionStr), is_list(Options) -> %%-------------------------------------------------------------------------- %% disconnect(ConnectionReferense) -> ok | {error, Reason} -%% +%% %% Description: Disconnects from the database and terminates both the erlang -%% control process and the database handling c-process. +%% control process and the database handling c-process. %%-------------------------------------------------------------------------- disconnect(ConnectionReference) when is_pid(ConnectionReference)-> ODBCCmd = [?CLOSE_CONNECTION], - case call(ConnectionReference, {disconnect, ODBCCmd}, 5000) of + case call(ConnectionReference, {disconnect, ODBCCmd}, 5000) of {error, connection_closed} -> %% If the connection has already been closed the effect of %% disconnect has already been acomplished - ok; + ok; %% Note a time out of this call will return ok, as disconnect %% will always succeed, the time out is to make sure %% the connection is killed brutaly if it will not be shut down @@ -141,161 +141,161 @@ disconnect(ConnectionReference) when is_pid(ConnectionReference)-> %% disconnect a connection started by another process. Other -> Other - end. - + end. + %%-------------------------------------------------------------------------- %% commit(ConnectionReference, CommitMode, ) -> ok | {error,Reason} -%% +%% %% Description: Commits or rollbacks a transaction. Needed on connections -%% where automatic commit is turned off. +%% where automatic commit is turned off. %%-------------------------------------------------------------------------- commit(ConnectionReference, CommitMode) -> commit(ConnectionReference, CommitMode, ?DEFAULT_TIMEOUT). -commit(ConnectionReference, commit, infinity) +commit(ConnectionReference, commit, infinity) when is_pid(ConnectionReference) -> ODBCCmd = [?COMMIT_TRANSACTION, ?COMMIT], call(ConnectionReference, {commit, ODBCCmd}, infinity); -commit(ConnectionReference, commit, TimeOut) +commit(ConnectionReference, commit, TimeOut) when is_pid(ConnectionReference), is_integer(TimeOut), TimeOut > 0 -> ODBCCmd = [?COMMIT_TRANSACTION, ?COMMIT], call(ConnectionReference, {commit, ODBCCmd}, TimeOut); -commit(ConnectionReference, rollback, infinity) +commit(ConnectionReference, rollback, infinity) when is_pid(ConnectionReference) -> ODBCCmd = [?COMMIT_TRANSACTION, ?ROLLBACK], call(ConnectionReference, {commit, ODBCCmd}, infinity); -commit(ConnectionReference, rollback, TimeOut) +commit(ConnectionReference, rollback, TimeOut) when is_pid(ConnectionReference), is_integer(TimeOut), TimeOut > 0 -> ODBCCmd = [?COMMIT_TRANSACTION, ?ROLLBACK], call(ConnectionReference, {commit, ODBCCmd}, TimeOut). %%-------------------------------------------------------------------------- %% sql_query(ConnectionReference, SQLQuery, ) -> {updated, NRows} | -%% {selected, ColNames, Rows} | {error, Reason} -%% +%% {selected, ColNames, Rows} | {error, Reason} +%% %% Description: Executes a SQL query. If it is a SELECT query the -%% result set is returned, otherwise the number of affected +%% result set is returned, otherwise the number of affected %% rows are returned. %%-------------------------------------------------------------------------- sql_query(ConnectionReference, SQLQuery) -> sql_query(ConnectionReference, SQLQuery, ?DEFAULT_TIMEOUT). -sql_query(ConnectionReference, SQLQuery, infinity) when - is_pid(ConnectionReference), is_list(SQLQuery) -> +sql_query(ConnectionReference, SQLQuery, infinity) when + is_pid(ConnectionReference), is_list(SQLQuery) -> ODBCCmd = [?QUERY, SQLQuery], call(ConnectionReference, {sql_query, ODBCCmd}, infinity); -sql_query(ConnectionReference, SQLQuery, TimeOut) - when is_pid(ConnectionReference),is_list(SQLQuery),is_integer(TimeOut),TimeOut>0 -> +sql_query(ConnectionReference, SQLQuery, TimeOut) + when is_pid(ConnectionReference),is_list(SQLQuery),is_integer(TimeOut),TimeOut>0 -> ODBCCmd = [?QUERY, SQLQuery], call(ConnectionReference, {sql_query, ODBCCmd}, TimeOut). %%-------------------------------------------------------------------------- %% select_count(ConnectionReference, SQLQuery, ) -> {ok, NrRows} | -%% {error, Reason} -%% +%% {error, Reason} +%% %% Description: Executes a SQL SELECT query and associates the result set %% with the connection. A cursor is positioned before %% the first row in the result set and the number of %% rows in the result set is returned. %%-------------------------------------------------------------------------- -select_count(ConnectionReference, SQLQuery) -> +select_count(ConnectionReference, SQLQuery) -> select_count(ConnectionReference, SQLQuery, ?DEFAULT_TIMEOUT). -select_count(ConnectionReference, SQLQuery, infinity) when +select_count(ConnectionReference, SQLQuery, infinity) when is_pid(ConnectionReference), is_list(SQLQuery) -> ODBCCmd = [?SELECT_COUNT, SQLQuery], call(ConnectionReference, {select_count, ODBCCmd}, infinity); -select_count(ConnectionReference, SQLQuery, TimeOut) when +select_count(ConnectionReference, SQLQuery, TimeOut) when is_pid(ConnectionReference), is_list(SQLQuery), is_integer(TimeOut), TimeOut > 0 -> ODBCCmd = [?SELECT_COUNT, SQLQuery], call(ConnectionReference, {select_count, ODBCCmd}, TimeOut). %%-------------------------------------------------------------------------- -%% first(ConnectionReference, ) -> {selected, ColNames, Rows} | -%% {error, Reason} -%% +%% first(ConnectionReference, ) -> {selected, ColNames, Rows} | +%% {error, Reason} +%% %% Description: Selects the first row in the current result set. The cursor -%% : is positioned at this row. +%% : is positioned at this row. %%-------------------------------------------------------------------------- -first(ConnectionReference) -> - first(ConnectionReference, ?DEFAULT_TIMEOUT). +first(ConnectionReference) -> + first(ConnectionReference, ?DEFAULT_TIMEOUT). -first(ConnectionReference, infinity) when is_pid(ConnectionReference) -> +first(ConnectionReference, infinity) when is_pid(ConnectionReference) -> ODBCCmd = [?SELECT, ?SELECT_FIRST], call(ConnectionReference, {select_cmd, absolute, ODBCCmd}, infinity); first(ConnectionReference, TimeOut) - when is_pid(ConnectionReference), is_integer(TimeOut), TimeOut > 0 -> + when is_pid(ConnectionReference), is_integer(TimeOut), TimeOut > 0 -> ODBCCmd = [?SELECT, ?SELECT_FIRST], call(ConnectionReference, {select_cmd, absolute, ODBCCmd}, TimeOut). %%-------------------------------------------------------------------------- -%% last(ConnectionReference, ) -> {selected, ColNames, Rows} | -%% {error, Reason} -%% +%% last(ConnectionReference, ) -> {selected, ColNames, Rows} | +%% {error, Reason} +%% %% Description: Selects the last row in the current result set. The cursor -%% : is positioned at this row. +%% : is positioned at this row. %%-------------------------------------------------------------------------- -last(ConnectionReference) -> - last(ConnectionReference, ?DEFAULT_TIMEOUT). +last(ConnectionReference) -> + last(ConnectionReference, ?DEFAULT_TIMEOUT). -last(ConnectionReference, infinity) when is_pid(ConnectionReference) -> +last(ConnectionReference, infinity) when is_pid(ConnectionReference) -> ODBCCmd = [?SELECT, ?SELECT_LAST], call(ConnectionReference, {select_cmd, absolute, ODBCCmd}, infinity); -last(ConnectionReference, TimeOut) - when is_pid(ConnectionReference), is_integer(TimeOut), TimeOut > 0 -> +last(ConnectionReference, TimeOut) + when is_pid(ConnectionReference), is_integer(TimeOut), TimeOut > 0 -> ODBCCmd = [?SELECT, ?SELECT_LAST], call(ConnectionReference, {select_cmd, absolute, ODBCCmd}, TimeOut). %%-------------------------------------------------------------------------- -%% next(ConnectionReference, ) -> {selected, ColNames, Rows} | -%% {error, Reason} -%% -%% Description: Selects the next row relative the current cursor position -%% : in the current result set. The cursor is positioned at -%% : this row. -%%-------------------------------------------------------------------------- -next(ConnectionReference) -> - next(ConnectionReference, ?DEFAULT_TIMEOUT). - -next(ConnectionReference, infinity) when is_pid(ConnectionReference) -> +%% next(ConnectionReference, ) -> {selected, ColNames, Rows} | +%% {error, Reason} +%% +%% Description: Selects the next row relative the current cursor position +%% : in the current result set. The cursor is positioned at +%% : this row. +%%-------------------------------------------------------------------------- +next(ConnectionReference) -> + next(ConnectionReference, ?DEFAULT_TIMEOUT). + +next(ConnectionReference, infinity) when is_pid(ConnectionReference) -> ODBCCmd = [?SELECT, ?SELECT_NEXT], call(ConnectionReference, {select_cmd, next, ODBCCmd}, infinity); -next(ConnectionReference, TimeOut) - when is_pid(ConnectionReference), is_integer(TimeOut), TimeOut > 0 -> +next(ConnectionReference, TimeOut) + when is_pid(ConnectionReference), is_integer(TimeOut), TimeOut > 0 -> ODBCCmd = [?SELECT, ?SELECT_NEXT], call(ConnectionReference, {select_cmd, next, ODBCCmd}, TimeOut). %%-------------------------------------------------------------------------- -%% prev(ConnectionReference, ) -> {selected, ColNames, Rows} | -%% {error, Reason} -%% -%% Description: Selects the previous row relative the current cursor +%% prev(ConnectionReference, ) -> {selected, ColNames, Rows} | +%% {error, Reason} +%% +%% Description: Selects the previous row relative the current cursor %% : position in the current result set. The cursor is -%% : positioned at this row. +%% : positioned at this row. %%-------------------------------------------------------------------------- -prev(ConnectionReference) -> - prev(ConnectionReference, ?DEFAULT_TIMEOUT). +prev(ConnectionReference) -> + prev(ConnectionReference, ?DEFAULT_TIMEOUT). -prev(ConnectionReference, infinity) when is_pid(ConnectionReference) -> +prev(ConnectionReference, infinity) when is_pid(ConnectionReference) -> ODBCCmd = [?SELECT, ?SELECT_PREV], call(ConnectionReference, {select_cmd, relative, ODBCCmd}, infinity); -prev(ConnectionReference, TimeOut) - when is_pid(ConnectionReference), is_integer(TimeOut), TimeOut > 0 -> +prev(ConnectionReference, TimeOut) + when is_pid(ConnectionReference), is_integer(TimeOut), TimeOut > 0 -> ODBCCmd = [?SELECT, ?SELECT_PREV], call(ConnectionReference, {select_cmd, relative, ODBCCmd}, TimeOut). %%-------------------------------------------------------------------------- -%% select(ConnectionReference, ) -> {selected, ColNames, Rows} | -%% {error, Reason} -%% +%% select(ConnectionReference, ) -> {selected, ColNames, Rows} | +%% {error, Reason} +%% %% Description: Selects rows. If is next it is %% semanticly eqvivivalent of calling next/[1,2] %% times. If is {relative, Pos} will be @@ -308,31 +308,31 @@ prev(ConnectionReference, TimeOut) select(ConnectionReference, Position, N) -> select(ConnectionReference, Position, N, ?DEFAULT_TIMEOUT). -select(ConnectionReference, next, N, infinity) +select(ConnectionReference, next, N, infinity) when is_pid(ConnectionReference), is_integer(N), N > 0 -> ODBCCmd = [?SELECT, ?SELECT_N_NEXT, - integer_to_list(?DUMMY_OFFSET), ";", + integer_to_list(?DUMMY_OFFSET), ";", integer_to_list(N), ";"], call(ConnectionReference, {select_cmd, next, ODBCCmd}, infinity); -select(ConnectionReference, next, N, TimeOut) +select(ConnectionReference, next, N, TimeOut) when is_pid(ConnectionReference), is_integer(N), N > 0, is_integer(TimeOut), TimeOut > 0 -> ODBCCmd = [?SELECT, ?SELECT_N_NEXT, - integer_to_list(?DUMMY_OFFSET), ";", + integer_to_list(?DUMMY_OFFSET), ";", integer_to_list(N), ";"], call(ConnectionReference, {select_cmd, next, ODBCCmd}, TimeOut); -select(ConnectionReference, {relative, Pos} , N, infinity) +select(ConnectionReference, {relative, Pos} , N, infinity) when is_pid(ConnectionReference), is_integer(Pos), Pos > 0, is_integer(N), N > 0 -> ODBCCmd = [?SELECT, ?SELECT_RELATIVE, integer_to_list(Pos), ";", integer_to_list(N), ";"], call(ConnectionReference, {select_cmd, relative, ODBCCmd}, infinity); -select(ConnectionReference, {relative, Pos} , N, TimeOut) +select(ConnectionReference, {relative, Pos} , N, TimeOut) when is_pid(ConnectionReference), is_integer(Pos), Pos >0, is_integer(N), N > 0, is_integer(TimeOut), TimeOut > 0 -> ODBCCmd = [?SELECT,?SELECT_RELATIVE, @@ -340,30 +340,36 @@ select(ConnectionReference, {relative, Pos} , N, TimeOut) call(ConnectionReference, {select_cmd, relative, ODBCCmd}, TimeOut); -select(ConnectionReference, {absolute, Pos} , N, infinity) +select(ConnectionReference, {absolute, Pos} , N, infinity) when is_pid(ConnectionReference), is_integer(Pos), Pos > 0, is_integer(N), N > 0 -> ODBCCmd = [?SELECT, ?SELECT_ABSOLUTE, integer_to_list(Pos), ";", integer_to_list(N), ";"], call(ConnectionReference, {select_cmd, absolute, ODBCCmd}, infinity); -select(ConnectionReference, {absolute, Pos} , N, TimeOut) - when is_pid(ConnectionReference), is_integer(Pos), Pos > 0, is_integer(N), N > 0, +select(ConnectionReference, {absolute, Pos} , N, TimeOut) + when is_pid(ConnectionReference), is_integer(Pos), Pos > 0, is_integer(N), N > 0, is_integer(TimeOut), TimeOut > 0 -> ODBCCmd = [?SELECT, ?SELECT_ABSOLUTE, integer_to_list(Pos), ";", integer_to_list(N), ";"], call(ConnectionReference, {select_cmd, absolute, ODBCCmd}, TimeOut). %%-------------------------------------------------------------------------- -%% param_query(ConnectionReference, SQLQuery, Params, ) -> -%% ok | {error, Reason} -%% -%% Description: Executes a parameterized update/delete/insert-query. +%% param_query(ConnectionReference, SQLQuery, Params, ) -> +%% ok | {error, Reason} +%% +%% Description: Executes a parameterized update/delete/insert-query. %%-------------------------------------------------------------------------- param_query(ConnectionReference, SQLQuery, Params) -> param_query(ConnectionReference, SQLQuery, Params, ?DEFAULT_TIMEOUT). -param_query(ConnectionReference, SQLQuery, Params, infinity) +param_query(ConnectionReference, SQLQuery, Params, Timeout) when is_binary(SQLQuery) -> + param_query(ConnectionReference, binary_to_list(SQLQuery), Params, Timeout); + +param_query(ConnectionReference, SQLQuery, Params, Timeout) when is_binary(SQLQuery) -> + param_query(ConnectionReference, binary_to_list(SQLQuery), Params, Timeout); + +param_query(ConnectionReference, SQLQuery, Params, infinity) when is_pid(ConnectionReference), is_list(SQLQuery), is_list(Params) -> Values = param_values(Params), NoRows = length(Values), @@ -383,34 +389,34 @@ param_query(ConnectionReference, SQLQuery, Params, TimeOut) call(ConnectionReference, {param_query, ODBCCmd}, TimeOut). %%-------------------------------------------------------------------------- -%% describe_table(ConnectionReference, Table, ) -> {ok, Desc} +%% describe_table(ConnectionReference, Table, ) -> {ok, Desc} %% %% Desc - [{ColName, Datatype}] %% ColName - atom() -%% Datatype - atom() +%% Datatype - atom() %% Description: Queries the database to find out the datatypes of the %% table %%-------------------------------------------------------------------------- describe_table(ConnectionReference, Table) -> describe_table(ConnectionReference, Table, ?DEFAULT_TIMEOUT). -describe_table(ConnectionReference, Table, infinity) when - is_pid(ConnectionReference), is_list(Table) -> +describe_table(ConnectionReference, Table, infinity) when + is_pid(ConnectionReference), is_list(Table) -> ODBCCmd = [?DESCRIBE, "SELECT * FROM " ++ Table], call(ConnectionReference, {describe_table, ODBCCmd}, infinity); -describe_table(ConnectionReference, Table, TimeOut) - when is_pid(ConnectionReference),is_list(Table),is_integer(TimeOut),TimeOut>0 -> +describe_table(ConnectionReference, Table, TimeOut) + when is_pid(ConnectionReference),is_list(Table),is_integer(TimeOut),TimeOut>0 -> ODBCCmd = [?DESCRIBE, "SELECT * FROM " ++ Table], call(ConnectionReference, {describe_table, ODBCCmd}, TimeOut). %%%========================================================================= %%% Start/stop %%%========================================================================= %%-------------------------------------------------------------------------- -%% start_link_sup(Args) -> {ok, Pid} | {error, Reason} -%% -%% Description: Callback function for the odbc supervisor. It is called -%% : when connect/2 calls supervisor:start_child/2 to start an +%% start_link_sup(Args) -> {ok, Pid} | {error, Reason} +%% +%% Description: Callback function for the odbc supervisor. It is called +%% : when connect/2 calls supervisor:start_child/2 to start an %% : instance of the erlang odbc control process. %%-------------------------------------------------------------------------- start_link_sup(Args) -> @@ -431,9 +437,9 @@ start_link_sup(Args) -> init(Args) -> process_flag(trap_exit, true), {value, {client, ClientPid}} = lists:keysearch(client, 1, Args), - + erlang:monitor(process, ClientPid), - + Inet = case gen_tcp:listen(0, [inet6, {ip, loopback}]) of {ok, Dummyport} -> gen_tcp:close(Dummyport), @@ -451,33 +457,33 @@ init(Args) -> {active, false}, {nodelay, true}, {ip, loopback}]), - %% Start the port program (a c program) that utilizes the odbc driver + %% Start the port program (a c program) that utilizes the odbc driver case os:find_executable(?SERVERPROG, ?SERVERDIR) of FileName when is_list(FileName)-> Port = open_port({spawn, "\""++FileName++"\""}, [{packet, ?LENGTH_INDICATOR_SIZE}, binary, exit_status]), - State = #state{listen_sockets = + State = #state{listen_sockets = [ListenSocketSup, ListenSocketOdbc], erlang_port = Port, owner = ClientPid}, {ok, State}; false -> {stop, port_program_executable_not_found} end. - + %%-------------------------------------------------------------------------- %% handle_call(Request, From, State) -> {reply, Reply, State} | %% {reply, Reply, State, Timeout} | %% {noreply, State} | %% {noreply, State, Timeout} | %% {stop, Reason, Reply, State} | -%% {stop, Reason, Reply, State} +%% {stop, Reason, Reply, State} %% Description: Handle incoming requests. Only requests from the process %% that created the connection are allowed in order to preserve %% the semantics of result sets. %% Note: The order of the function clauses is significant. %%-------------------------------------------------------------------------- -handle_call({Client, Msg, Timeout}, From, State = +handle_call({Client, Msg, Timeout}, From, State = #state{owner = Client, reply_to = undefined}) -> handle_msg(Msg, Timeout, State#state{reply_to = From}); @@ -486,22 +492,22 @@ handle_call({Client, Msg, Timeout}, From, State = %% request will be handled when we have received the answer to the %% timed out request and thrown it away, if it has not already been %% timed out itself in which case the request is thrown away. -handle_call(Request = {Client, _, Timeout}, From, +handle_call(Request = {Client, _, Timeout}, From, State = #state{owner = Client, reply_to = skip, num_timeouts = N}) when N < ?MAX_SEQ_TIMEOUTS -> {noreply, State#state{pending_request = {Request, From}}, Timeout}; -%% The client has sent so many sequential requests that has timed out that +%% The client has sent so many sequential requests that has timed out that %% there might be something radically wrong causing the ODBC-driver to -%% hang. So we give up and close the connection. -handle_call({Client, _, _}, From, - State = #state{owner = Client, +%% hang. So we give up and close the connection. +handle_call({Client, _, _}, From, + State = #state{owner = Client, num_timeouts = N}) when N >= ?MAX_SEQ_TIMEOUTS -> - gen_server:reply(From, {error, connection_closed}), + gen_server:reply(From, {error, connection_closed}), {stop, too_many_sequential_timeouts, State#state{reply_to = undefined}}; handle_call(_, _, State) -> - {reply, {error, process_not_owner_of_odbc_connection}, + {reply, {error, process_not_owner_of_odbc_connection}, State#state{reply_to = undefined}}. %%-------------------------------------------------------------------------- @@ -513,45 +519,45 @@ handle_msg({connect, ODBCCmd, AutoCommitMode, SrollableCursors}, Timeout, State) -> [ListenSocketSup, ListenSocketOdbc] = State#state.listen_sockets, - + %% Inform c-client so it knows where to send answers {ok, InetPortSup} = inet:port(ListenSocketSup), {ok, InetPortOdbc} = inet:port(ListenSocketOdbc), - - port_command(State#state.erlang_port, - [integer_to_list(InetPortSup), ";", + + port_command(State#state.erlang_port, + [integer_to_list(InetPortSup), ";", integer_to_list(InetPortOdbc) , ?STR_TERMINATOR]), - + NewState = State#state{auto_commit_mode = AutoCommitMode, scrollable_cursors = SrollableCursors}, - + case gen_tcp:accept(ListenSocketSup, port_timeout()) of {ok, SupSocket} -> gen_tcp:close(ListenSocketSup), case gen_tcp:accept(ListenSocketOdbc, port_timeout()) of {ok, OdbcSocket} -> gen_tcp:close(ListenSocketOdbc), - odbc_send(OdbcSocket, ODBCCmd), + odbc_send(OdbcSocket, ODBCCmd), {noreply, NewState#state{odbc_socket = OdbcSocket, - sup_socket = SupSocket}, + sup_socket = SupSocket}, Timeout}; {error, Reason} -> {stop, Reason, {error, connection_closed}, NewState} end; {error, Reason} -> {stop, Reason, {error, connection_closed}, NewState} - end; - + end; + handle_msg({disconnect, ODBCCmd}, Timeout, State) -> odbc_send(State#state.odbc_socket, ODBCCmd), {noreply, State#state{state = disconnecting}, Timeout}; -handle_msg({commit, _ODBCCmd}, Timeout, +handle_msg({commit, _ODBCCmd}, Timeout, State = #state{auto_commit_mode = on}) -> - {reply, {error, not_an_explicit_commit_connection}, + {reply, {error, not_an_explicit_commit_connection}, State#state{reply_to = undefined}, Timeout}; -handle_msg({commit, ODBCCmd}, Timeout, +handle_msg({commit, ODBCCmd}, Timeout, State = #state{auto_commit_mode = off}) -> odbc_send(State#state.odbc_socket, ODBCCmd), {noreply, State, Timeout}; @@ -577,7 +583,7 @@ handle_msg({select_cmd, absolute, ODBCCmd}, Timeout, odbc_send(State#state.odbc_socket, ODBCCmd), {noreply, State, Timeout}; -handle_msg({select_cmd, relative, ODBCCmd}, Timeout, +handle_msg({select_cmd, relative, ODBCCmd}, Timeout, State = #state{result_set = exists, relative_pos = true}) -> odbc_send(State#state.odbc_socket, ODBCCmd), {noreply, State, Timeout}; @@ -589,7 +595,7 @@ handle_msg({select_cmd, next, ODBCCmd}, Timeout, handle_msg({select_cmd, _Type, _ODBCCmd}, _Timeout, State = #state{result_set = undefined}) -> - {reply, {error, result_set_does_not_exist}, + {reply, {error, result_set_does_not_exist}, State#state{reply_to = undefined}}; handle_msg({select_cmd, _Type, _ODBCCmd}, _Timeout, State) -> @@ -599,24 +605,24 @@ handle_msg({select_cmd, _Type, _ODBCCmd}, _Timeout, State) -> off -> {error, scrollable_cursors_disabled} end, - + {reply, Reply, State#state{reply_to = undefined}}; %--------------------------------------------------------------------------- -%% Catch all - This can oly happen if the application programmer writes +%% Catch all - This can oly happen if the application programmer writes %% really bad code that violates the API. handle_msg(Request, _Timeout, State) -> {stop, {'API_violation_connection_colsed', Request}, {error, connection_closed}, State#state{reply_to = undefined}}. %%-------------------------------------------------------------------------- -%% handle_cast(Request, State) -> {noreply, State} | +%% handle_cast(Request, State) -> {noreply, State} | %% {noreply, State, Timeout} | -%% {stop, Reason, State} -%% Description: Handles cast messages. +%% {stop, Reason, State} +%% Description: Handles cast messages. %% Note: The order of the function clauses is significant. %%------------------------------------------------------------------------- -%% Catch all - This can only happen if the application programmer writes +%% Catch all - This can only happen if the application programmer writes %% really bad code that violates the API. handle_cast(Msg, State) -> {stop, {'API_violation_connection_colsed', Msg}, State}. @@ -628,21 +634,21 @@ handle_cast(Msg, State) -> %% down messages. %% Note: The order of the function clauses is significant. %%-------------------------------------------------------------------------- -handle_info({tcp, Socket, BinData}, State = #state{state = connecting, +handle_info({tcp, Socket, BinData}, State = #state{state = connecting, reply_to = From, odbc_socket = Socket}) -> case binary_to_term(BinData) of {ok, AbsolutSupport, RelativeSupport} -> NewState = State#state{absolute_pos = AbsolutSupport, relative_pos = RelativeSupport}, - gen_server:reply(From, ok), + gen_server:reply(From, ok), {noreply, NewState#state{state = connected, reply_to = undefined}}; Error -> - gen_server:reply(From, Error), + gen_server:reply(From, Error), {stop, normal, State#state{reply_to = undefined}} end; - + handle_info({tcp, Socket, _}, State = #state{state = connected, @@ -656,9 +662,9 @@ handle_info({tcp, Socket, _}, handle_info({tcp, Socket, _}, State = #state{state = connected, odbc_socket = Socket, reply_to = skip}) -> - + %% Disregard this message as it is a answer to a query that has timed - %% out and process the pending request. + %% out and process the pending request. {{_, Msg, Timeout}, From} = State#state.pending_request, handle_msg(Msg, Timeout, State#state{pending_request=undefined, reply_to = From}); @@ -666,7 +672,7 @@ handle_info({tcp, Socket, _}, handle_info({tcp, Socket, BinData}, State = #state{state = connected, reply_to = From, odbc_socket = Socket}) -> - %% Send the reply from the database (received by the erlang control + %% Send the reply from the database (received by the erlang control %% process from the port program) to the waiting client. gen_server:reply(From, BinData), {noreply, State#state{reply_to = undefined, @@ -676,36 +682,36 @@ handle_info({tcp, Socket, BinData}, State = #state{state = disconnecting, reply_to = From, odbc_socket = Socket}) -> - %% The connection will always be closed - gen_server:reply(From, ok), - + %% The connection will always be closed + gen_server:reply(From, ok), + case binary_to_term(BinData) of - ok -> + ok -> ok; {error, Reason} -> - Report = - io_lib:format("ODBC could not end connection " + Report = + io_lib:format("ODBC could not end connection " "gracefully due to ~p~n", [Reason]), error_logger:error_report(Report) end, - + {stop, normal, State#state{reply_to = undefined}}; -handle_info(timeout, - State = #state{state = disconnecting, +handle_info(timeout, + State = #state{state = disconnecting, reply_to = From}) when From /= undefined -> - gen_server:reply(From, ok), - {stop, {timeout, "Port program is not responding to disconnect, " + gen_server:reply(From, ok), + {stop, {timeout, "Port program is not responding to disconnect, " "will be killed"}, State}; -handle_info(timeout, - State = #state{state = connecting, +handle_info(timeout, + State = #state{state = connecting, reply_to = From}) when From /= undefined -> gen_server:reply(From, timeout), {stop, normal, State#state{reply_to = undefined}}; -handle_info(timeout, - State = #state{state = connected, +handle_info(timeout, + State = #state{state = connected, pending_request = undefined, reply_to = From}) when From /= undefined -> gen_server:reply(From, timeout), @@ -713,8 +719,8 @@ handle_info(timeout, num_timeouts = State#state.num_timeouts + 1}}; handle_info(timeout, State = - #state{state = connected, - pending_request = {{_, {disconnect, _}, _}, + #state{state = connected, + pending_request = {{_, {disconnect, _}, _}, PendingFrom}}) -> gen_server:reply(PendingFrom, ok), {stop, {timeout, "Port-program busy when trying to disconnect, " @@ -723,24 +729,24 @@ handle_info(timeout, State = num_timeouts = State#state.num_timeouts + 1}}; handle_info(timeout, State = - #state{state = connected, + #state{state = connected, pending_request = {_, PendingFrom}}) -> gen_server:reply(PendingFrom, timeout), - %% The state variable reply_to should continue to have the value skip - {noreply, State#state{pending_request = undefined, + %% The state variable reply_to should continue to have the value skip + {noreply, State#state{pending_request = undefined, num_timeouts = State#state.num_timeouts + 1}}; -handle_info({Port, {exit_status, ?EXIT_SUCCESS}}, +handle_info({Port, {exit_status, ?EXIT_SUCCESS}}, State = #state{erlang_port = Port, state = disconnecting}) -> - {noreply, State}; % Ignore as this is perfectly normal in this case + {noreply, State}; % Ignore as this is perfectly normal in this case -handle_info({Port, {exit_status, Status}}, +handle_info({Port, {exit_status, Status}}, State = #state{erlang_port = Port}) -> {stop, {port_exit, ?PORT_EXIT_REASON(Status)}, State}; handle_info({'EXIT', Port, _}, State = #state{erlang_port = Port, state = disconnecting}) -> - {noreply, State}; % Ignore as this is perfectly normal in this case + {noreply, State}; % Ignore as this is perfectly normal in this case handle_info({'EXIT', Port, Reason}, State = #state{erlang_port = Port}) -> {stop, Reason, State}; @@ -748,15 +754,15 @@ handle_info({'EXIT', Port, Reason}, State = #state{erlang_port = Port}) -> %%% If the owning process dies there is no reson to go on handle_info({'DOWN', _Ref, _Type, _Process, normal}, State) -> {stop, normal, State#state{reply_to = undefined}}; - + handle_info({'DOWN', _Ref, _Type, _Process, timeout}, State) -> {stop, normal, State#state{reply_to = undefined}}; handle_info({'DOWN', _Ref, _Type, _Process, shutdown}, State) -> {stop, normal, State#state{reply_to = undefined}}; - + handle_info({'DOWN', _Ref, _Type, Process, Reason}, State) -> - {stop, {stopped, {'EXIT', Process, Reason}}, + {stop, {stopped, {'EXIT', Process, Reason}}, State#state{reply_to = undefined}}; handle_info({tcp_closed, Socket}, State = #state{odbc_socket=Socket, @@ -765,7 +771,7 @@ handle_info({tcp_closed, Socket}, State = #state{odbc_socket=Socket, %--------------------------------------------------------------------------- %% Catch all - throws away unknown messages (This could happen by "accident" %% so we do not want to crash, but we make a log entry as it is an -%% unwanted behaviour.) +%% unwanted behaviour.) handle_info(Info, State) -> Report = io_lib:format("ODBC: received unexpected info: ~p~n", [Info]), error_logger:error_report(Report), @@ -783,7 +789,7 @@ terminate({port_exit, _Reason}, State = #state{reply_to = undefined}) -> terminate(_Reason, State = #state{reply_to = undefined}) -> - catch gen_tcp:send(State#state.sup_socket, + catch gen_tcp:send(State#state.sup_socket, [?SHUTDOWN, ?STR_TERMINATOR]), catch gen_tcp:close(State#state.odbc_socket), catch gen_tcp:close(State#state.sup_socket), @@ -804,23 +810,26 @@ code_change(_Vsn, State, _Extra) -> %%%======================================================================== connect(ConnectionReferense, ConnectionStr, Options) -> - {C_AutoCommitMode, ERL_AutoCommitMode} = + {C_AutoCommitMode, ERL_AutoCommitMode} = connection_config(auto_commit, Options), TimeOut = connection_config(timeout, Options), {C_TraceDriver, _} = connection_config(trace_driver, Options), - {C_SrollableCursors, ERL_SrollableCursors} = + {C_SrollableCursors, ERL_SrollableCursors} = connection_config(scrollable_cursors, Options), - {C_TupleRow, _} = + {C_TupleRow, _} = connection_config(tuple_row, Options), {BinaryStrings, _} = connection_config(binary_strings, Options), {ExtendedErrors, _} = connection_config(extended_errors, Options), + {ReturnTypes, _} = connection_config(return_types, Options), + {IntegerBigInts, _} = connection_config(integer_bigints, Options), + + ODBCCmd = + [?OPEN_CONNECTION, C_AutoCommitMode, C_TraceDriver, + C_SrollableCursors, C_TupleRow, BinaryStrings, ExtendedErrors, ReturnTypes, + IntegerBigInts, ConnectionStr], - ODBCCmd = - [?OPEN_CONNECTION, C_AutoCommitMode, C_TraceDriver, - C_SrollableCursors, C_TupleRow, BinaryStrings, ExtendedErrors, ConnectionStr], - %% Send request, to open a database connection, to the control process. - case call(ConnectionReferense, + case call(ConnectionReferense, {connect, ODBCCmd, ERL_AutoCommitMode, ERL_SrollableCursors}, TimeOut) of ok -> @@ -832,17 +841,23 @@ connect(ConnectionReferense, ConnectionStr, Options) -> %%------------------------------------------------------------------------- odbc_send(Socket, Msg) -> %% Note currently all allowed messages are lists NewMsg = Msg ++ [?STR_TERMINATOR], - ok = gen_tcp:send(Socket, NewMsg), + case gen_tcp:send(Socket, NewMsg) of + ok -> ok; + {error, Reason} -> + erlang:error(#{event => odbc_send_failed, + reason => Reason, + msg => Msg}) + end, ok = inet:setopts(Socket, [{active, once}]). %%-------------------------------------------------------------------------- connection_config(Key, Options) -> case lists:keysearch(Key, 1, Options) of - {value,{Key, on}} -> + {value, {Key, on}} -> {?ON, on}; - {value,{Key, off}} -> + {value, {Key, off}} -> {?OFF, off}; - {value,{Key, Value}} -> + {value, {Key, Value}} -> Value; _ -> connection_default(Key) @@ -856,7 +871,7 @@ connection_default(timeout) -> ?DEFAULT_TIMEOUT; connection_default(tuple_row) -> - {?ON, on}; + {?ON, on}; connection_default(trace_driver) -> {?OFF, off}; @@ -865,48 +880,52 @@ connection_default(scrollable_cursors) -> {?ON, on}; connection_default(binary_strings) -> {?OFF, off}; +connection_default(return_types) -> + {?OFF, off}; +connection_default(integer_bigints) -> + {?OFF, off}; connection_default(extended_errors) -> {?OFF, off}. %%------------------------------------------------------------------------- call(ConnectionReference, Msg, Timeout) -> - - Result = (catch gen_server:call(ConnectionReference, + + Result = (catch gen_server:call(ConnectionReference, {self(), Msg, Timeout}, infinity)), case Result of - %% Normal case, the result from the port-program has directly + %% Normal case, the result from the port-program has directly %% been forwarded to the client - Binary when is_binary(Binary) -> + Binary when is_binary(Binary) -> decode(Binary); - timeout -> + timeout -> exit(timeout); {'EXIT', _} -> {error, connection_closed}; %% At some occasions the erlang control process will have an %% answer that was not directly received from the port-program. - Term -> + Term -> Term - end. + end. %%------------------------------------------------------------------------- decode(Binary) -> case binary_to_term(Binary) of - [ResultSet | []] -> + [ResultSet | []] -> ResultSet; param_badarg -> - exit({badarg, odbc, param_query, 'Params'}); + exit({badarg, odbc, param_query, 'Params'}); MultipleResultSets_or_Other -> MultipleResultSets_or_Other end. %%------------------------------------------------------------------------- param_values(Params) -> - case Params of - [{_, Values} | _] -> + case Params of + [{_, Values} | _] -> Values; - [{_, _, Values} | _] -> + [{_, _, Values} | _] -> Values; - [] -> + [] -> [] end. @@ -917,12 +936,12 @@ fix_params({sql_smallint, InOut, Values}) -> {?USER_SMALL_INT, fix_inout(InOut), [256 | Values]}; fix_params({sql_tinyint, InOut, Values}) -> {?USER_TINY_INT, fix_inout(InOut), [256 | Values]}; -fix_params({{sql_decimal, Precision, 0}, InOut, +fix_params({{sql_decimal, Precision, 0}, InOut, Values}) when Precision >= 0, Precision =< 9 -> {?USER_DECIMAL, Precision, 0, fix_inout(InOut), [256 | Values]}; fix_params({{sql_decimal, Precision, Scale}, InOut, Values}) -> {?USER_DECIMAL, Precision, Scale, fix_inout(InOut), Values}; -fix_params({{sql_numeric, Precision, 0}, InOut, +fix_params({{sql_numeric, Precision, 0}, InOut, Values}) when Precision >= 0, Precision =< 9 -> {?USER_NUMERIC, Precision, 0, fix_inout(InOut), [256 | Values]}; fix_params({{sql_numeric, Precision, Scale}, InOut, Values}) -> @@ -942,6 +961,8 @@ fix_params({{sql_wvarchar, Max}, InOut, Values}) -> fix_params({{sql_wlongvarchar, Max}, InOut, Values}) -> NewValues = string_terminate(Values), {?USER_WLONGVARCHAR, Max, fix_inout(InOut), NewValues}; +fix_params({{sql_longvarbinary, Max}, InOut, Values}) -> + {?USER_LONGVARBINARY, Max, fix_inout(InOut), Values}; fix_params({{sql_float, Precision}, InOut, Values}) -> {?USER_FLOAT, Precision, fix_inout(InOut), Values}; fix_params({sql_real, InOut, Values}) -> @@ -952,7 +973,7 @@ fix_params({sql_bit, InOut, Values}) -> {?USER_BOOLEAN, fix_inout(InOut), Values}; fix_params({'sql_timestamp', InOut, Values}) -> NewValues = - case (catch + case (catch lists:map( fun({{Year,Month,Day},{Hour,Minute,Second}}) -> {Year,Month,Day,Hour,Minute,Second}; diff --git a/lib/odbc/src/odbc_internal.hrl b/lib/odbc/src/odbc_internal.hrl index 2968c6ba54ce..35326187e4cb 100644 --- a/lib/odbc/src/odbc_internal.hrl +++ b/lib/odbc/src/odbc_internal.hrl @@ -19,13 +19,13 @@ %% % -%% +%% %% Path to the c-program. -define(SERVERDIR, filename:nativename( filename:join(code:priv_dir(odbc), "bin"))). -%% Name of the C program +%% Name of the C program -define(SERVERPROG, "odbcserver"). %% Constats defining the command protocol between the erlang control @@ -74,8 +74,9 @@ -define(USER_WVARCHAR, 13). -define(USER_TIMESTAMP, 14). -define(USER_WLONGVARCHAR, 15). +-define(USER_LONGVARBINARY, 16). -%% INPUT & OUTPUT TYPE +%% INPUT & OUTPUT TYPE -define(IN, 0). -define(OUT, 1). -define(INOUT, 2). @@ -93,7 +94,7 @@ -define(EXIT_THREAD, 9). -define(EXIT_PARAM_ARRAY, 10). -define(EXIT_OLD_WINSOCK, 11). --define(EXIT_SOCKET_CONNECT, 12). +-define(EXIT_SOCKET_CONNECT, 12). -define(EXIT_SOCKET_SEND_HEADER, 13). -define(EXIT_SOCKET_SEND_BODY, 14). -define(EXIT_SOCKET_RECV_MSGSIZE,15). @@ -105,6 +106,7 @@ -define(EXIT_DESC, 21). -define(EXIT_BIND, 22). -define(EXIT_DRIVER_INFO, 23). +-define(EXIT_LONG_DATA, 40). %% Misc constants -define(DEFAULT_TIMEOUT, infinity). @@ -112,7 +114,7 @@ -define(MAX_SEQ_TIMEOUTS, 10). %% Handling of C exit codes --define(ENCODE_EXIT_FUN, +-define(ENCODE_EXIT_FUN, (fun(?EXIT_SUCCESS) -> normal_exit; (?EXIT_FAILURE) -> @@ -161,9 +163,11 @@ could_not_bind_data_buffers; (?EXIT_DRIVER_INFO) -> collecting_of_driver_information_faild; + (?EXIT_LONG_DATA) -> + extracting_long_data_failed; (_) -> killed end)). --define(PORT_EXIT_REASON(EXIT_STATUS), +-define(PORT_EXIT_REASON(EXIT_STATUS), ?ENCODE_EXIT_FUN(EXIT_STATUS)). diff --git a/lib/odbc/test/mysql.erl b/lib/odbc/test/mysql.erl index 69b136e74380..7abdddc9cd2c 100644 --- a/lib/odbc/test/mysql.erl +++ b/lib/odbc/test/mysql.erl @@ -193,6 +193,9 @@ bit_false_selected() -> bit_true_selected() -> {selected,["FIELD"], [{"1"}]}. +non_bit_promotes_to() -> + error. + %------------------------------------------------------------------------- %% Do not test float min/max as value is only theoretical defined in @@ -222,6 +225,15 @@ create_real_table() -> real_zero_selected() -> {selected,["FIELD"],[{0.00000e+0}]}. +%------------------------------------------------------------------------- +% Only really works with a subset of the possible precision and scale combinations +% It should ideally mirror the function map_dec_num_2_c_column +expected_value(Precision, _Scale, In) when Precision > 15 -> + erlang:float_to_list(In); + +expected_value(_Precision, _Scale, In) -> + In. + %------------------------------------------------------------------------- param_select_small_int() -> {selected,["FIELD"],[{1}, {2}]}. diff --git a/lib/odbc/test/odbc_data_type_SUITE.erl b/lib/odbc/test/odbc_data_type_SUITE.erl index a3a4bc78eb24..814b1787aec4 100644 --- a/lib/odbc/test/odbc_data_type_SUITE.erl +++ b/lib/odbc/test/odbc_data_type_SUITE.erl @@ -33,15 +33,15 @@ %% all(Arg) -> [Doc] | [Case] | {skip, Comment} %% Arg - doc | suite %% Doc - string() -%% Case - atom() -%% Name of a test case function. +%% Case - atom() +%% Name of a test case function. %% Comment - string() %% Description: Returns documentation/test cases in this test suite -%% or a skip tuple if the platform is not supported. +%% or a skip tuple if the platform is not supported. %%-------------------------------------------------------------------- suite() -> [{ct_hooks,[ts_install_cth]}]. -all() -> +all() -> case odbc_test_lib:odbc_check() of ok -> [{group, char},{group, fixed_char}, {group, binary_char}, @@ -51,7 +51,7 @@ all() -> Other -> {skip, Other} end. -groups() -> +groups() -> [{char, [], [varchar_lower_limit, varchar_upper_limit, varchar_no_padding, @@ -192,15 +192,15 @@ init_per_testcase(Case, Config) -> common_init_per_testcase(Case, Config) -> PlatformOptions = odbc_test_lib:platform_options(), - {ok, Ref} = + {ok, Ref} = case atom_to_list(Case) of "binary" ++ _ -> - odbc:connect(?RDBMS:connection_string(), + odbc:connect(?RDBMS:connection_string(), [{binary_strings, on}] ++ PlatformOptions); LCase when LCase == "utf8"; LCase == "nchar"; LCase == "nvarchar" -> - odbc:connect(?RDBMS:connection_string(), + odbc:connect(?RDBMS:connection_string(), [{binary_strings, on}] ++ PlatformOptions); _ -> odbc:connect(?RDBMS:connection_string(), PlatformOptions) @@ -233,10 +233,10 @@ is_supported_bit(_) -> end_per_testcase(_TestCase, Config) -> Ref = proplists:get_value(connection_ref, Config), ok = odbc:disconnect(Ref), - %% Clean up if needed + %% Clean up if needed Table = proplists:get_value(tableName, Config), {ok, NewRef} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), - odbc:sql_query(NewRef, "DROP TABLE " ++ Table), + odbc:sql_query(NewRef, "DROP TABLE " ++ Table), odbc:disconnect(NewRef). %%------------------------------------------------------------------------- @@ -246,23 +246,23 @@ end_per_testcase(_TestCase, Config) -> char_fixed_lower_limit() -> [{doc,"Tests fixed length char data type lower boundaries."}]. char_fixed_lower_limit(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), %% Below limit - {error, _} = - odbc:sql_query(Ref, - "CREATE TABLE " ++ Table ++ + {error, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ ?RDBMS:create_fixed_char_table( - (?RDBMS:fixed_char_min() - 1))), + (?RDBMS:fixed_char_min() - 1))), %% Lower limit {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ ?RDBMS:create_fixed_char_table( ?RDBMS:fixed_char_min())), %% Right length data - {updated, _} = + {updated, _} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ "'" ++ string:chars($a, ?RDBMS:fixed_char_min()) ++ "')"), @@ -273,7 +273,7 @@ char_fixed_lower_limit(Config) when is_list(Config) -> ["FIELD"] = odbc_test_lib:to_upper(Fields), %% Too long data - {error, _} = + {error, _} = odbc:sql_query(Ref,"INSERT INTO " ++ Table ++" VALUES(" ++ "'" ++ string:chars($a, (?RDBMS:fixed_char_min() @@ -290,40 +290,40 @@ char_fixed_upper_limit(Config) when is_list(Config) -> postgres -> {skip, "Limit unknown"}; _ -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - - %% Upper limit + + %% Upper limit {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ ?RDBMS:create_fixed_char_table( - ?RDBMS:fixed_char_max())), - {updated, _} = - odbc:sql_query(Ref,"INSERT INTO " ++ Table ++" VALUES(" ++ - "'" ++ string:chars($a, + ?RDBMS:fixed_char_max())), + {updated, _} = + odbc:sql_query(Ref,"INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ string:chars($a, ?RDBMS:fixed_char_max()) ++ "')"), %% Select data {selected, Fields, [{CharStr}]} = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), true = length(CharStr) == ?RDBMS:fixed_char_max(), - + ["FIELD"] = odbc_test_lib:to_upper(Fields), - + %% Too long data - {error, _} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ - "'" ++ string:chars($a, + {error, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ string:chars($a, (?RDBMS:fixed_char_max() - + 1)) - ++ "')"), + + 1)) + ++ "')"), %% Clean up {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "DROP TABLE " ++ Table), - - %% Above limit + + %% Above limit {error, _} = - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ ?RDBMS:create_fixed_char_table( (?RDBMS:fixed_char_max() + 1))) end. @@ -331,20 +331,20 @@ char_fixed_upper_limit(Config) when is_list(Config) -> %%------------------------------------------------------------------------- char_fixed_padding() -> - [{doc, "Tests that data that is shorter than the given size is padded " + [{doc, "Tests that data that is shorter than the given size is padded " "with blanks."}]. char_fixed_padding(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), %% Data should be padded with blanks {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ ?RDBMS:create_fixed_char_table( ?RDBMS:fixed_char_max())), {updated, _} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ "'" ++ string:chars($a, ?RDBMS:fixed_char_min()) ++ "')"), @@ -359,30 +359,30 @@ char_fixed_padding(Config) when is_list(Config) -> varchar_lower_limit() -> [{doc,"Tests variable length char data type lower boundaries."}]. varchar_lower_limit(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), %% Below limit - {error, _} = + {error, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ ?RDBMS:create_var_char_table( ?RDBMS:var_char_min() - 1)), %% Lower limit {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ ?RDBMS:create_var_char_table( ?RDBMS:var_char_min())), Str = string:chars($a, ?RDBMS:var_char_min()), %% Right length data - {updated, _} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ "'" ++ Str ++ "')"), %% Select data {selected, Fields, [{Str}]} = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), - + ["FIELD"] = odbc_test_lib:to_upper(Fields), %% Too long datae @@ -397,7 +397,7 @@ varchar_lower_limit(Config) when is_list(Config) -> varchar_upper_limit() -> [{doc,"Tests variable length char data type upper boundaries."}]. varchar_upper_limit(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), case ?RDBMS of @@ -406,36 +406,36 @@ varchar_upper_limit(Config) when is_list(Config) -> postgres -> {skip, "Limit unknown"}; _ -> - %% Upper limit + %% Upper limit {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ ?RDBMS:create_var_char_table( - ?RDBMS:var_char_max())), - {updated, _} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ - "'" ++ string:chars($a, + ?RDBMS:var_char_max())), + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ string:chars($a, ?RDBMS:var_char_max()) ++ "')"), - + {selected, Fields, [{CharStr}]} = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), true = length(CharStr) == ?RDBMS:var_char_max(), - + ["FIELD"] = odbc_test_lib:to_upper(Fields), - + %% Too long data - {error, _} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ - "'" ++ string:chars($a, - (?RDBMS:var_char_max()+1)) + {error, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ string:chars($a, + (?RDBMS:var_char_max()+1)) ++ "')"), %% Clean up {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "DROP TABLE " ++ Table), - - %% Above limit + odbc:sql_query(Ref, "DROP TABLE " ++ Table), + + %% Above limit {error, _} = - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ ?RDBMS:create_var_char_table( (?RDBMS:var_char_max() + 1))), ok @@ -443,19 +443,19 @@ varchar_upper_limit(Config) when is_list(Config) -> %%------------------------------------------------------------------------- varchar_no_padding() -> - [{doc, "Tests that data that is shorter than the given max size is not padded " + [{doc, "Tests that data that is shorter than the given max size is not padded " "with blanks."}]. varchar_no_padding(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), %% Data should NOT be padded with blanks {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ ?RDBMS:create_var_char_table( - ?RDBMS:var_char_max())), - {updated, _} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + ?RDBMS:var_char_max())), + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ "'" ++ string:chars($a, ?RDBMS:var_char_min()) ++ "')"), @@ -469,15 +469,15 @@ varchar_no_padding(Config) when is_list(Config) -> text_lower_limit() -> [{doc,"Tests 'long' char data type lower boundaries."}]. text_lower_limit(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - ?RDBMS:create_text_table()), + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_text_table()), - {updated, _} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ "'" ++ string:chars($a, ?RDBMS:text_min()) ++ "')"), @@ -490,16 +490,16 @@ text_lower_limit(Config) when is_list(Config) -> text_upper_limit() -> [{doc,"Tests 'text' char data type upper boundaries."}]. text_upper_limit(Config) when is_list(Config) -> - + {skip,"Consumes too much resources" }. %% Ref = proplists:get_value(connection_ref, Config), %% Table = proplists:get_value(tableName, Config), %% {updated, _} = % Value == 0 || -1 driver dependent! -%% odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ -%% ?RDBMS:create_text_table()), -%% {updated, _} = -%% odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ +%% odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ +%% ?RDBMS:create_text_table()), +%% {updated, _} = +%% odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ %% "'" ++ string:chars($a, ?RDBMS:text_max()) %% ++ "')"), @@ -507,10 +507,10 @@ text_upper_limit(Config) when is_list(Config) -> %% odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), %% length(CharStr) == ?RDBMS:text_max(), %% ["FIELD"] = odbc_test_lib:to_upper(Fields), - -%% {error, _} = -%% odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ -%% "'" ++ string:chars($a, (?RDBMS:text_max()+1)) + +%% {error, _} = +%% odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ +%% "'" ++ string:chars($a, (?RDBMS:text_max()+1)) %% ++ "')"). %%------------------------------------------------------------------------- @@ -518,25 +518,25 @@ text_upper_limit(Config) when is_list(Config) -> binary_char_fixed_lower_limit() -> [{doc,"Tests fixed length char data type lower boundaries."}]. binary_char_fixed_lower_limit(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), %% Below limit - {error, _} = - odbc:sql_query(Ref, - "CREATE TABLE " ++ Table ++ + {error, _} = + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ ?RDBMS:create_fixed_char_table( - (?RDBMS:fixed_char_min() - 1))), + (?RDBMS:fixed_char_min() - 1))), %% Lower limit {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ ?RDBMS:create_fixed_char_table( - ?RDBMS:fixed_char_min())), + ?RDBMS:fixed_char_min())), Str = string:chars($a, ?RDBMS:fixed_char_min()), %% Right length data - {updated, _} = + {updated, _} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ "'" ++ Str ++ "')"), @@ -550,11 +550,11 @@ binary_char_fixed_lower_limit(Config) when is_list(Config) -> ["FIELD"] = odbc_test_lib:to_upper(Fields), %% Too long data - {error, _} = + {error, _} = odbc:sql_query(Ref,"INSERT INTO " ++ Table ++" VALUES(" ++ - "'" ++ string:chars($a, + "'" ++ string:chars($a, (?RDBMS:fixed_char_min() - + 1)) + + 1)) ++ "')"). %%------------------------------------------------------------------------- @@ -566,63 +566,63 @@ binary_char_fixed_upper_limit(Config) when is_list(Config) -> postgres -> {skip, "Limit unknown"}; _ -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - - %% Upper limit + + %% Upper limit {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ ?RDBMS:create_fixed_char_table( - ?RDBMS:fixed_char_max())), - {updated, _} = - odbc:sql_query(Ref,"INSERT INTO " ++ Table ++" VALUES(" ++ - "'" ++ string:chars($a, + ?RDBMS:fixed_char_max())), + {updated, _} = + odbc:sql_query(Ref,"INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ string:chars($a, ?RDBMS:fixed_char_max()) ++ "')"), %% Select data {selected, Fields, [{CharBin}]} = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), true = size(CharBin) == ?RDBMS:fixed_char_max(), - + ["FIELD"] = odbc_test_lib:to_upper(Fields), - + %% Too long data - {error, _} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ - "'" ++ string:chars($a, + {error, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ string:chars($a, (?RDBMS:fixed_char_max() - + 1)) - ++ "')"), + + 1)) + ++ "')"), %% Clean up {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "DROP TABLE " ++ Table), - - %% Above limit + + %% Above limit {error, _} = - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ ?RDBMS:create_fixed_char_table( - (?RDBMS:fixed_char_max() + 1))), + (?RDBMS:fixed_char_max() + 1))), ok end. %%------------------------------------------------------------------------- binary_char_fixed_padding() -> - [{doc, "Tests that data that is shorter than the given size is padded " + [{doc, "Tests that data that is shorter than the given size is padded " "with blanks."}]. binary_char_fixed_padding(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), %% Data should be padded with blanks {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ ?RDBMS:create_fixed_char_table( - ?RDBMS:fixed_char_max())), + ?RDBMS:fixed_char_max())), - {updated, _} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ - "'" ++ string:chars($a, + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ string:chars($a, ?RDBMS:fixed_char_min()) ++ "')"), @@ -636,25 +636,25 @@ binary_char_fixed_padding(Config) when is_list(Config) -> binary_varchar_lower_limit() -> [{doc,"Tests variable length char data type lower boundaries."}]. binary_varchar_lower_limit(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), %% Below limit - {error, _} = - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + {error, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ ?RDBMS:create_var_char_table( - ?RDBMS:var_char_min() - 1)), + ?RDBMS:var_char_min() - 1)), %% Lower limit {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ ?RDBMS:create_var_char_table( - ?RDBMS:var_char_min())), + ?RDBMS:var_char_min())), Str = string:chars($a, ?RDBMS:var_char_min()), %% Right length data - {updated, _} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ "'" ++ Str ++ "')"), BinStr = list_to_binary(Str), @@ -662,14 +662,14 @@ binary_varchar_lower_limit(Config) when is_list(Config) -> %% Select data {selected, Fields, [{BinStr}]} = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), - + ["FIELD"] = odbc_test_lib:to_upper(Fields), %% Too long data - {error, _} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ - "'" ++ string:chars($a, - (?RDBMS:var_char_min()+1)) + {error, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ string:chars($a, + (?RDBMS:var_char_min()+1)) ++ "')"). %%------------------------------------------------------------------------- @@ -677,7 +677,7 @@ binary_varchar_lower_limit(Config) when is_list(Config) -> binary_varchar_upper_limit() -> [{doc,"Tests variable length char data type upper boundaries."}]. binary_varchar_upper_limit(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), case ?RDBMS of @@ -686,55 +686,55 @@ binary_varchar_upper_limit(Config) when is_list(Config) -> postgres -> {skip, "Limit unknown"}; _ -> - %% Upper limit + %% Upper limit {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ ?RDBMS:create_var_char_table( - ?RDBMS:var_char_max())), - {updated, _} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ - "'" ++ string:chars($a, + ?RDBMS:var_char_max())), + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ string:chars($a, ?RDBMS:var_char_max()) ++ "')"), - + {selected, Fields, [{CharBin}]} = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), true = size(CharBin) == ?RDBMS:var_char_max(), - + ["FIELD"] = odbc_test_lib:to_upper(Fields), - + %% Too long data - {error, _} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ - "'" ++ string:chars($a, - (?RDBMS:var_char_max()+1)) + {error, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ string:chars($a, + (?RDBMS:var_char_max()+1)) ++ "')"), %% Clean up {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "DROP TABLE " ++ Table), - - %% Above limit + odbc:sql_query(Ref, "DROP TABLE " ++ Table), + + %% Above limit {error, _} = - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ ?RDBMS:create_var_char_table( (?RDBMS:var_char_max() + 1))) end. %%------------------------------------------------------------------------- binary_varchar_no_padding() -> - [{doc,"Tests that data that is shorter than the given max size is not padded " + [{doc,"Tests that data that is shorter than the given max size is not padded " "with blanks."}]. binary_varchar_no_padding(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), %% Data should NOT be padded with blanks {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ ?RDBMS:create_var_char_table( - ?RDBMS:var_char_max())), - {updated, _} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + ?RDBMS:var_char_max())), + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ "'" ++ string:chars($a, ?RDBMS:var_char_min()) ++ "')"), @@ -748,15 +748,15 @@ binary_varchar_no_padding(Config) when is_list(Config) -> binary_text_lower_limit() -> [{doc,"Tests 'long' char data type lower boundaries."}]. binary_text_lower_limit(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - ?RDBMS:create_text_table()), + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_text_table()), - {updated, _} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ "'" ++ string:chars($a, ?RDBMS:text_min()) ++ "')"), @@ -769,16 +769,16 @@ binary_text_lower_limit(Config) when is_list(Config) -> binary_text_upper_limit() -> [{doc,"Tests text char data type upper boundaries."}]. binary_text_upper_limit(Config) when is_list(Config) -> - + {skip,"Consumes too much resources" }. %% Ref = proplists:get_value(connection_ref, Config), %% Table = proplists:get_value(tableName, Config), %% {updated, _} = % Value == 0 || -1 driver dependent! -%% odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ -%% ?RDBMS:create_text_table()), -%% {updated, _} = -%% odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ +%% odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ +%% ?RDBMS:create_text_table()), +%% {updated, _} = +%% odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ %% "'" ++ string:chars($a, ?RDBMS:text_max()) %% ++ "')"), @@ -786,10 +786,10 @@ binary_text_upper_limit(Config) when is_list(Config) -> %% odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), %% size(CharBin) == ?RDBMS:text_max(), %% ["FIELD"] = odbc_test_lib:to_upper(Fields), - -%% {error, _} = -%% odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ -%% "'" ++ string:chars($a, (?RDBMS:text_max()+1)) + +%% {error, _} = +%% odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ +%% "'" ++ string:chars($a, (?RDBMS:text_max()+1)) %% ++ "')"). @@ -797,30 +797,30 @@ binary_text_upper_limit(Config) when is_list(Config) -> tiny_int_lower_limit() -> [{doc,"Tests integer of type tinyint."}]. -tiny_int_lower_limit(Config) when is_list(Config) -> +tiny_int_lower_limit(Config) when is_list(Config) -> case ?RDBMS of postgres -> {skip, "Type tiniyint not supported"}; _ -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - + {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - ?RDBMS:create_tiny_int_table()), - - {updated, _} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_tiny_int_table()), + + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ "'" ++ integer_to_list(?RDBMS:tiny_int_min()) ++ "')"), - + SelectResult = ?RDBMS:tiny_int_min_selected(), SelectResult = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), - - {error, _} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ - "'" ++ integer_to_list(?RDBMS:tiny_int_min() + + {error, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ integer_to_list(?RDBMS:tiny_int_min() - 1) ++ "')") end. @@ -834,24 +834,24 @@ tiny_int_upper_limit(Config) when is_list(Config) -> postgres -> {skip, "Type tiniyint not supported"}; _ -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - + {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - ?RDBMS:create_tiny_int_table()), - - {updated, _} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_tiny_int_table()), + + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ "'" ++ integer_to_list(?RDBMS:tiny_int_max()) ++ "')"), - + SelectResult = ?RDBMS:tiny_int_max_selected(), SelectResult = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), - - {error, _} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + + {error, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ "'" ++ integer_to_list(?RDBMS:tiny_int_max() + 1) ++ "')") @@ -862,15 +862,15 @@ tiny_int_upper_limit(Config) when is_list(Config) -> small_int_lower_limit() -> [{doc,"Tests integer of type smallint."}]. small_int_lower_limit(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - ?RDBMS:create_small_int_table()), + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_small_int_table()), - {updated, _} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ "'" ++ integer_to_list(?RDBMS:small_int_min()) ++ "')"), @@ -878,8 +878,8 @@ small_int_lower_limit(Config) when is_list(Config) -> SelectResult = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), - {error, _} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + {error, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ "'" ++ integer_to_list(?RDBMS:small_int_min() - 1) ++ "')"). @@ -889,15 +889,15 @@ small_int_lower_limit(Config) when is_list(Config) -> small_int_upper_limit() -> [{doc,"Tests integer of type smallint."}]. small_int_upper_limit(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - ?RDBMS:create_small_int_table()), + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_small_int_table()), - {updated, _} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ "'" ++ integer_to_list(?RDBMS:small_int_max()) ++ "')"), @@ -905,9 +905,9 @@ small_int_upper_limit(Config) when is_list(Config) -> SelectResult = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), - {error, _} = - odbc:sql_query(Ref,"INSERT INTO " ++ Table ++" VALUES(" ++ - "'" ++ integer_to_list(?RDBMS:small_int_max() + {error, _} = + odbc:sql_query(Ref,"INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ integer_to_list(?RDBMS:small_int_max() + 1) ++ "')"). @@ -915,15 +915,15 @@ small_int_upper_limit(Config) when is_list(Config) -> int_lower_limit() -> [{doc,"Tests integer of type int."}]. int_lower_limit(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - ?RDBMS:create_int_table()), + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_int_table()), - {updated, _} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ "'" ++ integer_to_list(?RDBMS:int_min()) ++ "')"), @@ -931,8 +931,8 @@ int_lower_limit(Config) when is_list(Config) -> SelectResult = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), - {error, _} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + {error, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ "'" ++ integer_to_list(?RDBMS:int_min() - 1) ++ "')"). @@ -941,15 +941,15 @@ int_lower_limit(Config) when is_list(Config) -> int_upper_limit() -> [{doc,"Tests integer of type int."}]. int_upper_limit(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - ?RDBMS:create_int_table()), + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_int_table()), - {updated, _} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ "'" ++ integer_to_list(?RDBMS:int_max()) ++ "')"), @@ -957,8 +957,8 @@ int_upper_limit(Config) when is_list(Config) -> SelectResult = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), - {error, _} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + {error, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ "'" ++ integer_to_list(?RDBMS:int_max() + 1) ++ "')"). @@ -967,15 +967,15 @@ int_upper_limit(Config) when is_list(Config) -> big_int_lower_limit() -> [{doc,"Tests integer of type bigint"}]. big_int_lower_limit(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - ?RDBMS:create_big_int_table()), + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_big_int_table()), - {updated, _} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ "'" ++ integer_to_list(?RDBMS:big_int_min()) ++ "')"), @@ -983,9 +983,9 @@ big_int_lower_limit(Config) when is_list(Config) -> SelectResult = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), - {error, _} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ - "'" ++ integer_to_list(?RDBMS:big_int_min() + {error, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ integer_to_list(?RDBMS:big_int_min() - 1) ++ "')"). @@ -994,15 +994,15 @@ big_int_lower_limit(Config) when is_list(Config) -> big_int_upper_limit() -> [{doc,"Tests integer of type bigint."}]. big_int_upper_limit(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - ?RDBMS:create_big_int_table()), + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_big_int_table()), - {updated, _} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ "'" ++ integer_to_list(?RDBMS:big_int_max()) ++ "')"), @@ -1010,39 +1010,44 @@ big_int_upper_limit(Config) when is_list(Config) -> SelectResult = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), - {error, _} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ - "'" ++ integer_to_list(?RDBMS:big_int_max() + {error, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ integer_to_list(?RDBMS:big_int_max() + 1) ++ "')"). %%------------------------------------------------------------------------- -bit_false(Config) when is_list(Config) -> +bit_false(Config) when is_list(Config) -> case ?RDBMS of oracle -> {skip, "Not supported by driver"}; _ -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - + {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - ?RDBMS:create_bit_table()), - - {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_bit_table()), + + {updated, _} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ - " VALUES(" ++ + " VALUES(" ++ "'" ++ integer_to_list(?RDBMS:bit_false()) ++ "')"), - + SelectResult = ?RDBMS:bit_false_selected(), SelectResult = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), - - {error, _} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ - "'" ++ integer_to_list(-1) - ++ "')") + + InvalidInsert = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES(" ++ "'" ++ + integer_to_list(-1) ++ "')"), + case ?RDBMS:non_bit_promotes_to() of + error -> + {error, _} = InvalidInsert; + Value -> + {updated, Value} = InvalidInsert + end end. %%------------------------------------------------------------------------- @@ -1052,35 +1057,40 @@ bit_true(Config) when is_list(Config) -> oracle -> {skip, "Not supported by driver"}; _ -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - - + + {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - ?RDBMS:create_bit_table()), - - {updated, _} = - odbc:sql_query(Ref, - "INSERT INTO " ++ Table ++" VALUES(" ++ + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_bit_table()), + + {updated, _} = + odbc:sql_query(Ref, + "INSERT INTO " ++ Table ++" VALUES(" ++ "'" ++ integer_to_list(?RDBMS:bit_true()) ++ "')"), - + SelectResult = ?RDBMS:bit_true_selected(), SelectResult = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), - - {error, _} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ - "'" ++ integer_to_list(-1) - ++ "')") + + InvalidInsert = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES(" ++ "'" ++ + integer_to_list(-1) ++ "')"), + case ?RDBMS:non_bit_promotes_to() of + error -> + {error, _} = InvalidInsert; + Value -> + {updated, Value} = InvalidInsert + end end. %%------------------------------------------------------------------------- -float_lower_limit(Config) when is_list(Config) -> +float_lower_limit(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), case ?RDBMS of @@ -1088,11 +1098,11 @@ float_lower_limit(Config) when is_list(Config) -> {skip, "Not clearly defined in MYSQL"}; _ -> {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ ?RDBMS:create_float_table()), - {updated, _} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ "'" ++ float_to_list( ?RDBMS:float_min()) ++ "')"), @@ -1125,8 +1135,8 @@ float_lower_limit(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -float_upper_limit(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), +float_upper_limit(Config) when is_list(Config) -> + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), case ?RDBMS of @@ -1155,15 +1165,15 @@ float_upper_limit(Config) when is_list(Config) -> %%------------------------------------------------------------------------- float_zero() -> [{doc,"Test the float value zero."}]. -float_zero(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), +float_zero(Config) when is_list(Config) -> + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - ?RDBMS:create_float_table()), + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_float_table()), - {updated, _} = + {updated, _} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES('0')"), SelectResult = ?RDBMS:float_zero_selected(), @@ -1172,19 +1182,19 @@ float_zero(Config) when is_list(Config) -> %%------------------------------------------------------------------------- real_zero() -> [{doc,"Test the real value zero."}]. -real_zero(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), +real_zero(Config) when is_list(Config) -> + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), case ?RDBMS of oracle -> - {skip, "Not supported in Oracle"}; - _ -> + {skip, "Not supported in Oracle"}; + _ -> {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - ?RDBMS:create_real_table()), + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_real_table()), - {updated, _} = + {updated, _} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES('0')"), @@ -1196,14 +1206,14 @@ real_zero(Config) when is_list(Config) -> dec_long(suit) -> []; dec_long(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - "(FIELD DECIMAL (9,0))"), + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + "(FIELD DECIMAL (9,0))"), - {updated, _} = + {updated, _} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1.6)"), {selected, Fields, [{2}]} = @@ -1213,45 +1223,48 @@ dec_long(Config) when is_list(Config) -> dec_double(suit) -> []; dec_double(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - "(FIELD DECIMAL (10,0))"), - {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + "(FIELD DECIMAL (10,0))"), + {updated, _} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1.6)"), - {selected, Fields, [{2.00000}]} = + ExpectedValue1 = ?RDBMS:expected_value(10, 0, 1.6), + {selected, Fields, [{ExpectedValue1}]} = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), ["FIELD"] = odbc_test_lib:to_upper(Fields), %% Clean up {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "DROP TABLE " ++ Table), + odbc:sql_query(Ref, "DROP TABLE " ++ Table), {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - "(FIELD DECIMAL (15,0))"), - {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + "(FIELD DECIMAL (15,0))"), + {updated, _} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1.6)"), - {selected, Fields1, [{2.00000}]} = + ExpectedValue2 = ?RDBMS:expected_value(15, 0, 1.6), + {selected, Fields1, [{ExpectedValue2}]} = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), ["FIELD"] = odbc_test_lib:to_upper(Fields1), %% Clean up {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "DROP TABLE " ++ Table), + odbc:sql_query(Ref, "DROP TABLE " ++ Table), {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - "(FIELD DECIMAL (15, 1))"), - {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + "(FIELD DECIMAL (15, 1))"), + {updated, _} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1.6)"), - {selected, Fields2, [{1.60000}]} = + ExpectedValue3 = ?RDBMS:expected_value(15, 1, 1.6), + {selected, Fields2, [{ExpectedValue3}]} = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), ["FIELD"] = odbc_test_lib:to_upper(Fields2). @@ -1259,44 +1272,46 @@ dec_double(Config) when is_list(Config) -> dec_bignum(suit) -> []; dec_bignum(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - "(FIELD DECIMAL (16,0))"), - {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + "(FIELD DECIMAL (16,0))"), + {updated, _} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1.6)"), - {selected, Fields, [{"2"}]} = + ExpectedValue1 = ?RDBMS:expected_value(16, 0, 1.6), + {selected, Fields, [{ExpectedValue1}]} = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), ["FIELD"] = odbc_test_lib:to_upper(Fields), %% Clean up {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "DROP TABLE " ++ Table), + odbc:sql_query(Ref, "DROP TABLE " ++ Table), {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - "(FIELD DECIMAL (16,1))"), - {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + "(FIELD DECIMAL (16,1))"), + {updated, _} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1.6)"), - {selected, Fields1, [{"1.6"}]} = + ExpectedValue2 = ?RDBMS:expected_value(16, 1, 1.6), + {selected, Fields1, [{ExpectedValue2}]} = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), ["FIELD"] = odbc_test_lib:to_upper(Fields1). %%------------------------------------------------------------------------ num_long(suit) -> []; num_long(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - "(FIELD DECIMAL (9,0))"), + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + "(FIELD DECIMAL (9,0))"), - {updated, _} = + {updated, _} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1.5)"), {selected, Fields, [{2}]} = @@ -1306,74 +1321,79 @@ num_long(Config) when is_list(Config) -> num_double(suit) -> []; num_double(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - "(FIELD DECIMAL (10,0))"), - {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + "(FIELD DECIMAL (10,0))"), + {updated, _} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1.6)"), - {selected, Fields, [{2.0000}]} = + ExpectedValue1 = ?RDBMS:expected_value(10, 0, 1.6), + {selected, Fields, [{ExpectedValue1}]} = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), ["FIELD"] = odbc_test_lib:to_upper(Fields), %% Clean up {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "DROP TABLE " ++ Table), + odbc:sql_query(Ref, "DROP TABLE " ++ Table), {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - "(FIELD DECIMAL (15,0))"), - {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + "(FIELD DECIMAL (15,0))"), + {updated, _} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1.6)"), - {selected, Fields1, [{2.0000}]} = + ExpectedValue2 = ?RDBMS:expected_value(15, 0, 1.6), + {selected, Fields1, [{ExpectedValue2}]} = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), ["FIELD"] = odbc_test_lib:to_upper(Fields1), %% Clean up {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "DROP TABLE " ++ Table), + odbc:sql_query(Ref, "DROP TABLE " ++ Table), {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - "(FIELD DECIMAL (15,1))"), - {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + "(FIELD DECIMAL (15,1))"), + {updated, _} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1.6)"), - {selected, Fields2, [{1.6000}]} = + ExpectedValue3 = ?RDBMS:expected_value(15, 1, 1.6), + {selected, Fields2, [{ExpectedValue3}]} = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), ["FIELD"] = odbc_test_lib:to_upper(Fields2). %%------------------------------------------------------------------------ num_bignum(suit) -> []; num_bignum(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - "(FIELD DECIMAL (16,0))"), - {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + "(FIELD DECIMAL (16,0))"), + {updated, _} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1.6)"), - {selected, Fields, [{"2"}]} = + ExpectedValue1 = ?RDBMS:expected_value(16, 0, 1.6), + {selected, Fields, [{ExpectedValue1}]} = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), ["FIELD"] = odbc_test_lib:to_upper(Fields), %% Clean up {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "DROP TABLE " ++ Table), + odbc:sql_query(Ref, "DROP TABLE " ++ Table), {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - "(FIELD DECIMAL (16,1))"), - {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + "(FIELD DECIMAL (16,1))"), + {updated, _} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1.6)"), - {selected, Fields1, [{"1.6"}]} = + ExpectedValue2 = ?RDBMS:expected_value(16, 1, 1.6), + {selected, Fields1, [{ExpectedValue2}]} = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), ["FIELD"] = odbc_test_lib:to_upper(Fields1). @@ -1383,9 +1403,9 @@ utf8() -> utf8(suit) -> []; utf8(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ "(FIELD text)"), Latin1Data = ["ÖÄÅÄÖÅäöå", @@ -1400,26 +1420,26 @@ utf8(Config) when is_list(Config) -> "Row 10", "Row 11", "Row 12"], - + UnicodeIn = lists:map(fun(String) -> unicode:characters_to_binary(String,latin1,utf8) end, Latin1Data), - + ct:pal("UnicodeIn: ~p ~n",[UnicodeIn]), {updated, _} = odbc:param_query(Ref,"INSERT INTO " ++ Table ++ "(FIELD) values(?)", [{{sql_varchar,50}, UnicodeIn}]), - + {selected,_,UnicodeOut} = odbc:sql_query(Ref,"SELECT * FROM " ++ Table), ct:pal("UnicodeOut: ~p~n", [UnicodeOut]), - + Result = lists:map(fun({Char}) -> unicode:characters_to_list(Char,utf8) end, UnicodeOut), ct:pal("Result: ~p ~n", [Result]), - + Latin1Data = Result. %%------------------------------------------------------------------------ @@ -1457,11 +1477,11 @@ nvarchar(Config) when is_list(Config) -> timestamp(suit) -> []; timestamp(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ ?RDBMS:create_timestamp_table()), Data = [calendar:local_time(), @@ -1470,13 +1490,13 @@ timestamp(Config) when is_list(Config) -> {{2009,6,19},{20,54,59}}, {{2009,6,20},{20,54,59}}, {{2009,6,21},{20,54,59}}], - + {updated, _} = odbc:param_query(Ref,"INSERT INTO " ++ Table ++ "(FIELD) values(?)", [{sql_timestamp,Data}]), - - %%% Crate list or database table rows + + %%% Crate list or database table rows TimeStamps = lists:map(fun(Value) -> {Value} end, Data), - + {selected,_, TimeStamps} = odbc:sql_query(Ref, "SELECT * FROM " ++ Table). %%------------------------------------------------------------------------ diff --git a/lib/odbc/test/odbc_query_SUITE.erl b/lib/odbc/test/odbc_query_SUITE.erl index c283872965c9..70f08a2b0367 100644 --- a/lib/odbc/test/odbc_query_SUITE.erl +++ b/lib/odbc/test/odbc_query_SUITE.erl @@ -32,15 +32,15 @@ %% all(Arg) -> [Doc] | [Case] | {skip, Comment} %% Arg - doc | suite %% Doc - string() -%% Case - atom() -%% Name of a test case function. +%% Case - atom() +%% Name of a test case function. %% Comment - string() %% Description: Returns documentation/test cases in this test suite -%% or a skip tuple if the platform is not supported. +%% or a skip tuple if the platform is not supported. %%-------------------------------------------------------------------- suite() -> [{ct_hooks,[ts_install_cth]}]. -all() -> +all() -> case odbc_test_lib:odbc_check() of ok -> [stored_proc, sql_query, next, {group, scrollable_cursors}, select_count, @@ -53,7 +53,7 @@ all() -> Other -> {skip, Other} end. -groups() -> +groups() -> [{multiple_result_sets, [], [multiple_select_result_sets, multiple_mix_result_sets, multiple_result_sets_error]}, @@ -161,10 +161,10 @@ init_per_testcase(_Case, Config) -> end_per_testcase(_Case, Config) -> Ref = proplists:get_value(connection_ref, Config), ok = odbc:disconnect(Ref), - %% Clean up if needed + %% Clean up if needed Table = proplists:get_value(tableName, Config), {ok, NewRef} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), - odbc:sql_query(NewRef, "DROP TABLE " ++ Table), + odbc:sql_query(NewRef, "DROP TABLE " ++ Table), odbc:disconnect(NewRef). %%------------------------------------------------------------------------- @@ -196,32 +196,32 @@ sql_query(Config) when is_list(Config) -> Table = proplists:get_value(tableName, Config), {updated, _} = - odbc:sql_query(Ref, - "CREATE TABLE " ++ Table ++ + odbc:sql_query(Ref, + "CREATE TABLE " ++ Table ++ " (ID integer, DATA varchar(10))"), - {updated, Count} = + {updated, Count} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"), true = odbc_test_lib:check_row_count(1, Count), InsertResult = ?RDBMS:insert_result(), - InsertResult = + InsertResult = odbc:sql_query(Ref, "SELECT * FROM " ++ Table), - {updated, NewCount} = - odbc:sql_query(Ref, "UPDATE " ++ Table ++ + {updated, NewCount} = + odbc:sql_query(Ref, "UPDATE " ++ Table ++ " SET DATA = 'foo' WHERE ID = 1"), - + true = odbc_test_lib:check_row_count(1, NewCount), UpdateResult = ?RDBMS:update_result(), - UpdateResult = + UpdateResult = odbc:sql_query(Ref, "SELECT * FROM " ++ Table), - {updated, NewCount1} = odbc:sql_query(Ref, "DELETE FROM " ++ Table ++ + {updated, NewCount1} = odbc:sql_query(Ref, "DELETE FROM " ++ Table ++ " WHERE ID = 1"), - + true = odbc_test_lib:check_row_count(1, NewCount1), {selected, Fields, []} = @@ -231,7 +231,7 @@ sql_query(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- -select_count() -> +select_count() -> [{doc, "Tests select_count/[2,3]'s timeout, " " select_count's functionality will be better tested by other tests " " such as first."}]. @@ -240,16 +240,16 @@ select_count(Config) when is_list(Config) -> Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - {updated, _} = odbc:sql_query(Ref, + {updated, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (ID integer)"), {updated, Count} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1)"), true = odbc_test_lib:check_row_count(1, Count), - {ok, _} = + {ok, _} = odbc:select_count(Ref, "SELECT * FROM " ++ Table, ?TIMEOUT), - {'EXIT', {function_clause, _}} = + {'EXIT', {function_clause, _}} = (catch odbc:select_count(Ref, "SELECT * FROM ", -1)), ok. %%------------------------------------------------------------------------- @@ -259,10 +259,10 @@ first(Config) when is_list(Config) -> Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - {updated, _} = odbc:sql_query(Ref, + {updated, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (ID integer)"), - + {updated, Count} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1)"), true = odbc_test_lib:check_row_count(1, Count), @@ -274,7 +274,7 @@ first(Config) when is_list(Config) -> FirstResult = ?RDBMS:selected_ID(1, first), FirstResult = odbc:first(Ref), - FirstResult = odbc:first(Ref, ?TIMEOUT), + FirstResult = odbc:first(Ref, ?TIMEOUT), {'EXIT', {function_clause, _}} = (catch odbc:first(Ref, -1)), ok. @@ -285,7 +285,7 @@ last(Config) when is_list(Config) -> Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - {updated, _} = odbc:sql_query(Ref, + {updated, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (ID integer)"), @@ -300,7 +300,7 @@ last(Config) when is_list(Config) -> LastResult = ?RDBMS:selected_ID(2, last), LastResult = odbc:last(Ref), - LastResult = odbc:last(Ref, ?TIMEOUT), + LastResult = odbc:last(Ref, ?TIMEOUT), {'EXIT', {function_clause, _}} = (catch odbc:last(Ref, -1)), ok. @@ -311,7 +311,7 @@ next(Config) when is_list(Config) -> Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - {updated, _} = odbc:sql_query(Ref, + {updated, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (ID integer)"), @@ -326,7 +326,7 @@ next(Config) when is_list(Config) -> NextResult = ?RDBMS:selected_ID(1, next), NextResult = odbc:next(Ref), NextResult2 = ?RDBMS:selected_ID(2, next), - NextResult2 = odbc:next(Ref, ?TIMEOUT), + NextResult2 = odbc:next(Ref, ?TIMEOUT), {'EXIT', {function_clause, _}} = (catch odbc:next(Ref, -1)), ok. %%------------------------------------------------------------------------- @@ -336,7 +336,7 @@ prev(Config) when is_list(Config) -> Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - {updated, _} = odbc:sql_query(Ref, + {updated, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (ID integer)"), @@ -354,7 +354,7 @@ prev(Config) when is_list(Config) -> PrevResult = odbc:prev(Ref), odbc:last(Ref), % Position cursor last so there will be a prev - PrevResult = odbc:prev(Ref, ?TIMEOUT), + PrevResult = odbc:prev(Ref, ?TIMEOUT), {'EXIT', {function_clause, _}} = (catch odbc:prev(Ref, -1)), ok. %%------------------------------------------------------------------------- @@ -365,7 +365,7 @@ select_next(Config) when is_list(Config) -> Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - {updated, _} = odbc:sql_query(Ref, + {updated, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (ID integer)"), @@ -380,15 +380,15 @@ select_next(Config) when is_list(Config) -> {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(5)"), - {ok, _} = odbc:select_count(Ref, "SELECT * FROM " ++ Table), + {ok, _} = odbc:select_count(Ref, "SELECT * FROM " ++ Table), SelectResult1 = ?RDBMS:selected_next_N(1), SelectResult1 = odbc:select(Ref, next, 3), %% Test that selecting stops at the end of the result set SelectResult2 = ?RDBMS:selected_next_N(2), - SelectResult2 = odbc:select(Ref, next, 3, ?TIMEOUT), - {'EXIT',{function_clause, _}} = + SelectResult2 = odbc:select(Ref, next, 3, ?TIMEOUT), + {'EXIT',{function_clause, _}} = (catch odbc:select(Ref, next, 2, -1)), %% If you try fetching data beyond the the end of result set, @@ -406,14 +406,14 @@ select_relative(Config) when is_list(Config) -> Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - {updated, _} = odbc:sql_query(Ref, + {updated, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (ID integer)"), {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1)"), {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ - " VALUES(2)"), + " VALUES(2)"), {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(3)"), {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ @@ -435,7 +435,7 @@ select_relative(Config) when is_list(Config) -> %% Test that selecting stops at the end of the result set SelectResult2 = ?RDBMS:selected_relative_N(2), SelectResult2 = odbc:select(Ref, {relative, 3}, 3, ?TIMEOUT), - {'EXIT',{function_clause, _}} = + {'EXIT',{function_clause, _}} = (catch odbc:select(Ref, {relative, 3} , 2, -1)), ok. @@ -447,7 +447,7 @@ select_absolute(Config) when is_list(Config) -> Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - {updated, _} = odbc:sql_query(Ref, + {updated, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (ID integer)"), @@ -461,7 +461,7 @@ select_absolute(Config) when is_list(Config) -> " VALUES(4)"), {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(5)"), - {ok, _} = odbc:select_count(Ref, "SELECT * FROM " ++ Table), + {ok, _} = odbc:select_count(Ref, "SELECT * FROM " ++ Table), SelectResult1 = ?RDBMS:selected_absolute_N(1), SelectResult1 = odbc:select(Ref, {absolute, 1}, 3), @@ -469,7 +469,7 @@ select_absolute(Config) when is_list(Config) -> %% Test that selecting stops at the end of the result set SelectResult2 = ?RDBMS:selected_absolute_N(2), SelectResult2 = odbc:select(Ref, {absolute, 1}, 6, ?TIMEOUT), - {'EXIT',{function_clause, _}} = + {'EXIT',{function_clause, _}} = (catch odbc:select(Ref, {absolute, 1}, 2, -1)), ok. @@ -477,15 +477,15 @@ select_absolute(Config) when is_list(Config) -> create_table_twice() -> [{doc, "Test what happens if you try to create the same table twice."}]. create_table_twice(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - {updated, _} = - odbc:sql_query(Ref, + {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (ID integer, DATA varchar(10))"), - {error, Error} = - odbc:sql_query(Ref, + {error, Error} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (ID integer, DATA varchar(10))"), is_driver_error(Error), @@ -495,11 +495,11 @@ create_table_twice(Config) when is_list(Config) -> delete_table_twice() -> [{doc, "Test what happens if you try to delete the same table twice."}]. delete_table_twice(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - {updated, _} = - odbc:sql_query(Ref, + {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (ID integer, DATA varchar(10))"), {updated, _} = odbc:sql_query(Ref, "DROP TABLE " ++ Table), @@ -512,15 +512,15 @@ duplicate_key() -> [{doc, "Test what happens if you try to use the same key twice"}]. duplicate_key(suit) -> []; duplicate_key(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - {updated, _} = - odbc:sql_query(Ref, + {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (ID integer, DATA char(10), PRIMARY KEY(ID))"), - {updated, 1} = + {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"), {error, Error} = @@ -533,12 +533,12 @@ not_connection_owner() -> [{doc, "Test what happens if a process that did not start the connection" " tries to acess it."}]. not_connection_owner(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), spawn_link(?MODULE, not_owner, [self(), Ref, Table]), - receive + receive continue -> ok end. @@ -557,36 +557,36 @@ no_result_set() -> [{doc, "Tests what happens if you try to use a function that needs an " "associated result set when there is none."}]. no_result_set(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), {error, result_set_does_not_exist} = odbc:first(Ref), {error, result_set_does_not_exist} = odbc:last(Ref), {error, result_set_does_not_exist} = odbc:next(Ref), {error, result_set_does_not_exist} = odbc:prev(Ref), {error, result_set_does_not_exist} = odbc:select(Ref, next, 1), - {error, result_set_does_not_exist} = + {error, result_set_does_not_exist} = odbc:select(Ref, {absolute, 2}, 1), - {error, result_set_does_not_exist} = + {error, result_set_does_not_exist} = odbc:select(Ref, {relative, 2}, 1), ok. %%------------------------------------------------------------------------- query_error() -> [{doc, "Test what happens if there is an error in the query."}]. query_error(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - {updated, _} = - odbc:sql_query(Ref, + {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (ID integer, DATA char(10), PRIMARY KEY(ID))"), - {updated, 1} = + {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"), - {error, _} = + {error, _} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"), - {error, _} = + {error, _} = odbc:sql_query(Ref, "INSERT ONTO " ++ Table ++ " VALUES(1,'bar')"), ok. @@ -596,33 +596,33 @@ multiple_select_result_sets() -> multiple_select_result_sets(Config) when is_list(Config) -> case ?RDBMS of sqlserver -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - - {updated, _} = - odbc:sql_query(Ref, + + {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (ID integer, DATA varchar(10), " "PRIMARY KEY(ID))"), - {updated, 1} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + {updated, 1} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"), - - {updated, 1} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + + {updated, 1} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(2, 'foo')"), - + MultipleResult = ?RDBMS:multiple_select(), - - MultipleResult = - odbc:sql_query(Ref, "SELECT * FROM " ++ Table ++ - "; SELECT DATA FROM "++ Table ++ + + MultipleResult = + odbc:sql_query(Ref, "SELECT * FROM " ++ Table ++ + "; SELECT DATA FROM "++ Table ++ " WHERE ID=2"), ok; _ -> {skip, "multiple result_set not supported"} end. - + %%------------------------------------------------------------------------- multiple_mix_result_sets() -> [{doc, "Test what happens if you have a batch of select and other type of" @@ -630,25 +630,25 @@ multiple_mix_result_sets() -> multiple_mix_result_sets(Config) when is_list(Config) -> case ?RDBMS of sqlserver -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - - {updated, _} = - odbc:sql_query(Ref, + + {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (ID integer, DATA varchar(10), " "PRIMARY KEY(ID))"), - {updated, 1} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + {updated, 1} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"), MultipleResult = ?RDBMS:multiple_mix(), - - MultipleResult = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ - " VALUES(2,'foo'); UPDATE " ++ Table ++ + + MultipleResult = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + " VALUES(2,'foo'); UPDATE " ++ Table ++ " SET DATA = 'foobar' WHERE ID =1;SELECT " - "* FROM " + "* FROM " ++ Table ++ ";DELETE FROM " ++ Table ++ " WHERE ID =1; SELECT DATA FROM " ++ Table), ok; @@ -661,62 +661,62 @@ multiple_result_sets_error() -> multiple_result_sets_error(Config) when is_list(Config) -> case ?RDBMS of sqlserver -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - - {updated, _} = - odbc:sql_query(Ref, + + {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (ID integer, DATA varchar(10), " "PRIMARY KEY(ID))"), - {updated, 1} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + {updated, 1} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"), - - {error, Error} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ + + {error, Error} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'foo'); SELECT * FROM " ++ Table), is_driver_error(Error), - - {error, NewError} = - odbc:sql_query(Ref, "SELECT * FROM " - ++ Table ++ ";INSERT INTO " ++ Table ++ + + {error, NewError} = + odbc:sql_query(Ref, "SELECT * FROM " + ++ Table ++ ";INSERT INTO " ++ Table ++ " VALUES(1,'foo')"), is_driver_error(NewError), ok; _ -> {skip, "multiple result_set not supported"} - end. + end. %%------------------------------------------------------------------------- param_insert_tiny_int()-> [{doc,"Test insertion of tiny ints by parameterized queries."}]. param_insert_tiny_int(Config) when is_list(Config) -> - case ?RDBMS of + case ?RDBMS of sqlserver -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - - {updated, _} = - odbc:sql_query(Ref, + + {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (FIELD TINYINT)"), - - {updated, Count} = - odbc:param_query(Ref, "INSERT INTO " ++ Table ++ - "(FIELD) VALUES(?)", + + {updated, Count} = + odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", [{sql_tinyint, [1, 2]}], ?TIMEOUT),%Make sure to test timeout clause - + true = odbc_test_lib:check_row_count(2, Count), - + InsertResult = ?RDBMS:param_select_tiny_int(), - - InsertResult = + + InsertResult = odbc:sql_query(Ref, "SELECT * FROM " ++ Table), - - {'EXIT',{badarg,odbc,param_query,'Params'}} = - (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + + {'EXIT',{badarg,odbc,param_query,'Params'}} = + (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ "(FIELD) VALUES(?)", [{sql_tinyint, [1, "2"]}])), ok; @@ -727,16 +727,16 @@ param_insert_tiny_int(Config) when is_list(Config) -> param_insert_small_int()-> [{doc,"Test insertion of small ints by parameterized queries."}]. param_insert_small_int(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - {updated, _} = - odbc:sql_query(Ref, + {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (FIELD SMALLINT)"), - {updated, Count} = - odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + {updated, Count} = + odbc:param_query(Ref, "INSERT INTO " ++ Table ++ "(FIELD) VALUES(?)", [{sql_smallint, [1, 2]}], ?TIMEOUT), %% Make sure to test timeout clause @@ -744,41 +744,41 @@ param_insert_small_int(Config) when is_list(Config) -> InsertResult = ?RDBMS:param_select_small_int(), - InsertResult = + InsertResult = odbc:sql_query(Ref, "SELECT * FROM " ++ Table), - {'EXIT',{badarg,odbc,param_query,'Params'}} = - (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + {'EXIT',{badarg,odbc,param_query,'Params'}} = + (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ "(FIELD) VALUES(?)", - [{sql_smallint, [1, "2"]}])), + [{sql_smallint, [1, "2"]}])), ok. %%------------------------------------------------------------------------- param_insert_int()-> [{doc,"Test insertion of ints by parameterized queries."}]. param_insert_int(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - {updated, _} = - odbc:sql_query(Ref, + {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (FIELD INT)"), Int = ?RDBMS:small_int_max() + 1, - {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ - "(FIELD) VALUES(?)", + {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", [{sql_integer, [1, Int]}]), true = odbc_test_lib:check_row_count(2, Count), - + InsertResult = ?RDBMS:param_select_int(), - InsertResult = + InsertResult = odbc:sql_query(Ref, "SELECT * FROM " ++ Table), - {'EXIT',{badarg,odbc,param_query,'Params'}} = - (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + {'EXIT',{badarg,odbc,param_query,'Params'}} = + (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ "(FIELD) VALUES(?)", [{sql_integer, [1, "2"]}])), ok. @@ -787,123 +787,123 @@ param_insert_int(Config) when is_list(Config) -> param_insert_integer()-> [{doc,"Test insertion of integers by parameterized queries."}]. param_insert_integer(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - {updated, _} = - odbc:sql_query(Ref, + {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (FIELD INTEGER)"), Int = ?RDBMS:small_int_max() + 1, - {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ - "(FIELD) VALUES(?)", + {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", [{sql_integer, [1, Int]}]), true = odbc_test_lib:check_row_count(2, Count), InsertResult = ?RDBMS:param_select_int(), - InsertResult = + InsertResult = odbc:sql_query(Ref, "SELECT * FROM " ++ Table), - {'EXIT',{badarg,odbc,param_query,'Params'}} = - (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ - "(FIELD) VALUES(?)", - [{sql_integer, [1, 2.3]}])), + {'EXIT',{badarg,odbc,param_query,'Params'}} = + (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{sql_integer, [1, 2.3]}])), ok. %%------------------------------------------------------------------------- param_insert_decimal()-> [{doc,"Test insertion of decimal numbers by parameterized queries."}]. param_insert_decimal(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - {updated, _} = - odbc:sql_query(Ref, + {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (FIELD DECIMAL (3,0))"), - {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ - "(FIELD) VALUES(?)", + {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", [{{sql_decimal, 3, 0}, [1, 2]}]), true = odbc_test_lib:check_row_count(2, Count), - + InsertResult = ?RDBMS:param_select_decimal(), - InsertResult = + InsertResult = odbc:sql_query(Ref, "SELECT * FROM " ++ Table), - {'EXIT',{badarg,odbc,param_query,'Params'}} = - (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ - "(FIELD) VALUES(?)", + {'EXIT',{badarg,odbc,param_query,'Params'}} = + (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", [{{sql_decimal, 3, 0}, [1, "2"]}])), odbc:sql_query(Ref, "DROP TABLE " ++ Table), - {updated, _} = - odbc:sql_query(Ref, + {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (FIELD DECIMAL (3,1))"), - {updated, NewCount} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ - "(FIELD) VALUES(?)", + {updated, NewCount} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", [{{sql_decimal, 3, 1}, [0.25]}]), true = odbc_test_lib:check_row_count(1, NewCount), - - {selected, Fields, [{Value}]} = + + {selected, Fields, [{Value}]} = odbc:sql_query(Ref, "SELECT * FROM " ++ Table), ["FIELD"] = odbc_test_lib:to_upper(Fields), - + odbc_test_lib:match_float(Value, 0.3, 0.01), - + ok. %%------------------------------------------------------------------------- param_insert_numeric()-> [{doc,"Test insertion of numeric numbers by parameterized queries."}]. param_insert_numeric(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - {updated, _} = - odbc:sql_query(Ref, + {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (FIELD NUMERIC (3,0))"), - {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ - "(FIELD) VALUES(?)", + {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", [{{sql_numeric,3,0}, [1, 2]}]), true = odbc_test_lib:check_row_count(2, Count), InsertResult = ?RDBMS:param_select_numeric(), - InsertResult = + InsertResult = odbc:sql_query(Ref, "SELECT * FROM " ++ Table), - {'EXIT',{badarg,odbc,param_query,'Params'}} = - (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ - "(FIELD) VALUES(?)", - [{{sql_decimal, 3, 0}, [1, "2"]}])), + {'EXIT',{badarg,odbc,param_query,'Params'}} = + (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{{sql_decimal, 3, 0}, [1, "2"]}])), odbc:sql_query(Ref, "DROP TABLE " ++ Table), - {updated, _} = - odbc:sql_query(Ref, + {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (FIELD NUMERIC (3,1))"), - {updated, NewCount} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ - "(FIELD) VALUES(?)", + {updated, NewCount} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", [{{sql_numeric, 3, 1}, [0.25]}]), true = odbc_test_lib:check_row_count(1, NewCount), - {selected, Fileds, [{Value}]} = + {selected, Fileds, [{Value}]} = odbc:sql_query(Ref, "SELECT * FROM " ++ Table), ["FIELD"] = odbc_test_lib:to_upper(Fileds), @@ -915,17 +915,17 @@ param_insert_numeric(Config) when is_list(Config) -> param_insert_char()-> [{doc,"Test insertion of fixed length string by parameterized queries."}]. param_insert_char(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - {updated, _} = - odbc:sql_query(Ref, + {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (FIELD CHAR (10))"), - {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ - "(FIELD) VALUES(?)", - [{{sql_char, 10}, + {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{{sql_char, 10}, ["foofoofoof", "0123456789"]}]), true = odbc_test_lib:check_row_count(2, Count), @@ -934,32 +934,32 @@ param_insert_char(Config) when is_list(Config) -> ["FIELD"] = odbc_test_lib:to_upper(Fileds), - {error, _} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ - "(FIELD) VALUES(?)", - [{{sql_char, 10}, + {error, _} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{{sql_char, 10}, ["foo", "01234567890"]}]), - {'EXIT',{badarg,odbc,param_query,'Params'}} = - (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + {'EXIT',{badarg,odbc,param_query,'Params'}} = + (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ "(FIELD) VALUES(?)", - [{{sql_char, 10}, ["1", 2.3]}])), + [{{sql_char, 10}, ["1", 2.3]}])), ok. %%------------------------------------------------------------------------- param_insert_character()-> [{doc,"Test insertion of fixed length string by parameterized queries."}]. param_insert_character(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - {updated, _} = - odbc:sql_query(Ref, + {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (FIELD CHARACTER (10))"), - {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ - "(FIELD) VALUES(?)", - [{{sql_char, 10}, + {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{{sql_char, 10}, ["foofoofoof", "0123456789"]}]), true = odbc_test_lib:check_row_count(2, Count), @@ -969,34 +969,34 @@ param_insert_character(Config) when is_list(Config) -> ["FIELD"] = odbc_test_lib:to_upper(Fileds), - {error, _} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ - "(FIELD) VALUES(?)", - [{{sql_char, 10}, + {error, _} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{{sql_char, 10}, ["foo", "01234567890"]}]), - {'EXIT',{badarg,odbc,param_query,'Params'}} = - (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ - "(FIELD) VALUES(?)", - [{{sql_char, 10}, ["1", 2]}])), + {'EXIT',{badarg,odbc,param_query,'Params'}} = + (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{{sql_char, 10}, ["1", 2]}])), ok. %%------------------------------------------------------------------------ param_insert_char_varying()-> [{doc,"Test insertion of variable length strings by parameterized queries."}]. param_insert_char_varying(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - {updated, _} = - odbc:sql_query(Ref, + {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (FIELD CHAR VARYING(10))"), - {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ - "(FIELD) VALUES(?)", - [{{sql_varchar, 10}, + {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{{sql_varchar, 10}, ["foo", "0123456789"]}]), - + true = odbc_test_lib:check_row_count(2, Count), {selected, Fileds, [{"foo"}, {"0123456789"}]} = @@ -1004,33 +1004,33 @@ param_insert_char_varying(Config) when is_list(Config) -> ["FIELD"] = odbc_test_lib:to_upper(Fileds), - {error, _} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ - "(FIELD) VALUES(?)", - [{{sql_varchar, 10}, + {error, _} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{{sql_varchar, 10}, ["foo", "01234567890"]}]), - {'EXIT',{badarg,odbc,param_query,'Params'}} = - (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ - "(FIELD) VALUES(?)", - [{{sql_varchar, 10}, ["1", 2.3]}])), + {'EXIT',{badarg,odbc,param_query,'Params'}} = + (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{{sql_varchar, 10}, ["1", 2.3]}])), ok. %%------------------------------------------------------------------------- param_insert_character_varying()-> [{doc,"Test insertion of variable length strings by parameterized queries."}]. param_insert_character_varying(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - {updated, _} = - odbc:sql_query(Ref, + {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (FIELD CHARACTER VARYING(10))"), - {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ - "(FIELD) VALUES(?)", - [{{sql_varchar, 10}, + {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{{sql_varchar, 10}, ["foo", "0123456789"]}]), true = odbc_test_lib:check_row_count(2, Count), @@ -1040,30 +1040,30 @@ param_insert_character_varying(Config) when is_list(Config) -> ["FIELD"] = odbc_test_lib:to_upper(Fileds), - {error, _} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ - "(FIELD) VALUES(?)", - [{{sql_varchar, 10}, + {error, _} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{{sql_varchar, 10}, ["foo", "01234567890"]}]), - {'EXIT',{badarg,odbc,param_query,'Params'}} = - (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ - "(FIELD) VALUES(?)", - [{{sql_varchar, 10}, ["1", 2]}])), + {'EXIT',{badarg,odbc,param_query,'Params'}} = + (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", + [{{sql_varchar, 10}, ["1", 2]}])), ok. %%------------------------------------------------------------------------- param_insert_float()-> [{doc,"Test insertion of floats by parameterized queries."}]. param_insert_float(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - {updated, _} = - odbc:sql_query(Ref, + {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (FIELD FLOAT(5))"), - {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ - "(FIELD) VALUES(?)", + {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", [{{sql_float,5}, [1.3, 1.2]}]), true = odbc_test_lib:check_row_count(2, Count), @@ -1073,7 +1073,7 @@ param_insert_float(Config) when is_list(Config) -> ["FIELD"] = odbc_test_lib:to_upper(Fileds), - case (odbc_test_lib:match_float(Float1, 1.3, 0.000001) and + case (odbc_test_lib:match_float(Float1, 1.3, 0.000001) and odbc_test_lib:match_float(Float2, 1.2, 0.000001)) of true -> ok; @@ -1081,8 +1081,8 @@ param_insert_float(Config) when is_list(Config) -> ct:fail(float_numbers_do_not_match) end, - {'EXIT',{badarg,odbc,param_query,'Params'}} = - (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + {'EXIT',{badarg,odbc,param_query,'Params'}} = + (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ "(FIELD) VALUES(?)", [{{sql_float, 5}, [1.0, "2"]}])), ok. @@ -1091,16 +1091,16 @@ param_insert_float(Config) when is_list(Config) -> param_insert_real()-> [{doc,"Test insertion of real numbers by parameterized queries."}]. param_insert_real(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - {updated, _} = - odbc:sql_query(Ref, + {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (FIELD REAL)"), - {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ - "(FIELD) VALUES(?)", + {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", [{sql_real, [1.3, 1.2]}]), true = odbc_test_lib:check_row_count(2, Count), @@ -1112,7 +1112,7 @@ param_insert_real(Config) when is_list(Config) -> ["FIELD"] = odbc_test_lib:to_upper(Fileds), - case (odbc_test_lib:match_float(Real1, 1.3, 0.000001) and + case (odbc_test_lib:match_float(Real1, 1.3, 0.000001) and odbc_test_lib:match_float(Real2, 1.2, 0.000001)) of true -> ok; @@ -1120,8 +1120,8 @@ param_insert_real(Config) when is_list(Config) -> ct:fail(real_numbers_do_not_match) end, - {'EXIT',{badarg,odbc,param_query,'Params'}} = - (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + {'EXIT',{badarg,odbc,param_query,'Params'}} = + (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ "(FIELD) VALUES(?)", [{sql_real,[1.0, "2"]}])), ok. @@ -1130,16 +1130,16 @@ param_insert_real(Config) when is_list(Config) -> param_insert_double()-> [{doc,"Test insertion of doubles by parameterized queries."}]. param_insert_double(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - {updated, _} = - odbc:sql_query(Ref, + {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (FIELD DOUBLE PRECISION)"), - {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ - "(FIELD) VALUES(?)", + {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", [{sql_double, [1.3, 1.2]}]), true = odbc_test_lib:check_row_count(2, Count), @@ -1149,7 +1149,7 @@ param_insert_double(Config) when is_list(Config) -> ["FIELD"] = odbc_test_lib:to_upper(Fileds), - case (odbc_test_lib:match_float(Double1, 1.3, 0.000001) and + case (odbc_test_lib:match_float(Double1, 1.3, 0.000001) and odbc_test_lib:match_float(Double2, 1.2, 0.000001)) of true -> ok; @@ -1157,9 +1157,9 @@ param_insert_double(Config) when is_list(Config) -> ct:fail(double_numbers_do_not_match) end, - {'EXIT',{badarg,odbc,param_query,'Params'}} = - (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ - "(FIELD) VALUES(?)", + {'EXIT',{badarg,odbc,param_query,'Params'}} = + (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + "(FIELD) VALUES(?)", [{sql_double, [1.0, "2"]}])), ok. @@ -1167,57 +1167,57 @@ param_insert_double(Config) when is_list(Config) -> param_insert_mix()-> [{doc,"Test insertion of a mixture of datatypes by parameterized queries."}]. param_insert_mix(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - {updated, _} = - odbc:sql_query(Ref, + {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (ID INTEGER, DATA CHARACTER VARYING(10)," " PRIMARY KEY(ID))"), - {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ "(ID, DATA) VALUES(?, ?)", - [{sql_integer, [1, 2]}, + [{sql_integer, [1, 2]}, {{sql_varchar, 10}, ["foo", "bar"]}]), true = odbc_test_lib:check_row_count(2, Count), InsertResult = ?RDBMS:param_select_mix(), - InsertResult = + InsertResult = odbc:sql_query(Ref, "SELECT * FROM " ++ Table), ok. %%------------------------------------------------------------------------- param_update()-> [{doc,"Test parameterized update query."}]. param_update(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - {updated, _} = - odbc:sql_query(Ref, + {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (ID INTEGER, DATA CHARACTER VARYING(10)," " PRIMARY KEY(ID))"), - {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ "(ID, DATA) VALUES(?, ?)", - [{sql_integer, [1, 2, 3]}, - {{sql_varchar, 10}, + [{sql_integer, [1, 2, 3]}, + {{sql_varchar, 10}, ["foo", "bar", "baz"]}]), true = odbc_test_lib:check_row_count(3, Count), {updated, NewCount} = odbc:param_query(Ref, "UPDATE " ++ Table ++ " SET DATA = 'foobar' WHERE ID = ?", - [{sql_integer, [1, 2]}]), + [{sql_integer, [1, 2]}]), true = odbc_test_lib:check_row_count(2, NewCount), UpdateResult = ?RDBMS:param_update(), - UpdateResult = + UpdateResult = odbc:sql_query(Ref, "SELECT * FROM " ++ Table), ok. @@ -1226,18 +1226,18 @@ delete_nonexisting_row() -> % OTP-5759 [{doc, "Make a delete...where with false conditions (0 rows deleted). ", "This used to give an error message (see ticket OTP-5759)."}]. delete_nonexisting_row(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), {updated, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (ID INTEGER, DATA CHARACTER VARYING(10))"), {updated, Count} = - odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + odbc:param_query(Ref, "INSERT INTO " ++ Table ++ "(ID, DATA) VALUES(?, ?)", [{sql_integer, [1, 2, 3]}, {{sql_varchar, 10}, ["foo", "bar", "baz"]}]), - + true = odbc_test_lib:check_row_count(3, Count), {updated, NewCount} = @@ -1254,31 +1254,31 @@ delete_nonexisting_row(Config) when is_list(Config) -> param_delete() -> [{doc,"Test parameterized delete query."}]. param_delete(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - {updated, _} = - odbc:sql_query(Ref, + {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (ID INTEGER, DATA CHARACTER VARYING(10)," " PRIMARY KEY(ID))"), - {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ "(ID, DATA) VALUES(?, ?)", - [{sql_integer, [1, 2, 3]}, - {{sql_varchar, 10}, + [{sql_integer, [1, 2, 3]}, + {{sql_varchar, 10}, ["foo", "bar", "baz"]}]), true = odbc_test_lib:check_row_count(3, Count), {updated, NewCount} = odbc:param_query(Ref, "DELETE FROM " ++ Table ++ " WHERE ID = ?", - [{sql_integer, [1, 2]}]), + [{sql_integer, [1, 2]}]), true = odbc_test_lib:check_row_count(2, NewCount), UpdateResult = ?RDBMS:param_delete(), - UpdateResult = + UpdateResult = odbc:sql_query(Ref, "SELECT * FROM " ++ Table), ok. @@ -1287,19 +1287,19 @@ param_delete(Config) when is_list(Config) -> param_select() -> [{doc,"Test parameterized select query."}]. param_select(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - {updated, _} = - odbc:sql_query(Ref, + {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (ID INTEGER, DATA CHARACTER VARYING(10)," " PRIMARY KEY(ID))"), - {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ "(ID, DATA) VALUES(?, ?)", - [{sql_integer, [1, 2, 3]}, - {{sql_varchar, 10}, + [{sql_integer, [1, 2, 3]}, + {{sql_varchar, 10}, ["foo", "bar", "foo"]}]), true = odbc_test_lib:check_row_count(3, Count), @@ -1308,26 +1308,26 @@ param_select(Config) when is_list(Config) -> SelectResult = odbc:param_query(Ref, "SELECT * FROM " ++ Table ++ " WHERE DATA = ?", - [{{sql_varchar, 10}, ["foo"]}]), + [{{sql_varchar, 10}, ["foo"]}]), ok. %%------------------------------------------------------------------------- param_select_empty_params() -> [{doc,"Test parameterized select query with no parameters."}]. param_select_empty_params(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - {updated, _} = - odbc:sql_query(Ref, + {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (ID INTEGER, DATA CHARACTER VARYING(10)," " PRIMARY KEY(ID))"), - {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ "(ID, DATA) VALUES(?, ?)", - [{sql_integer, [1, 2, 3]}, - {{sql_varchar, 10}, + [{sql_integer, [1, 2, 3]}, + {{sql_varchar, 10}, ["foo", "bar", "foo"]}]), true = odbc_test_lib:check_row_count(3, Count), @@ -1336,67 +1336,67 @@ param_select_empty_params(Config) when is_list(Config) -> SelectResult = odbc:param_query(Ref, "SELECT * FROM " ++ Table ++ " WHERE DATA = \'foo\'", - []), + []), ok. %%------------------------------------------------------------------------- param_delete_empty_params() -> [{doc,"Test parameterized delete query with no parameters."}]. param_delete_empty_params(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - {updated, _} = - odbc:sql_query(Ref, + {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (ID INTEGER, DATA CHARACTER VARYING(10)," " PRIMARY KEY(ID))"), - {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ + {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ "(ID, DATA) VALUES(?, ?)", - [{sql_integer, [1, 2, 3]}, - {{sql_varchar, 10}, + [{sql_integer, [1, 2, 3]}, + {{sql_varchar, 10}, ["foo", "bar", "baz"]}]), true = odbc_test_lib:check_row_count(3, Count), {updated, NewCount} = odbc:param_query(Ref, "DELETE FROM " ++ Table ++ " WHERE ID = 1 OR ID = 2", - []), + []), true = odbc_test_lib:check_row_count(2, NewCount), UpdateResult = ?RDBMS:param_delete(), - UpdateResult = + UpdateResult = odbc:sql_query(Ref, "SELECT * FROM " ++ Table), ok. %%------------------------------------------------------------------------- describe_integer() -> [{doc,"Test describe_table/[2,3] for integer columns."}]. -describe_integer(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), +describe_integer(Config) when is_list(Config) -> + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - {updated, _} = - odbc:sql_query(Ref, + {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (myint1 SMALLINT, myint2 INT, myint3 INTEGER)"), Decs = ?RDBMS:describe_integer(), %% Make sure to test timeout clause - Decs = odbc:describe_table(Ref, Table, ?TIMEOUT), + Decs = odbc:describe_table(Ref, Table, ?TIMEOUT), ok. %%------------------------------------------------------------------------- describe_string() -> [{doc,"Test describe_table/[2,3] for string columns."}]. describe_string(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - {updated, _} = - odbc:sql_query(Ref, + {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (str1 char(10), str2 character(10), " "str3 CHAR VARYING(10), str4 " @@ -1411,11 +1411,11 @@ describe_string(Config) when is_list(Config) -> describe_floating() -> [{doc,"Test describe_table/[2,3] for floting columns."}]. describe_floating(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - {updated, _} = - odbc:sql_query(Ref, + {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (f FLOAT(5), r REAL, " "d DOUBLE PRECISION)"), @@ -1430,11 +1430,11 @@ describe_dec_num() -> [{doc,"Test describe_table/[2,3] for decimal and numerical columns"}]. describe_dec_num(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - {updated, _} = - odbc:sql_query(Ref, + {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (mydec DECIMAL(9,3), mynum NUMERIC(9,2))"), @@ -1448,12 +1448,12 @@ describe_dec_num(Config) when is_list(Config) -> describe_timestamp() -> [{doc,"Test describe_table/[2,3] for tinmestap columns"}]. describe_timestamp(Config) when is_list(Config) -> - - Ref = proplists:get_value(connection_ref, Config), + + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), - + {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ ?RDBMS:create_timestamp_table()), Decs = ?RDBMS:describe_timestamp(), @@ -1466,7 +1466,7 @@ describe_no_such_table() -> [{doc,"Test what happens if you try to describe a table that does not exist."}]. describe_no_such_table(Config) when is_list(Config) -> - Ref = proplists:get_value(connection_ref, Config), + Ref = proplists:get_value(connection_ref, Config), Table = proplists:get_value(tableName, Config), {error, _ } = odbc:describe_table(Ref, Table), diff --git a/lib/odbc/test/odbc_test.hrl b/lib/odbc/test/odbc_test.hrl index 24ae2a4c2937..169f9e935499 100644 --- a/lib/odbc/test/odbc_test.hrl +++ b/lib/odbc/test/odbc_test.hrl @@ -18,7 +18,7 @@ %% %CopyrightEnd% %% - + % Default timetrap timeout (set in init_per_testcase). % This should be set relatively high (10-15 times the expected % max testcasetime). @@ -30,9 +30,9 @@ {unix,linux} -> case erlang:system_info({wordsize, external}) of 4 -> - mysql; + sqlserver; _ -> - postgres + sqlserver end; {unix, darwin} -> mysql; diff --git a/lib/odbc/test/oracle.erl b/lib/odbc/test/oracle.erl index 589401b8229f..c73b072053b6 100644 --- a/lib/odbc/test/oracle.erl +++ b/lib/odbc/test/oracle.erl @@ -61,7 +61,7 @@ selected_absolute_N(_)-> selected_list_rows() -> {selected,["ID", "DATA"],[["1", "bar"],["2","foo"]]}. - + first_list_rows() -> {error, driver_does_not_support_function}. last_list_rows() -> @@ -94,7 +94,7 @@ create_fixed_char_table(Size) -> var_char_min() -> 1. var_char_max() -> - 2000. + 2000. create_var_char_table(Size) -> " (FIELD varchar2(" ++ integer_to_list(Size) ++ "))". @@ -103,14 +103,14 @@ create_var_char_table(Size) -> text_min() -> 1. text_max() -> - 2147483646. % 2147483647. %% 2^31 - 1 + 2147483646. % 2147483647. %% 2^31 - 1 create_text_table() -> " (FIELD long)". %Oracle long is variable length char data %------------------------------------------------------------------------- create_timestamp_table() -> - " (FIELD DATETIME)". + " (FIELD DATETIME)". %------------------------------------------------------------------------- tiny_int_min() -> @@ -160,7 +160,7 @@ int_max_selected() -> %------------------------------------------------------------------------- big_int_min() -> -99999999999999999999999999999999999999. - + big_int_max() -> 99999999999999999999999999999999999999. @@ -176,7 +176,7 @@ big_int_max_selected() -> %------------------------------------------------------------------------- float_min() -> 1.40129846432481707e-45. - + float_max() -> 3.40282346638528860e+38. @@ -192,6 +192,17 @@ float_zero_selected() -> {selected,["FIELD"],[{0.00000e+0}]}. %------------------------------------------------------------------------- + +% Only really works with a subset of the possible precision and scale combinations +% It should ideally mirror the function map_dec_num_2_c_column +expected_value(Precision, _Scale, In) when Precision > 15 -> + erlang:float_to_list(In); + +expected_value(_Precision, _Scale, In) -> + In. + +%------------------------------------------------------------------------- + param_select_small_int() -> {selected,["FIELD"],[{"1"}, {"2"}]}. @@ -230,9 +241,9 @@ param_select() -> describe_integer() -> {ok,[{"MYINT1",{sql_decimal,38,0}},{"MYINT2",{sql_decimal,38,0}}, {"MYINT3",{sql_decimal,38,0}}]}. - + describe_string() -> - {ok,[{"STR1",{sql_char,10}}, + {ok,[{"STR1",{sql_char,10}}, {"STR2",{sql_char,10}}, {"STR3",{sql_varchar,10}}, {"STR4",{sql_varchar,10}}]}. diff --git a/lib/odbc/test/postgres.erl b/lib/odbc/test/postgres.erl index e055be954478..e07d6e94fa05 100644 --- a/lib/odbc/test/postgres.erl +++ b/lib/odbc/test/postgres.erl @@ -32,14 +32,14 @@ connection_string() -> "DSN=Postgres;UID=odbctest"; {unix, linux} -> Size = erlang:system_info({wordsize, external}), - linux_dist_connection_string(Size) + linux_dist_connection_string(Size) end. linux_dist_connection_string(4) -> case linux_dist() of "ubuntu" -> - "DSN=PostgresLinuxUbuntu;UID=odbctest"; - _ -> + "DSN=PostgresLinuxUbuntu;UID=odbctest"; + _ -> "DSN=PostgresLinux;UID=odbctest" end; @@ -50,7 +50,7 @@ linux_dist_connection_string(_) -> _ -> "DSN=PostgresLinux64;UID=odbctest" end. - + linux_dist() -> case file:read_file("/etc/issue") of {ok, Binary} -> @@ -59,8 +59,8 @@ linux_dist() -> {error, _} -> other end. - - + + %------------------------------------------------------------------------- insert_result() -> {selected,["id","data"],[{1,"bar"}]}. @@ -93,7 +93,7 @@ selected_absolute_N(_)-> selected_list_rows() -> {selected,["id", "data"],[[1, "bar"],[2,"foo"]]}. - + first_list_rows() -> {error, driver_does_not_support_function}. last_list_rows() -> @@ -117,7 +117,7 @@ multiple_mix()-> fixed_char_min() -> 1. fixed_char_max() -> - 2000. + 2000. create_fixed_char_table(Size) -> " (FIELD char(" ++ integer_to_list(Size) ++ "))". @@ -126,7 +126,7 @@ create_fixed_char_table(Size) -> var_char_min() -> 1. var_char_max() -> - 2000. + 2000. create_var_char_table(Size) -> " (FIELD varchar(" ++ integer_to_list(Size) ++ "))". @@ -135,14 +135,14 @@ create_var_char_table(Size) -> text_min() -> 1. text_max() -> - 2147483646. % 2147483647. %% 2^31 - 1 + 2147483646. % 2147483647. %% 2^31 - 1 create_text_table() -> - " (FIELD text)". + " (FIELD text)". %------------------------------------------------------------------------- create_timestamp_table() -> - " (FIELD TIMESTAMP)". + " (FIELD TIMESTAMP)". %------------------------------------------------------------------------- small_int_min() -> @@ -177,7 +177,7 @@ int_max_selected() -> %------------------------------------------------------------------------- big_int_min() -> -9223372036854775808. - + big_int_max() -> 9223372036854775807. @@ -205,6 +205,9 @@ bit_false_selected() -> bit_true_selected() -> {selected,["field"], [{"1"}]}. +non_bit_promotes_to() -> + error. + %------------------------------------------------------------------------- float_min() -> 5.0e-324. @@ -241,6 +244,17 @@ real_zero_selected() -> {selected,["field"],[{0.00000e+0}]}. %------------------------------------------------------------------------- + +% Only really works with a subset of the possible precision and scale combinations +% It should ideally mirror the function map_dec_num_2_c_column +expected_value(Precision, _Scale, In) when Precision > 15 -> + erlang:float_to_list(In); + +expected_value(_Precision, _Scale, In) -> + In. + +%------------------------------------------------------------------------- + param_select_small_int() -> {selected,["field"],[{1}, {2}]}. @@ -282,7 +296,7 @@ describe_integer() -> {"myint3",sql_integer}]}. describe_string() -> - {ok,[{"str1",{sql_char,10}}, + {ok,[{"str1",{sql_char,10}}, {"str2",{sql_char,10}}, {"str3",{sql_varchar,10}}, {"str4",{sql_varchar,10}}]}. diff --git a/lib/odbc/test/sqlserver.erl b/lib/odbc/test/sqlserver.erl index faffeaae04cf..95b62a7ab2c6 100644 --- a/lib/odbc/test/sqlserver.erl +++ b/lib/odbc/test/sqlserver.erl @@ -27,7 +27,7 @@ %------------------------------------------------------------------------- connection_string() -> - "DSN=sql-server;UID=odbctest;PWD=gurka". + "DSN=hernan-mssql;UID=hernan;PWD=hernan". %------------------------------------------------------------------------- insert_result() -> @@ -110,7 +110,7 @@ create_fixed_char_table(Size) -> var_char_min() -> 1. var_char_max() -> - 8000. + 8000. create_var_char_table(Size) -> " (FIELD varchar(" ++ integer_to_list(Size) ++ "))". @@ -118,14 +118,14 @@ create_var_char_table(Size) -> text_min() -> 1. text_max() -> - 2147483647. %% 2^31 - 1 + 2147483647. %% 2^31 - 1 create_text_table() -> " (FIELD text)". %------------------------------------------------------------------------- create_timestamp_table() -> - " (FIELD DATETIME)". + " (FIELD DATETIME)". %------------------------------------------------------------------------- tiny_int_min() -> @@ -179,7 +179,7 @@ big_int_max() -> 9223372036854775807. % 2^63-1 create_big_int_table() -> - " (FIELD bigint)". + " (FIELD bigint)". big_int_min_selected() -> {selected,["FIELD"],[{integer_to_list(big_int_min())}]}. @@ -194,13 +194,17 @@ bit_true() -> 1. create_bit_table() -> - " (FIELD bit)". + " (FIELD bit)". bit_false_selected() -> {selected,["FIELD"],[{false}]}. bit_true_selected() -> {selected,["FIELD"], [{true}]}. + +non_bit_promotes_to() -> + bit_true(). + %------------------------------------------------------------------------- float_min() -> -1.79e+308. @@ -236,6 +240,14 @@ create_real_table() -> real_zero_selected() -> {selected,["FIELD"],[{0.00000e+0}]}. %------------------------------------------------------------------------- +% Only really works with a subset of the possible precision and scale combinations +% It should ideally mirror the function map_dec_num_2_c_column +expected_value(_Precision, _Scale = 0, In) -> + round(In); + +expected_value(_Precision, _Scale, In) -> + In. +%------------------------------------------------------------------------- param_select_tiny_int() -> {selected,["FIELD"],[{1}, {2}]}. @@ -280,7 +292,7 @@ describe_integer() -> {"myint3", sql_integer}]}. describe_string() -> - {ok,[{"str1",{sql_char,10}}, + {ok,[{"str1",{sql_char,10}}, {"str2",{sql_char,10}}, {"str3",{sql_varchar,10}}, {"str4",{sql_varchar,10}}]}.