2828 database using the Microsoft ODBC API. The c-process is implemented
2929 using two threads the supervisor thread and the database handler thread.
3030 If the database thread should hang erlang can close the c-process down
31- by sendig a shutdown request to the supervisor thread.
31+ by sending a shutdown request to the supervisor thread.
3232
3333 Erlang will start this c-process as a port-program and send information
3434 regarding inet-port numbers through the erlang-port.
6262 they are converted to string values.
6363
6464 [?OPEN_CONNECTION, C_AutoCommitMode, C_TraceDriver, C_SrollableCursors,
65- C_TupelRow, BinaryStrings, ConnectionStr]
65+ C_TupelRow, BinaryStrings, ExtendedErrors, 255, 1,
66+ MaxLongColumnSizeOctets (4 bytes, big-endian, 0 = default), ConnectionStr]
67+ Legacy layout: same first six bytes then ConnectionStr; optional tuning via
68+ ERL_ODBC_MAX_LONG_COLUMN_SIZE.
6669 [?CLOSE_CONNECTION]
6770 [?COMMIT_TRANSACTION, CommitMode]
6871 [?QUERY, SQLQuery]
8083 C_SrollableCursors - ?ON | ?OFF
8184 C_TupelRow - - ?ON | ?OFF
8285 BinaryStrings - ?ON | ?OFF
86+ ExtendedErrors - ?ON | ?OFF
8387 ConnectionStr - String
8488 CommitMode - ?COMMIT | ?ROLLBACK
8589 SQLQuery - String
105109#include <stdlib.h>
106110#include <string.h>
107111#include <stdio.h>
112+ #include <errno.h>
108113
109114#ifdef UNIX
110115#include <unistd.h>
@@ -141,11 +146,12 @@ DWORD WINAPI database_handler(const char *port);
141146#else
142147void database_handler (const char * port );
143148#endif
144- static db_result_msg handle_db_request (byte * reqstring , db_state * state );
149+ static db_result_msg handle_db_request (byte * reqstring , size_t msg_len ,
150+ db_state * state );
145151static void supervise (const char * port );
146152/* ----------------- ODBC functions --------------------------------------*/
147153
148- static db_result_msg db_connect (byte * connStrIn , db_state * state );
154+ static db_result_msg db_connect (byte * args , size_t args_len , db_state * state );
149155static db_result_msg db_close_connection (db_state * state );
150156static db_result_msg db_end_tran (byte compleationtype , db_state * state );
151157static db_result_msg db_query (byte * sql , db_state * state );
@@ -193,7 +199,7 @@ static byte * receive_erlang_port_msg(void);
193199#ifdef WIN32
194200static SOCKET connect_to_erlang (const char * port );
195201static void send_msg (db_result_msg * msg , SOCKET socket );
196- static byte * receive_msg (SOCKET socket );
202+ static byte * receive_msg (SOCKET socket , size_t * msg_len_out );
197203static Boolean receive_msg_part (SOCKET socket ,
198204 byte * buffer , size_t msg_len );
199205static Boolean send_msg_part (SOCKET socket , byte * buffer , size_t msg_len );
@@ -202,7 +208,7 @@ static void init_winsock(void);
202208#elif defined(UNIX )
203209static int connect_to_erlang (const char * port );
204210static void send_msg (db_result_msg * msg , int socket );
205- static byte * receive_msg (int socket );
211+ static byte * receive_msg (int socket , size_t * msg_len_out );
206212static Boolean receive_msg_part (int socket , byte * buffer , size_t msg_len );
207213static Boolean send_msg_part (int socket , byte * buffer , size_t msg_len );
208214static void close_socket (int socket );
@@ -333,7 +339,7 @@ void supervise(const char *port) {
333339#endif
334340
335341 socket = connect_to_erlang (port );
336- msg = receive_msg (socket );
342+ msg = receive_msg (socket , NULL );
337343
338344 if (msg [0 ] == SHUTDOWN ) {
339345 reason = EXIT_SUCCESS ;
@@ -357,7 +363,7 @@ DWORD WINAPI database_handler(const char *port)
357363 byte * request_buffer = NULL ;
358364 db_state state =
359365 {NULL , NULL , NULL , NULL , 0 , {NULL , 0 , 0 },
360- FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE};
366+ FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, ( SQLULEN ) 0 };
361367 byte request_id ;
362368#ifdef WIN32
363369 SOCKET socket ;
@@ -369,23 +375,26 @@ DWORD WINAPI database_handler(const char *port)
369375 socket = connect_to_erlang (port );
370376
371377 do {
372- request_buffer = receive_msg (socket );
378+ {
379+ size_t req_len ;
380+ request_buffer = receive_msg (socket , & req_len );
373381
374- request_id = request_buffer [0 ];
375- msg = handle_db_request (request_buffer , & state );
382+ request_id = request_buffer [0 ];
383+ msg = handle_db_request (request_buffer , req_len , & state );
384+ }
385+
386+ send_msg (& msg , socket ); /* Send answer to erlang */
387+
388+ if (msg .dyn_alloc ) {
389+ ei_x_free (& (state .dynamic_buffer ));
390+ } else {
391+ free (msg .buffer );
392+ msg .buffer = NULL ;
393+ }
394+
395+ free (request_buffer );
396+ request_buffer = NULL ;
376397
377- send_msg (& msg , socket ); /* Send answer to erlang */
378-
379- if (msg .dyn_alloc ) {
380- ei_x_free (& (state .dynamic_buffer ));
381- } else {
382- free (msg .buffer );
383- msg .buffer = NULL ;
384- }
385-
386- free (request_buffer );
387- request_buffer = NULL ;
388-
389398 } while (request_id != CLOSE_CONNECTION );
390399
391400 shutdown (socket , 2 );
@@ -400,47 +409,78 @@ DWORD WINAPI database_handler(const char *port)
400409/* Description: Calls the appropriate function to handle the database
401410 request received from the erlang-process. Returns a message to send back
402411 to erlang. */
403- static db_result_msg handle_db_request (byte * reqstring , db_state * state )
412+ static db_result_msg handle_db_request (byte * reqstring , size_t msg_len ,
413+ db_state * state )
404414{
405415 byte * args ;
406416 byte request_id ;
417+ size_t args_len ;
407418
408419 /* First byte is an index that identifies the requested command the
409420 rest is the argument string. */
410421 request_id = reqstring [0 ];
411422 args = reqstring + sizeof (byte );
423+ args_len = (msg_len > sizeof (byte )) ? (msg_len - sizeof (byte )) : 0 ;
412424
413425 switch (request_id ) {
414426 case OPEN_CONNECTION :
415- return db_connect (args , state );
427+ return db_connect (args , args_len , state );
416428 case CLOSE_CONNECTION :
417- return db_close_connection (state );
429+ return db_close_connection (state );
418430 case COMMIT_TRANSACTION :
419- if (args [0 ] == COMMIT ) {
420- return db_end_tran ((byte )SQL_COMMIT , state );
421- } else { /* args[0] == ROLLBACK */
422- return db_end_tran ((byte )SQL_ROLLBACK , state );
423- }
431+ if (args [0 ] == COMMIT ) {
432+ return db_end_tran ((byte )SQL_COMMIT , state );
433+ } else { /* args[0] == ROLLBACK */
434+ return db_end_tran ((byte )SQL_ROLLBACK , state );
435+ }
424436 case QUERY :
425- return db_query (args , state );
437+ return db_query (args , state );
426438 case SELECT_COUNT :
427- return db_select_count (args , state );
439+ return db_select_count (args , state );
428440 case SELECT :
429- return db_select (args , state );
441+ return db_select (args , state );
430442 case PARAM_QUERY :
431- return db_param_query (args , state );
443+ return db_param_query (args , state );
432444 case DESCRIBE :
433- return db_describe_table (args , state );
445+ return db_describe_table (args , state );
434446 default :
435- DO_EXIT (EXIT_FAILURE ); /* Should not happen */
436- }
447+ DO_EXIT (EXIT_FAILURE ); /* Should not happen */
448+ }
437449}
438450
439451/* ----------------- ODBC-functions ----------------------------------*/
452+
453+ static SQLULEN
454+ clamp_max_long_col_size (SQLULEN value )
455+ {
456+ if (value == (SQLULEN )0 )
457+ return (SQLULEN )DEFAULT_MAX_LONG_COL_SIZE ;
458+ if (value > (SQLULEN )HARD_MAX_LONG_COL_SIZE )
459+ return (SQLULEN )HARD_MAX_LONG_COL_SIZE ;
460+ return value ;
461+ }
462+
463+ static SQLULEN
464+ max_long_col_size_from_env (void )
465+ {
466+ const char * e = getenv ("ERL_ODBC_MAX_LONG_COLUMN_SIZE" );
467+ unsigned long v ;
468+ char * end = NULL ;
469+
470+ if (e == NULL || e [0 ] == '\0' )
471+ return (SQLULEN )0 ;
472+ errno = 0 ;
473+ v = strtoul (e , & end , 10 );
474+ if (end == e || * end != '\0' )
475+ return (SQLULEN )0 ;
476+ if (errno == ERANGE )
477+ return (SQLULEN )0 ;
478+ return (SQLULEN )v ;
479+ }
440480
441481/* Description: Tries to open a connection to the database using
442482 <connStrIn>, returns a message indicating the outcome. */
443- static db_result_msg db_connect (byte * args , db_state * state )
483+ static db_result_msg db_connect (byte * args , size_t args_len , db_state * state )
444484{
445485 /*
446486 * Danil Onishchenko aka RubberCthulhu, alevandal@gmail.com. 2013.01.09.
@@ -465,14 +505,32 @@ static db_result_msg db_connect(byte *args, db_state *state)
465505 int erl_auto_commit_mode , erl_trace_driver ,
466506 use_srollable_cursors , tuple_row_state , binary_strings ,
467507 extended_errors ;
508+ SQLULEN long_col_cfg ;
468509
510+ if (args_len < 6 )
511+ DO_EXIT (EXIT_FAILURE );
512+
469513 erl_auto_commit_mode = args [0 ];
470514 erl_trace_driver = args [1 ];
471515 use_srollable_cursors = args [2 ];
472516 tuple_row_state = args [3 ];
473517 binary_strings = args [4 ];
474518 extended_errors = args [5 ];
475- connStrIn = args + 6 * sizeof (byte );
519+
520+ if (args_len >= 13 && args [6 ] == OPEN_CONNECTION_PROTO_V1_A &&
521+ args [7 ] == OPEN_CONNECTION_PROTO_V1_B ) {
522+ long_col_cfg = ((SQLULEN )args [8 ] << 24 ) | ((SQLULEN )args [9 ] << 16 ) |
523+ ((SQLULEN )args [10 ] << 8 ) | (SQLULEN )args [11 ];
524+ connStrIn = args + 12 ;
525+ max_long_column_size (state ) = clamp_max_long_col_size (long_col_cfg );
526+ } else {
527+ connStrIn = args + 6 ;
528+ max_long_column_size (state ) =
529+ clamp_max_long_col_size (max_long_col_size_from_env ());
530+ }
531+
532+ if ((size_t )(connStrIn - args ) >= args_len )
533+ DO_EXIT (EXIT_FAILURE );
476534
477535 if (tuple_row_state == ON ) {
478536 tuple_row (state ) = TRUE;
@@ -1325,9 +1383,9 @@ static db_result_msg encode_column_name_list(SQLSMALLINT num_of_columns,
13251383 SQLULEN size ;
13261384
13271385 msg = encode_empty_message ();
1328-
1386+
13291387 ei_x_encode_list_header (& dynamic_buffer (state ), num_of_columns );
1330-
1388+
13311389 for (i = 0 ; i < num_of_columns ; ++ i ) {
13321390
13331391 if (!sql_success (SQLDescribeCol (statement_handle (state ),
@@ -1337,32 +1395,37 @@ static db_result_msg encode_column_name_list(SQLSMALLINT num_of_columns,
13371395 & nullable )))
13381396 DO_EXIT (EXIT_DESC );
13391397
1340- if (size == 0 && (sql_type == SQL_LONGVARCHAR || sql_type == SQL_LONGVARBINARY || sql_type == SQL_WLONGVARCHAR ))
1341- size = MAXCOLSIZE ;
1342-
1398+ if (sql_type == SQL_LONGVARCHAR || sql_type == SQL_LONGVARBINARY || sql_type == SQL_WLONGVARCHAR ) {
1399+ if (size == 0 ) {
1400+ size = max_long_column_size (state );
1401+ } else if (size > max_long_column_size (state )) {
1402+ size = max_long_column_size (state );
1403+ }
1404+ }
1405+
13431406 (columns (state )[i ]).type .decimal_digits = dec_digits ;
13441407 (columns (state )[i ]).type .sql = sql_type ;
13451408 (columns (state )[i ]).type .col_size = size ;
1346-
1409+
13471410 msg = map_sql_2_c_column (& columns (state )[i ], state );
13481411 if (msg .length > 0 ) {
13491412 return msg ; /* An error has occurred */
13501413 } else {
13511414 if (columns (state )[i ].type .len > 0 ) {
13521415 columns (state )[i ].buffer =
13531416 (char * )safe_malloc (columns (state )[i ].type .len );
1354-
1417+
13551418 if (columns (state )[i ].type .c == SQL_C_BINARY ) {
13561419 /* retrieved later by retrive_binary_data */
13571420 } else {
13581421 if (!sql_success (
1359- SQLBindCol
1360- (statement_handle (state ),
1361- (SQLSMALLINT )(i + 1 ),
1362- columns (state )[i ].type .c ,
1363- columns (state )[i ].buffer ,
1364- columns (state )[i ].type .len ,
1365- & columns (state )[i ].type .strlen_or_indptr )))
1422+ SQLBindCol
1423+ (statement_handle (state ),
1424+ (SQLSMALLINT )(i + 1 ),
1425+ columns (state )[i ].type .c ,
1426+ columns (state )[i ].buffer ,
1427+ columns (state )[i ].type .len ,
1428+ & columns (state )[i ].type .strlen_or_indptr )))
13661429 DO_EXIT (EXIT_BIND );
13671430 }
13681431 ei_x_encode_string_len (& dynamic_buffer (state ),
@@ -1395,9 +1458,9 @@ static db_result_msg encode_value_list(SQLSMALLINT num_of_columns,
13951458 result = SQLFetch (statement_handle (state ));
13961459
13971460 if (result == SQL_NO_DATA ) /* Reached end of result set */
1398- {
1399- break ;
1400- }
1461+ {
1462+ break ;
1463+ }
14011464
14021465 ei_x_encode_list_header (& dynamic_buffer (state ), 1 );
14031466
@@ -1956,9 +2019,9 @@ static void close_socket(int socket)
19562019#endif
19572020
19582021#ifdef WIN32
1959- static byte * receive_msg (SOCKET socket )
2022+ static byte * receive_msg (SOCKET socket , size_t * msg_len_out )
19602023#elif defined(UNIX )
1961- static byte * receive_msg (int socket )
2024+ static byte * receive_msg (int socket , size_t * msg_len_out )
19622025#endif
19632026{
19642027 byte lengthstr [LENGTH_INDICATOR_SIZE ];
@@ -1979,6 +2042,9 @@ static byte * receive_msg(int socket)
19792042 DO_EXIT (EXIT_SOCKET_RECV_BODY );
19802043 }
19812044
2045+ if (msg_len_out != NULL )
2046+ * msg_len_out = msg_len ;
2047+
19822048 return buffer ;
19832049}
19842050
0 commit comments