diff --git a/scsh/odbc/odbc.c b/scsh/odbc/odbc.c index d2692c3..05c4c82 100644 --- a/scsh/odbc/odbc.c +++ b/scsh/odbc/odbc.c @@ -162,18 +162,12 @@ s48_value odbc_sql_connect(s48_value connection_handle, SQLHDBC ch; SQLCHAR *dsn, *user, *auth; SQLRETURN retval; - int dsn_len, user_len, auth_len; ODBC_DEBUG_PRINTF("odbc_sql_connect\n"); dsn = (SQLCHAR *) s48_extract_string(ds_name); user = (SQLCHAR *) s48_extract_string(user_name); auth = (SQLCHAR *) s48_extract_string(authentication); - - dsn_len = S48_STRING_LENGTH(ds_name); - user_len = S48_STRING_LENGTH(user_name); - auth_len = S48_STRING_LENGTH(authentication); - ch = (SQLHDBC) s48_extract_integer(connection_handle); retval = SQLConnect(ch, @@ -218,10 +212,9 @@ s48_value odbc_sql_data_sources(s48_value env_handle) SQLHENV eh; SQLUSMALLINT dir; SQLRETURN retval; - SQLCHAR server_name[SQL_MAX_DSN_LENGTH+1]; - SQLSMALLINT server_name_len; - SQLCHAR driver_descr[ODBC_MAX_DRIVER_NAME_LEN]; - SQLSMALLINT driver_descr_len; + SQLCHAR *server_name, *driver_descr; + SQLSMALLINT server_name_len, driver_descr_len; + SQLSMALLINT server_name_needed, driver_descr_needed; s48_value result = S48_UNSPECIFIC; int first, more_items; @@ -231,20 +224,59 @@ s48_value odbc_sql_data_sources(s48_value env_handle) eh = (SQLHENV) s48_extract_integer(env_handle); ODBC_DEBUG_PRINTF("odbc_sql_data_sources\n"); + + server_name_len = driver_descr_len = ODBC_RETVAL_BUFFER_INITIAL_SIZE; + server_name = (SQLCHAR *) calloc(server_name_len, sizeof(SQLCHAR)); + driver_descr = (SQLCHAR *) calloc(driver_descr_len, sizeof(SQLCHAR)); + + if ((server_name == NULL) || (driver_descr == NULL)) + { + ODBC_RAISE_EXCEPTION("Could not allocate memory for return values"); + return S48_UNSPECIFIC; + } + + redo_loop: + ODBC_DEBUG_PRINTF("redo_loop\n"); result = S48_NULL; first = more_items = 1; - + while (more_items) { - + printf("Calling SQLDataSources() server_name_len: %d driver_descr_len: %d\n", + sizeof(SQLCHAR)*server_name_len, + sizeof(SQLCHAR)*driver_descr_len); retval = SQLDataSources(eh, (first ? SQL_FETCH_FIRST : SQL_FETCH_NEXT), - server_name, SQL_MAX_DSN_LENGTH+1, &server_name_len, - driver_descr, ODBC_MAX_DRIVER_NAME_LEN, &driver_descr_len); - /* FIXME */ - if (driver_descr_len > ODBC_MAX_DRIVER_NAME_LEN - 1) + server_name, sizeof(SQLCHAR)*server_name_len, &server_name_needed, + driver_descr, sizeof(SQLCHAR)*driver_descr_len, &driver_descr_needed); + + if (ODBC_SUCCESS(retval)) { - ODBC_RAISE_EXCEPTION("SQLDataSources(): string driver_descr to long"); - return S48_UNSPECIFIC; + int redo_call = 0; + + if (server_name_needed > server_name_len) + { + ODBC_DEBUG_PRINTF("realloc() server_name\n"); + printf("needed: %d\n", server_name_needed); + server_name_len = server_name_needed+1; + server_name = (SQLCHAR *) realloc(server_name, (size_t) server_name_len); + redo_call = 1; + } + if (driver_descr_needed > driver_descr_len) + { + ODBC_DEBUG_PRINTF("realloc() driver_desrc\n"); + printf("needed: %d\n", driver_descr_needed); + driver_descr_len = driver_descr_needed+1; + driver_descr = (SQLCHAR *) realloc(driver_descr, (size_t) driver_descr_len); + redo_call = 1; + } + + if ((server_name == NULL) || (driver_descr == NULL)) + { + ODBC_RAISE_EXCEPTION("Could not allocate memory for return values"); + return S48_UNSPECIFIC; + } + + if (redo_call) goto redo_loop; } first = 0; @@ -253,6 +285,7 @@ s48_value odbc_sql_data_sources(s48_value env_handle) { case SQL_SUCCESS: case SQL_SUCCESS_WITH_INFO: { + printf("(cons '%s' '%s')\n", server_name, driver_descr); result = s48_cons(s48_cons(s48_enter_string(server_name), s48_enter_string(driver_descr)), result); @@ -281,6 +314,9 @@ s48_value odbc_sql_data_sources(s48_value env_handle) } } + free(server_name); + free(driver_descr); + S48_GC_UNPROTECT(); return result; } @@ -401,21 +437,35 @@ s48_value odbc_sql_get_info_string(s48_value conn_handle, s48_value info_key) SQLHDBC ch; SQLUSMALLINT ik; SQLRETURN retval; - SQLCHAR info[ODBC_GET_INFO_MAX_LEN]; - SQLSMALLINT buffer_size; + SQLCHAR *buffer = NULL; + SQLSMALLINT buffer_needed, buffer_len; ODBC_DEBUG_PRINTF("odbc_sql_get_info_string\n"); ch = (SQLHDBC) s48_extract_integer(conn_handle); ik = (SQLUSMALLINT) s48_extract_integer(info_key); + buffer_len = ODBC_RETVAL_BUFFER_INITIAL_SIZE; - retval = SQLGetInfo(ch, ik, &info, ODBC_GET_INFO_MAX_LEN, &buffer_size); + for (;;) + { + odbc_sql_alloc((void **) &buffer, buffer_len, sizeof(SQLCHAR)); + + printf("SQLGetInfo(): buffer_len %d\n", buffer_len); + retval = SQLGetInfo(ch, ik, buffer, buffer_len, &buffer_needed); + + if (ODBC_SUCCESS(retval) && (buffer_needed > buffer_len)) + buffer_len = buffer_needed+1; + else + break; + } switch (retval) { case SQL_SUCCESS: case SQL_SUCCESS_WITH_INFO: { - return s48_enter_string(info); + s48_value res = s48_enter_string(buffer); + free(buffer); + return res; } case SQL_ERROR: { @@ -612,21 +662,32 @@ s48_value odbc_sql_get_connect_attr_string(s48_value conn_handle, SQLHDBC ch; SQLINTEGER attr; - SQLCHAR buffer[ODBC_GET_CONNECT_ATTR_MAX_LEN]; - SQLINTEGER buffer_size; + SQLCHAR *buffer = NULL; + SQLINTEGER buffer_needed, buffer_len; SQLRETURN retval; ODBC_DEBUG_PRINTF("odbc_sql_get_connect_attr_string\n"); + + buffer_len = ODBC_RETVAL_BUFFER_INITIAL_SIZE; - retval = SQLGetConnectAttr(ch, attr, - buffer, ODBC_GET_CONNECT_ATTR_MAX_LEN-1, - &buffer_size); + for (;;) { + odbc_sql_alloc((void **) &buffer, buffer_len, sizeof(SQLCHAR)); + + retval = SQLGetConnectAttr(ch, attr, buffer, buffer_len, &buffer_needed); + + if (ODBC_SUCCESS(retval) && (buffer_needed > buffer_len)) + buffer_needed = buffer_len; + else + break; + } switch (retval) { case SQL_SUCCESS: case SQL_SUCCESS_WITH_INFO: { - return s48_enter_string(buffer); + s48_value res = s48_enter_string(buffer); + free(buffer); + return res; } case SQL_ERROR: { @@ -816,7 +877,6 @@ s48_value odbc_sql_set_stmt_attr_string(s48_value stmt_handle, s48_value attribute, s48_value value) { - SQLHSTMT sh; SQLINTEGER attr; SQLCHAR *val; @@ -828,8 +888,7 @@ s48_value odbc_sql_set_stmt_attr_string(s48_value stmt_handle, attr = (SQLINTEGER) s48_extract_integer(attribute); val = (SQLCHAR *) s48_extract_string(value); - retval = SQLSetStmtAttr(sh, attr, - val, S48_STRING_LENGTH(value)); + retval = SQLSetStmtAttr(sh, attr, val, S48_STRING_LENGTH(value)); switch (retval) { @@ -855,10 +914,8 @@ s48_value odbc_sql_set_stmt_attr_string(s48_value stmt_handle, } } -s48_value odbc_sql_get_stmt_attr_int(s48_value stmt_handle, - s48_value attribute) +s48_value odbc_sql_get_stmt_attr_int(s48_value stmt_handle, s48_value attribute) { - SQLHSTMT sh; SQLINTEGER attr, val, buf_size; SQLRETURN retval; @@ -899,20 +956,32 @@ s48_value odbc_sql_get_stmt_attr_string(s48_value stmt_handle, { SQLHSTMT sh; - SQLINTEGER attr, buf_size; - SQLCHAR buf[ODBC_GET_STMT_ATTR_MAX_LEN]; + SQLINTEGER attr, buffer_len, buffer_needed; + SQLCHAR *buffer = NULL; SQLRETURN retval; ODBC_DEBUG_PRINTF("odbc_sql_get_stmt_attr_string\n"); - retval = SQLGetStmtAttr(sh, attr, &buf, - ODBC_GET_STMT_ATTR_MAX_LEN-1, &buf_size); - + buffer_len = ODBC_RETVAL_BUFFER_INITIAL_SIZE; + + for (;;) + { + odbc_sql_alloc((void **) &buffer, buffer_len, sizeof(SQLCHAR)); + retval = SQLGetStmtAttr(sh, attr, &buffer, buffer_len, &buffer_needed); + + if (ODBC_SUCCESS(retval) && (buffer_needed > buffer_len)) + buffer_len = buffer_needed+1; + else + break; + } + switch (retval) { case SQL_SUCCESS: case SQL_SUCCESS_WITH_INFO: { - return s48_enter_string(buf); + s48_value res = s48_enter_string(buffer); + free(buffer); + return res; } case SQL_ERROR: { @@ -1007,8 +1076,8 @@ s48_value odbc_sql_get_desc_field_string(s48_value desc_handle, s48_value rec_nu { SQLHDESC dh; SQLSMALLINT rn, fi; - SQLCHAR value[ODBC_MAX_GET_DESC_FIELD_STR_LEN]; - SQLINTEGER buffer_len; + SQLCHAR *buffer = NULL; + SQLINTEGER buffer_len, buffer_needed; SQLRETURN retval; ODBC_DEBUG_PRINTF("odbc_sql_get_desc_field_string\n"); @@ -1017,15 +1086,26 @@ s48_value odbc_sql_get_desc_field_string(s48_value desc_handle, s48_value rec_nu rn = (SQLSMALLINT) s48_extract_integer(rec_number); fi = (SQLSMALLINT) s48_extract_integer(field_id); - retval = SQLGetDescField(dh, rn, fi, (SQLPOINTER) value, - (ODBC_MAX_GET_DESC_FIELD_STR_LEN - 1), - &buffer_len); + buffer_len = ODBC_RETVAL_BUFFER_INITIAL_SIZE; + for (;;) + { + odbc_sql_alloc((void **) &buffer, buffer_len, sizeof(SQLCHAR)); + retval = SQLGetDescField(dh, rn, fi, (SQLPOINTER) buffer, + buffer_len, &buffer_needed); + + if (ODBC_SUCCESS(retval) && (buffer_needed > buffer_len)) + buffer_len = buffer_needed+1; + else + break; + } switch (retval) { case SQL_SUCCESS: case SQL_SUCCESS_WITH_INFO: { - return s48_enter_string(value); + s48_value res = s48_enter_string(buffer); + free(buffer); + return res; } case SQL_NO_DATA: { @@ -1344,20 +1424,32 @@ s48_value odbc_sql_get_cursor_name(s48_value stmt_handle) { SQLHSTMT sh; SQLRETURN retval; - SQLCHAR cursorname[ODBC_MAX_CURSOR_NAME_STR_LEN]; - SQLSMALLINT buffer_len; + SQLCHAR *buffer = NULL; + SQLSMALLINT buffer_len, buffer_needed; ODBC_DEBUG_PRINTF("odbc_sql_get_cursor_name\n"); sh = (SQLHSTMT) s48_extract_integer(stmt_handle); - retval = SQLGetCursorName(sh, cursorname, ODBC_MAX_CURSOR_NAME_STR_LEN - 1, &buffer_len); + buffer_len = ODBC_RETVAL_BUFFER_INITIAL_SIZE; + for (;;) + { + odbc_sql_alloc((void **) &buffer, buffer_len, sizeof(SQLCHAR)); + retval = SQLGetCursorName(sh, buffer, buffer_len, &buffer_needed); + + if (ODBC_SUCCESS(retval) && (buffer_needed > buffer_len)) + buffer_len = buffer_needed+1; + else + break; + } switch (retval) { case SQL_SUCCESS: case SQL_SUCCESS_WITH_INFO: { - return s48_enter_string(cursorname); + s48_value res = s48_enter_string(buffer); + free(buffer); + return s48_enter_string(buffer); } case SQL_ERROR: { @@ -1514,24 +1606,35 @@ s48_value odbc_sql_execute_direct(s48_value stmt_handle, s48_value odbc_sql_native_sql(s48_value conn_handle, s48_value stmt_txt) { SQLHDBC ch; - SQLCHAR *stmt_in, stmt_out[ODBC_MAX_NATIVE_SQL_STR_LEN]; - SQLINTEGER buffer_len; + SQLCHAR *stmt_in, *stmt_out = NULL; + SQLINTEGER buffer_len, buffer_needed; SQLRETURN retval; ch = (SQLHDBC) s48_extract_integer(conn_handle); stmt_in = (SQLCHAR *) s48_extract_string(stmt_txt); ODBC_DEBUG_PRINTF("odbc_sql_native_sql\n"); + + buffer_len = ODBC_RETVAL_BUFFER_INITIAL_SIZE; + for (;;) + { + odbc_sql_alloc((void **) &stmt_out, buffer_len, sizeof(SQLCHAR)); + retval = SQLNativeSql(ch, stmt_in, S48_STRING_LENGTH(stmt_txt), + stmt_out, buffer_len, &buffer_needed); - retval = SQLNativeSql(ch, stmt_in, S48_STRING_LENGTH(stmt_txt), - stmt_out, ODBC_MAX_NATIVE_SQL_STR_LEN - 1, - &buffer_len); + if (ODBC_SUCCESS(retval) && buffer_needed > buffer_len) + buffer_len = buffer_needed+1; + else + break; + } switch (retval) { case SQL_SUCCESS: case SQL_SUCCESS_WITH_INFO: { - return s48_enter_string(stmt_out); + s48_value res = s48_enter_string(stmt_out); + free(stmt_out); + return res; } case SQL_ERROR: { @@ -1672,14 +1775,25 @@ s48_value odbc_sql_get_data(s48_value stmt_handle, case SQL_C_CHAR: case SQL_C_BINARY: { - SQLCHAR str[ODBC_GET_DATA_MAX_STR_LEN]; - SQLINTEGER str_len; - - retval = SQLGetData(sh, cn, tt, - str, ODBC_GET_DATA_MAX_STR_LEN-1, - &str_len); + SQLCHAR *buffer = NULL; + SQLINTEGER buffer_len, buffer_needed; + s48_value res = S48_UNSPECIFIC; + + buffer_len = ODBC_RETVAL_BUFFER_INITIAL_SIZE; + for (;;) + { + odbc_sql_alloc((void **) &buffer, buffer_len, sizeof(SQLCHAR)); + retval = SQLGetData(sh, cn, tt, + buffer, buffer_len, &buffer_needed); + if (ODBC_SUCCESS(retval) && (buffer_needed > buffer_len)) + buffer_len = buffer_needed+1; + else + break; + } check_sql_get_data_result(retval, sh); - return s48_enter_string((char *) str); + res = s48_enter_string(buffer); + free(buffer); + return res; } case SQL_C_SSHORT: case SQL_C_USHORT: @@ -1966,8 +2080,8 @@ s48_value odbc_sql_describe_col(s48_value stmt_handle, s48_value column_number) SQLHSTMT sh; SQLSMALLINT cn; - SQLCHAR column_name[ODBC_DESCRIBE_COL_MAX_STR_LEN]; - SQLSMALLINT buf_len, data_type, digits, nullable; + SQLCHAR *buffer = NULL; + SQLSMALLINT buffer_len, buffer_needed, data_type, digits, nullable; SQLUINTEGER col_size; SQLRETURN retval; s48_value col_rec = S48_UNSPECIFIC; @@ -1980,14 +2094,20 @@ s48_value odbc_sql_describe_col(s48_value stmt_handle, s48_value column_number) sh = (SQLHSTMT) s48_extract_integer(stmt_handle); cn = (SQLSMALLINT) s48_extract_integer(column_number); - retval = SQLDescribeCol(sh, cn, - column_name, - ODBC_DESCRIBE_COL_MAX_STR_LEN - 1, - &buf_len, - &data_type, - &col_size, - &digits, - &nullable); + buffer_len = ODBC_RETVAL_BUFFER_INITIAL_SIZE; + for (;;) + { + odbc_sql_alloc((void **) &buffer, buffer_len, sizeof(SQLCHAR)); + retval = SQLDescribeCol(sh, cn, + buffer, buffer_len, &buffer_needed, + &data_type, &col_size, &digits, + &nullable); + + if (ODBC_SUCCESS(retval) && (buffer_needed > buffer_len)) + buffer_len = buffer_needed+1; + else + break; + } switch (retval) { @@ -1995,7 +2115,7 @@ s48_value odbc_sql_describe_col(s48_value stmt_handle, s48_value column_number) { col_rec = s48_make_record(odbc_column_record_type); S48_RECORD_SET(col_rec, SR_ODBC_COLUMN_NAME, - s48_enter_string(column_name)); + s48_enter_string(buffer)); S48_RECORD_SET(col_rec, SR_ODBC_COLUMN_TYPE, s48_enter_integer(data_type)); S48_RECORD_SET(col_rec, SR_ODBC_COLUMN_SIZE, @@ -2006,6 +2126,7 @@ s48_value odbc_sql_describe_col(s48_value stmt_handle, s48_value column_number) s48_enter_integer(nullable)); S48_GC_UNPROTECT(); + free(buffer); return col_rec; } case SQL_STILL_EXECUTING: @@ -2817,8 +2938,8 @@ s48_value odbc_sql_get_diag_recs(s48_value handle_type, s48_value handle) SQLHANDLE h; SQLCHAR sql_state[6]; SQLINTEGER native_error; - SQLCHAR message[ERROR_MSG_BUFFER_LEN]; - SQLSMALLINT i, message_len, more_recs; + SQLCHAR *buffer = NULL; + SQLSMALLINT i, more_recs, buffer_len, buffer_needed; SQLRETURN retval; s48_value res = S48_UNSPECIFIC; s48_value diag_rec = S48_UNSPECIFIC; @@ -2833,12 +2954,23 @@ s48_value odbc_sql_get_diag_recs(s48_value handle_type, s48_value handle) ODBC_DEBUG_PRINTF("odbc_sql_get_diag_recs\n"); i = more_recs = 1; + buffer_len = ODBC_RETVAL_BUFFER_INITIAL_SIZE; while (more_recs) { diag_rec = s48_make_record(odbc_diag_record_type); - retval = SQLGetDiagRec(ht, h, i, - sql_state, &native_error, - message, sizeof(message), &message_len); + + for (;;) + { + odbc_sql_alloc((void **) &buffer, buffer_len, sizeof(SQLCHAR)); + retval = SQLGetDiagRec(ht, h, i, + sql_state, &native_error, + buffer, buffer_len, &buffer_needed); + + if (ODBC_SUCCESS(retval) && (buffer_needed > buffer_len)) + buffer_len = buffer_needed+1; + else + break; + } switch (retval) { @@ -2846,7 +2978,7 @@ s48_value odbc_sql_get_diag_recs(s48_value handle_type, s48_value handle) { S48_RECORD_SET(diag_rec, SR_ODBC_DIAG_SQL_STATE, s48_enter_string(sql_state)); S48_RECORD_SET(diag_rec, SR_ODBC_DIAG_NATIVE_ERROR, s48_enter_integer(native_error)); - S48_RECORD_SET(diag_rec, SR_ODBC_DIAG_MESSAGE, s48_enter_string(message)); + S48_RECORD_SET(diag_rec, SR_ODBC_DIAG_MESSAGE, s48_enter_string(buffer)); res = s48_cons(diag_rec, res); break; } @@ -2875,6 +3007,7 @@ s48_value odbc_sql_get_diag_recs(s48_value handle_type, s48_value handle) } S48_GC_UNPROTECT(); /* res */ + free(buffer); return res; } @@ -3050,6 +3183,30 @@ s48_value struct_to_sql_timestamp_record(SQL_TIMESTAMP_STRUCT *ts) return sql_timestamp; } +void odbc_sql_alloc(void **buffer, size_t buffer_len, size_t type_len) +{ + if (*buffer == NULL) + { +#ifdef ODBC_DEBUG_MSGS + printf("calloc %d %d\n", buffer_len+1, type_len); +#endif + *buffer = (void *) calloc(buffer_len+1, type_len); + } + else + { +#ifdef ODBC_DEBUG_MSGS + printf("realloc %d\n", buffer_len*type_len); +#endif + *buffer = (void *) realloc(*buffer, buffer_len*type_len); + } + + if (*buffer == NULL) + { + ODBC_RAISE_EXCEPTION("Could not allocate return value buffer"); + return; + } +} + void s48_init_odbc(void) { /* bindings for record types */ diff --git a/scsh/odbc/odbc.h b/scsh/odbc/odbc.h index 54aef14..27961ec 100644 --- a/scsh/odbc/odbc.h +++ b/scsh/odbc/odbc.h @@ -19,6 +19,8 @@ #define ODBC_MAX_NATIVE_SQL_STR_LEN ODBC_MAX_STR_LEN #define ODBC_MAX_GET_DESC_FIELD_STR_LEN ODBC_MAX_STR_LEN +#define ODBC_RETVAL_BUFFER_INITIAL_SIZE 3 + #define ODBC_DEBUG_MSGS 1 #define ODBC_RAISE_EXCEPTION(MSG) s48_raise_string_os_error(MSG) @@ -41,6 +43,9 @@ /* offsets for scheme records */ +/* some useful macros */ +#define ODBC_SUCCESS(retval) ((retval == SQL_SUCCESS) || (retval == SQL_SUCCESS_WITH_INFO)) + /* corresponds to sql-date */ static s48_value sql_date_record_type = S48_FALSE; @@ -419,5 +424,7 @@ s48_value struct_to_sql_time_record(SQL_TIME_STRUCT *ts); void sql_timestamp_record_to_struct(s48_value sql_timestamp, SQL_TIMESTAMP_STRUCT *ts); +void odbc_sql_alloc(void **buffer, size_t buffer_len, size_t type_len); + void s48_init_odbc(void); diff --git a/scsh/odbc/odbc.scm b/scsh/odbc/odbc.scm index 8d5f6e1..9e9f5b3 100644 --- a/scsh/odbc/odbc.scm +++ b/scsh/odbc/odbc.scm @@ -203,7 +203,7 @@ (define sql-api-sqlsetscrolloptions 69) (define sql-api-sqltableprivileges 70) -;;; info keys for odbc-sql-get-info-argint/string +;;; info keys for odbc-sql-get-info-arg-int/string ; ODBC 1.0, returns integer (define sql-get-info-arg-maxdriverconnections 0) ; ODBC 1.0, returns integer @@ -472,7 +472,7 @@ (define (odbc-sql-data-sources env-handle) (check-arg environment-handle? env-handle odbc-sql-data-sources) - (odbc-sql-data-sources-internal (environment-handle-handle env-handle))) + (reverse (odbc-sql-data-sources-internal (environment-handle-handle env-handle)))) (import-lambda-definition odbc-sql-data-sources-internal (env-handle) @@ -685,6 +685,14 @@ (stmt-handle stmt-txt) "odbc_sql_execute_direct") +(define (odbc-sql-native-sql conn-handle stmt-txt) + (check-arg connection-handle? conn-handle odbc-sql-native-sql) + (odbc-sql-native-sql-internal (connection-handle-handle conn-handle) + stmt-txt)) + +(import-lambda-definition odbc-sql-native-sql-internal + (conn-handle stmt-txt) + "odbc_sql_native_sql") ;;; PART 7