diff options
Diffstat (limited to 'lib/odbc/c_src/odbcserver.c')
-rw-r--r-- | lib/odbc/c_src/odbcserver.c | 376 |
1 files changed, 245 insertions, 131 deletions
diff --git a/lib/odbc/c_src/odbcserver.c b/lib/odbc/c_src/odbcserver.c index aaaea20a10..d61ce940c3 100644 --- a/lib/odbc/c_src/odbcserver.c +++ b/lib/odbc/c_src/odbcserver.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1999-2010. All Rights Reserved. + * Copyright Ericsson AB 1999-2011. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -59,7 +59,7 @@ they are converted to string values. [?OPEN_CONNECTION, C_AutoCommitMode, C_TraceDriver, C_SrollableCursors, - C_TupelRow, ConnectionStr] + C_TupelRow, BinaryStrings, ConnectionStr] [?CLOSE_CONNECTION] [?COMMIT_TRANSACTION, CommitMode] [?QUERY, SQLQuery] @@ -76,6 +76,7 @@ C_TraceDriver - ?ON | ?OFF C_SrollableCursors - ?ON | ?OFF C_TupelRow - - ?ON | ?OFF + BinaryStrings - ?ON | ?OFF ConnectionStr - String CommitMode - ?COMMIT | ?ROLLBACK SQLQuery - String @@ -88,7 +89,8 @@ InOrOut = [ERL_ODBC_IN | ERL_ODBC_OUT | ERL_ODBC_INOUT] Datatype - USER_INT | USER_SMALL_INT | {USER_DECIMAL, Precision, Scale} | {USER_NMERIC, Precision, Scale} | {USER_CHAR, Max} | {USER_VARCHAR, Max} | - {USER_FLOAT, Precision} | USER_REAL | USER_DOUBLE + {USER_WVARCHAR, Max} | {USER_FLOAT, Precision} | USER_REAL | USER_DOUBLE | + USER_TIMESTAMP Scale - integer Precision - integer Max - integer @@ -106,8 +108,8 @@ #if defined WIN32 #include <winsock2.h> -/* #include <ws2tcpip.h > When we can support a newer c-compiler*/ #include <windows.h> +#include <ws2tcpip.h > #include <fcntl.h> #include <sql.h> #include <sqlext.h> @@ -173,7 +175,7 @@ static void encode_column_dyn(db_column column, int column_nr, db_state *state); static void encode_data_type(SQLINTEGER sql_type, SQLINTEGER size, SQLSMALLINT decimal_digits, db_state *state); -static Boolean decode_params(byte *buffer, int *index, param_array **params, +static Boolean decode_params(db_state *state, byte *buffer, int *index, param_array **params, int i, int j); /*------------- Erlang port communication functions ----------------------*/ @@ -378,7 +380,7 @@ DWORD WINAPI database_handler(const char *port) shutdown(socket, 2); close_socket(socket); clean_socket_lib(); - DO_EXIT(EXIT_SUCCESS); + /* Exit will be done by suervisor thread */ } /* Description: Calls the appropriate function to handle the database @@ -433,26 +435,33 @@ static db_result_msg db_connect(byte *args, db_state *state) diagnos diagnos; byte *connStrIn; int erl_auto_commit_mode, erl_trace_driver, - use_srollable_cursors, tuple_row_state; + use_srollable_cursors, tuple_row_state, binary_strings; erl_auto_commit_mode = args[0]; erl_trace_driver = args[1]; use_srollable_cursors = args[2]; tuple_row_state = args[3]; - connStrIn = args + 4 * sizeof(byte); + binary_strings = args[4]; + connStrIn = args + 5 * 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; + } else { + binary_strings(state) = FALSE; + } + if(use_srollable_cursors == ON) { - use_srollable_cursors(state) = TRUE; + use_srollable_cursors(state) = TRUE; } else { - use_srollable_cursors(state) = FALSE; + use_srollable_cursors(state) = FALSE; } - + init_driver(erl_auto_commit_mode, erl_trace_driver, state); connlen = (SQLSMALLINT)strlen((const char*)connStrIn); @@ -463,7 +472,7 @@ static db_result_msg db_connect(byte *args, db_state *state) &stringlength2ptr, SQL_DRIVER_NOPROMPT); if (!sql_success(result)) { - diagnos = get_diagnos(SQL_HANDLE_STMT, statement_handle(state)); + diagnos = get_diagnos(SQL_HANDLE_DBC, connection_handle(state)); strcat((char *)diagnos.error_msg, " Connection to database failed."); msg = encode_error_message(diagnos.error_msg); @@ -1072,6 +1081,7 @@ static db_result_msg encode_out_params(db_state *state, int j = 0; param_array column; db_result_msg msg; + TIMESTAMP_STRUCT* ts; msg = encode_empty_message(); ei_x_encode_tuple_header(&dynamic_buffer(state), 3); @@ -1101,9 +1111,34 @@ static db_result_msg encode_out_params(db_state *state, } else { void* values = retrive_param_values(&column); switch(column.type.c) { + case SQL_C_TYPE_TIMESTAMP: + ts = (TIMESTAMP_STRUCT*) values; + ei_x_encode_tuple_header(&dynamic_buffer(state), 2); + ei_x_encode_tuple_header(&dynamic_buffer(state), 3); + ei_x_encode_long(&dynamic_buffer(state), (long)(ts->year)); + ei_x_encode_long(&dynamic_buffer(state), (long)(ts->month)); + ei_x_encode_long(&dynamic_buffer(state), (long)(ts->day)); + ei_x_encode_tuple_header(&dynamic_buffer(state), 3); + ei_x_encode_long(&dynamic_buffer(state), (long)(ts->hour)); + ei_x_encode_long(&dynamic_buffer(state), (long)(ts->minute)); + ei_x_encode_long(&dynamic_buffer(state), (long)(ts->second)); + break; case SQL_C_CHAR: - ei_x_encode_string(&dynamic_buffer(state), ((char*)values)+j*column.type.len); - break; + if binary_strings(state) { + ei_x_encode_binary(&dynamic_buffer(state), + ((char*)values)+j*column.type.len, + (column.type.strlen_or_indptr_array[j])); + } + else { + ei_x_encode_string(&dynamic_buffer(state), + ((char*)values)+j*column.type.len); + } + break; + case SQL_C_WCHAR: + ei_x_encode_binary(&dynamic_buffer(state), + ((char*)values)+j*column.type.len, + (column.type.strlen_or_indptr_array[j])); + break; case SQL_C_SLONG: ei_x_encode_long(&dynamic_buffer(state), ((long*)values)[j]); break; @@ -1359,13 +1394,35 @@ static db_result_msg encode_row_count(SQLINTEGER num_of_rows, static void encode_column_dyn(db_column column, int column_nr, db_state *state) { + TIMESTAMP_STRUCT* ts; if (column.type.len == 0 || column.type.strlen_or_indptr == SQL_NULL_DATA) { ei_x_encode_atom(&dynamic_buffer(state), "null"); } 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: - ei_x_encode_string(&dynamic_buffer(state), column.buffer); + 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); + } + break; + case SQL_C_WCHAR: + ei_x_encode_binary(&dynamic_buffer(state), + column.buffer,column.type.strlen_or_indptr); break; case SQL_C_SLONG: ei_x_encode_long(&dynamic_buffer(state), @@ -1379,9 +1436,14 @@ static void encode_column_dyn(db_column column, int column_nr, ei_x_encode_atom(&dynamic_buffer(state), column.buffer[0]?"true":"false"); break; - case SQL_C_BINARY: + case SQL_C_BINARY: column = retrive_binary_data(column, column_nr, state); - ei_x_encode_string(&dynamic_buffer(state), (void *)column.buffer); + 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); + } break; default: ei_x_encode_atom(&dynamic_buffer(state), "error"); @@ -1404,6 +1466,16 @@ static void encode_data_type(SQLINTEGER sql_type, SQLINTEGER size, 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_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_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_atom(&dynamic_buffer(state), "sql_numeric"); @@ -1446,7 +1518,7 @@ static void encode_data_type(SQLINTEGER sql_type, SQLINTEGER size, ei_x_encode_atom(&dynamic_buffer(state), "SQL_TYPE_TIME"); break; case SQL_TYPE_TIMESTAMP: - ei_x_encode_atom(&dynamic_buffer(state), "SQL_TYPE_TIMESTAMP"); + ei_x_encode_atom(&dynamic_buffer(state), "sql_timestamp"); break; case SQL_BIGINT: ei_x_encode_atom(&dynamic_buffer(state), "SQL_BIGINT"); @@ -1492,67 +1564,96 @@ static void encode_data_type(SQLINTEGER sql_type, SQLINTEGER size, } } -static Boolean decode_params(byte *buffer, int *index, param_array **params, +static Boolean decode_params(db_state *state, byte *buffer, int *index, param_array **params, int i, int j) { int erl_type, size; long bin_size, l64; + long val; param_array* param; - + TIMESTAMP_STRUCT* ts; + ei_get_type(buffer, index, &erl_type, &size); param = &(*params)[i]; switch (param->type.c) { case SQL_C_CHAR: - if(erl_type != ERL_STRING_EXT) { - return FALSE; - } - - ei_decode_string(buffer, index, &(param->values.string[param->offset])); - param->offset += param->type.len; - param->type.strlen_or_indptr_array[j] = SQL_NTS; - break; - + if (binary_strings(state)) { + ei_decode_binary(buffer, index, + &(param->values.string[param->offset]), &bin_size); + param->offset += param->type.len; + param->type.strlen_or_indptr_array[j] = SQL_NTS; + } else { + if(erl_type != ERL_STRING_EXT) { + return FALSE; + } + ei_decode_string(buffer, index, &(param->values.string[param->offset])); + param->offset += param->type.len; + param->type.strlen_or_indptr_array[j] = SQL_NTS; + } + break; + case SQL_C_WCHAR: + ei_decode_binary(buffer, index, &(param->values.string[param->offset]), &bin_size); + param->offset += param->type.len; + param->type.strlen_or_indptr_array[j] = SQL_NTS; + break; + case SQL_C_TYPE_TIMESTAMP: + ts = (TIMESTAMP_STRUCT*) param->values.string; + ei_decode_tuple_header(buffer, index, &size); + ei_decode_long(buffer, index, &val); + ts[j].year = (SQLUSMALLINT)val; + ei_decode_long(buffer, index, &val); + ts[j].month = (SQLUSMALLINT)val; + ei_decode_long(buffer, index, &val); + ts[j].day = (SQLUSMALLINT)val; + ei_decode_long(buffer, index, &val); + ts[j].hour = (SQLUSMALLINT)val; + ei_decode_long(buffer, index, &val); + ts[j].minute = (SQLUSMALLINT)val; + ei_decode_long(buffer, index, &val); + ts[j].second = (SQLUSMALLINT)val; + ts[j].fraction = (SQLINTEGER)0; + break; case SQL_C_SLONG: - if(!((erl_type == ERL_SMALL_INTEGER_EXT) || - (erl_type == ERL_INTEGER_EXT) || - (erl_type == ERL_SMALL_BIG_EXT) || - (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 || l64<INT_MIN) { - return FALSE; - } + if(!((erl_type == ERL_SMALL_INTEGER_EXT) || + (erl_type == ERL_INTEGER_EXT) || + (erl_type == ERL_SMALL_BIG_EXT) || + (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 || l64<INT_MIN) { + return FALSE; + } param->values.integer[j]=(SQLINTEGER)l64; break; - + case SQL_C_DOUBLE: - if((erl_type != ERL_FLOAT_EXT)) { - return FALSE; - } - ei_decode_double(buffer, index, &(param->values.floating[j])); - break; - + if((erl_type != ERL_FLOAT_EXT)) { + return FALSE; + } + ei_decode_double(buffer, index, &(param->values.floating[j])); + break; + case SQL_C_BIT: if((erl_type != ERL_ATOM_EXT)) { - return FALSE; + return FALSE; } ei_decode_boolean(buffer, index, &(param->values.bool[j])); break; - + default: - return FALSE; + return FALSE; } - + return TRUE; } @@ -1626,74 +1727,48 @@ static byte * receive_erlang_port_msg(void) } /* ------------- Socket communication functions --------------------------*/ -#define USE_IPV4 -#ifdef UNIX -#define SOCKET int -#endif -#if defined WIN32 || defined USE_IPV4 -/* Currently only an old windows compiler is supported so we do not have ipv6 - capabilities */ +#if defined(WIN32) static SOCKET connect_to_erlang(const char *port) -{ - SOCKET sock; - struct sockaddr_in sin; - - sock = socket(AF_INET, SOCK_STREAM, 0); - - memset(&sin, 0, sizeof(sin)); - sin.sin_port = htons ((unsigned short)atoi(port)); - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = inet_addr("127.0.0.1"); - - if (connect(sock, (struct sockaddr*)&sin, sizeof(sin)) != 0) { - close_socket(sock); - DO_EXIT(EXIT_SOCKET_CONNECT); - } - return sock; -} #elif defined(UNIX) static int connect_to_erlang(const char *port) +#endif { - int sock; - - struct addrinfo hints; - struct addrinfo *erlang_ai, *first; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = PF_UNSPEC; /* PF_INET or PF_INET6 */ - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - - if (getaddrinfo("localhost", port, &hints, &first) != 0) { - DO_EXIT(EXIT_FAILURE); - } +#if defined(WIN32) + SOCKET sock; +#elif defined(UNIX) + int sock; +#endif + struct sockaddr_in sin; + +#if defined(HAVE_STRUCT_SOCKADDR_IN6_SIN6_ADDR) && defined(AF_INET6) + struct sockaddr_in6 sin6; + + sock = socket(AF_INET6, SOCK_STREAM, 0); + + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_port = htons ((unsigned short)atoi(port)); + sin6.sin6_family = AF_INET6; + sin6.sin6_addr = in6addr_loopback; - for (erlang_ai = first; erlang_ai; erlang_ai = erlang_ai->ai_next) { + if (connect(sock, (struct sockaddr*)&sin6, sizeof(sin6)) == 0) { + return sock; + } + close_socket(sock); +#endif + sock = socket(AF_INET, SOCK_STREAM, 0); + + memset(&sin, 0, sizeof(sin)); + sin.sin_port = htons ((unsigned short)atoi(port)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - sock = socket(erlang_ai->ai_family, erlang_ai->ai_socktype, - erlang_ai->ai_protocol); - if (sock < 0) - continue; - if (connect(sock, (struct sockaddr*)erlang_ai->ai_addr, - erlang_ai->ai_addrlen) < 0) { - close(sock); - sock = -1; - continue; - } else { - break; + if (connect(sock, (struct sockaddr*)&sin, sizeof(sin)) != 0) { + close_socket(sock); + DO_EXIT(EXIT_SOCKET_CONNECT); } - } - freeaddrinfo(first); - - if (sock < 0){ - close_socket(sock); - DO_EXIT(EXIT_SOCKET_CONNECT); - } - - return sock; + return sock; } -#endif #ifdef WIN32 static void close_socket(SOCKET socket) @@ -1932,7 +2007,7 @@ static void init_driver(int erl_auto_commit_mode, int erl_trace_driver, db_state *state) { - int auto_commit_mode, trace_driver, use_srollable_cursors; + int auto_commit_mode, trace_driver; if(erl_auto_commit_mode == ON) { auto_commit_mode = SQL_AUTOCOMMIT_ON; @@ -1958,18 +2033,19 @@ static void init_driver(int erl_auto_commit_mode, int erl_trace_driver, environment_handle(state), &connection_handle(state)))) DO_EXIT(EXIT_ALLOC); + /* By default Erlang handles all timeouts */ if(!sql_success(SQLSetConnectAttr(connection_handle(state), SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER)TIME_OUT, 0))) - DO_EXIT(EXIT_CONNECTION); + DO_EXIT(EXIT_CONNECTION); if(!sql_success(SQLSetConnectAttr(connection_handle(state), SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)auto_commit_mode, 0))) - DO_EXIT(EXIT_CONNECTION); + DO_EXIT(EXIT_CONNECTION); if(!sql_success(SQLSetConnectAttr(connection_handle(state), SQL_ATTR_TRACE, (SQLPOINTER)trace_driver, 0))) - DO_EXIT(EXIT_CONNECTION); + DO_EXIT(EXIT_CONNECTION); } static void init_param_column(param_array *params, byte *buffer, int *index, @@ -2053,6 +2129,32 @@ static void init_param_column(param_array *params, byte *buffer, int *index, sizeof(byte)* params->type.len); break; + case USER_WCHAR: + case USER_WVARCHAR: + if(user_type == USER_WCHAR) { + params->type.sql = SQL_WCHAR; + } else { + params->type.sql = SQL_WVARCHAR; + } + ei_decode_long(buffer, index, &length); + /* Max string length + string terminator */ + params->type.len = (length+1)*sizeof(SQLWCHAR); + params->type.c = SQL_C_WCHAR; + params->type.col_size = (SQLUINTEGER)length; + params->type.strlen_or_indptr_array = + (SQLLEN*)safe_malloc(num_param_values * sizeof(SQLINTEGER)); + params->values.string = + (byte *)safe_malloc(num_param_values * sizeof(byte) * params->type.len); + + break; + case USER_TIMESTAMP: + params->type.sql = SQL_TYPE_TIMESTAMP; + params->type.len = sizeof(TIMESTAMP_STRUCT); + params->type.c = SQL_C_TYPE_TIMESTAMP; + params->type.col_size = (SQLUINTEGER)COL_SQL_TIMESTAMP; + params->values.string = + (byte *)safe_malloc(num_param_values * params->type.len); + break; case USER_FLOAT: params->type.sql = SQL_FLOAT; params->type.c = SQL_C_DOUBLE; @@ -2190,11 +2292,17 @@ static db_result_msg map_sql_2_c_column(db_column* column) case SQL_LONGVARCHAR: case SQL_VARBINARY: case SQL_LONGVARBINARY: - column -> type.len = (column -> type.col_size) + - /* Make place for NULL termination */ - sizeof(byte); - column -> type.c = SQL_C_CHAR; - column -> type.strlen_or_indptr = SQL_NTS; + column -> type.len = (column -> type.col_size) + + /* Make place for NULL termination */ + sizeof(byte); + column -> type.c = SQL_C_CHAR; + column -> type.strlen_or_indptr = SQL_NTS; + break; + case SQL_WCHAR: + case SQL_WVARCHAR: + column -> type.len = (column -> type.col_size + 1)*sizeof(SQLWCHAR); + column -> type.c = SQL_C_WCHAR; + column -> type.strlen_or_indptr = SQL_NTS; break; case SQL_NUMERIC: case SQL_DECIMAL: @@ -2218,12 +2326,16 @@ static db_result_msg map_sql_2_c_column(db_column* column) break; case SQL_TYPE_DATE: case SQL_TYPE_TIME: - case SQL_TYPE_TIMESTAMP: column -> type.len = (column -> type.col_size) + sizeof(byte); column -> type.c = SQL_C_CHAR; column -> type.strlen_or_indptr = SQL_NTS; break; + case SQL_TYPE_TIMESTAMP: + column -> type.len = sizeof(TIMESTAMP_STRUCT); + column -> type.c = SQL_C_TYPE_TIMESTAMP; + column -> type.strlen_or_indptr = (SQLINTEGER)NULL; + break; case SQL_BIGINT: column -> type.len = DEC_NUM_LENGTH; column -> type.c = SQL_C_CHAR; @@ -2280,7 +2392,7 @@ static param_array * bind_parameter_arrays(byte *buffer, int *index, } for (j = 0; j < num_param_values; j++) { - if(!decode_params(buffer, index, ¶ms, i, j)) { + if(!decode_params(state, buffer, index, ¶ms, i, j)) { /* An input parameter was not of the expected type */ free_params(¶ms, i); return params; @@ -2309,7 +2421,9 @@ static void * retrive_param_values(param_array *Param) { switch(Param->type.c) { case SQL_C_CHAR: - return (void *)Param->values.string; + case SQL_C_WCHAR: + case SQL_C_TYPE_TIMESTAMP: + return (void *)Param->values.string; case SQL_C_SLONG: return (void *)Param->values.integer; case SQL_C_DOUBLE: |