diff --git a/cpp/lib/ClientBindUploader.cpp b/cpp/lib/ClientBindUploader.cpp index 50c482d9b2..ee26cc63ab 100644 --- a/cpp/lib/ClientBindUploader.cpp +++ b/cpp/lib/ClientBindUploader.cpp @@ -4,6 +4,7 @@ #include +#include #include "ClientBindUploader.hpp" #include "../logger/SFLogger.hpp" @@ -83,6 +84,7 @@ void ClientBindUploader::executeUploading(const std::string &sql, std::basic_iostream& uploadStream, size_t dataSize) { + snowflake_prepare(m_stmt, sql.c_str(), 0); SF_STATUS ret = _snowflake_execute_put_get_native(m_stmt, &uploadStream, dataSize, NULL); if (ret != SF_STATUS_SUCCESS) { @@ -111,44 +113,55 @@ _snowflake_stage_bind_upload(SF_STMT* sfstmt) char name_buf[SF_PARAM_NAME_BUF_LEN]; char* name = NULL; char* value = NULL; + struct bind_info { + SF_BIND_INPUT* input; + void* val_ptr; + int step; + }; + std::vector bindInfo; for (unsigned int i = 0; i < sfstmt->params_len; i++) { - cJSON* binding; SF_BIND_INPUT* input = _snowflake_get_binding_by_index(sfstmt, i, &name, name_buf, SF_PARAM_NAME_BUF_LEN); if (input == NULL) { log_error("_snowflake_execute_ex: No parameter by this name %s", name); - continue; + return NULL; } - type = snowflake_type_to_string( - c_type_to_snowflake(input->c_type, SF_DB_TYPE_TIMESTAMP_NTZ)); - cJSON* val_array = snowflake_cJSON_CreateArray(); - size_t step = _snowflake_get_binding_value_size(input); - void* val_ptr = input->value; - size_t val_len; - cJSON* single_val = NULL; - for (int64 j = 0; j < sfstmt->paramset_size; j++, val_ptr = (char*)val_ptr + step) + bindInfo.emplace_back(); + bindInfo.back().input = input; + bindInfo.back().val_ptr = input->value; + bindInfo.back().step = _snowflake_get_binding_value_size(input); + } + for (int64 i = 0; i < sfstmt->paramset_size; i++) + { + for (unsigned int j = 0; j < sfstmt->params_len; j++) { - if (SF_BIND_LEN_NULL == input->len_ind[j]) + SF_BIND_INPUT* input = bindInfo[j].input; + void* val_ptr = bindInfo[j].val_ptr; + int val_len = input->len; + if (input->len_ind) + { + val_len = input->len_ind[i]; + } + + if (SF_BIND_LEN_NULL == val_len) { uploader.addNullValue(); } if ((SF_C_TYPE_STRING == input->c_type) && - (SF_BIND_LEN_NTS == input->len_ind[j])) + (SF_BIND_LEN_NTS == val_len)) { val_len = strlen((char*)val_ptr); } - else - { - val_len = (size_t)(input->len_ind[j]); - } + value = value_to_string(val_ptr, val_len, input->c_type); - uploader.addStringValue(value, input->type); if (value) { + uploader.addStringValue(value, input->type); SF_FREE(value); } + bindInfo[j].val_ptr = (char*)bindInfo[j].val_ptr + bindInfo[j].step; } } bindStage = uploader.getStagePath(); @@ -161,7 +174,7 @@ _snowflake_stage_bind_upload(SF_STMT* sfstmt) if (!bindStage.empty()) { char* bind_stage = (char*) SF_CALLOC(1, bindStage.size() + 1); - sf_strncpy(bind_stage, bindStage.size(), bindStage.c_str(), bindStage.size()); + sf_strncpy(bind_stage, bindStage.size() + 1, bindStage.c_str(), bindStage.size()); return bind_stage; } diff --git a/lib/client.c b/lib/client.c index 23cb8f90cf..3afe1e9b2d 100644 --- a/lib/client.c +++ b/lib/client.c @@ -1959,11 +1959,17 @@ cJSON* STDCALL _snowflake_get_binding_json(SF_STMT* sfstmt) cJSON* val_array = snowflake_cJSON_CreateArray(); size_t step = _snowflake_get_binding_value_size(input); void* val_ptr = input->value; - size_t val_len; + int64 val_len; cJSON* single_val = NULL; for (int64 j = 0; j < sfstmt->paramset_size; j++, val_ptr = (char*)val_ptr + step) { - if (SF_BIND_LEN_NULL == input->len_ind[j]) + val_len = input->len; + if (input->len_ind) + { + val_len = input->len_ind[j]; + } + + if (SF_BIND_LEN_NULL == val_len) { single_val = snowflake_cJSON_CreateNull(); snowflake_cJSON_AddItemToArray(val_array, single_val); @@ -1971,14 +1977,11 @@ cJSON* STDCALL _snowflake_get_binding_json(SF_STMT* sfstmt) } if ((SF_C_TYPE_STRING == input->c_type) && - (SF_BIND_LEN_NTS == input->len_ind[j])) + (SF_BIND_LEN_NTS == val_len)) { val_len = strlen((char*)val_ptr); } - else - { - val_len = (size_t)(input->len_ind[j]); - } + value = value_to_string(val_ptr, val_len, input->c_type); single_val = snowflake_cJSON_CreateString(value); snowflake_cJSON_AddItemToArray(val_array, single_val); diff --git a/tests/test_bind_params.c b/tests/test_bind_params.c index 255cc0b857..4ffa97da8b 100644 --- a/tests/test_bind_params.c +++ b/tests/test_bind_params.c @@ -4,6 +4,7 @@ #include #include "utils/test_setup.h" +#include "memory.h" #define INPUT_ARRAY_SIZE 3 @@ -111,10 +112,192 @@ void test_bind_parameters(void **unused) { snowflake_term(sf); } +void test_array_binding_core(unsigned int array_size) { + /* init */ + SF_STATUS status; + int8* int8_array = NULL; + int8 int8_value = -12; + char int8_expected_result[] = "-12"; + uint8* uint8_array = NULL; + uint8 uint8_value = 12; + char uint8_expected_result[] = "12"; + int64* int64_array = NULL; + int64 int64_value = -12345; + char int64_expected_result[] = "-12345"; + uint64* uint64_array = NULL; + uint64 uint64_value = 12345; + char uint64_expected_result[] = "12345"; + float64* float_array = NULL; + float64 float_value = 1.23; + char float_expected_result[] = "1.23"; + char* string_array = NULL; + char string_value[] = "str"; + char string_expected_result[] = "str"; + byte* binary_array = NULL; + byte binary_value[] = {0x12, 0x34, 0x56, 0x78}; + char binary_expected_result[] = "12345678"; + sf_bool* bool_array = NULL; + sf_bool bool_value = SF_BOOLEAN_TRUE; + char bool_expected_result[] = "1"; + SF_BIND_INPUT int8_input; + SF_BIND_INPUT uint8_input; + SF_BIND_INPUT int64_input; + SF_BIND_INPUT uint64_input; + SF_BIND_INPUT float_input; + SF_BIND_INPUT string_input; + SF_BIND_INPUT binary_input; + SF_BIND_INPUT bool_input; + + SF_BIND_INPUT input_array[8]; + char* expected_results[8]; + unsigned int i = 0, j = 0; + + // initialize bindings with argument + int8_array = SF_CALLOC(array_size, sizeof(int8_value)); + uint8_array = SF_CALLOC(array_size, sizeof(uint8_value)); + int64_array = SF_CALLOC(array_size, sizeof(int64_value)); + uint64_array = SF_CALLOC(array_size, sizeof(uint64_value)); + float_array = SF_CALLOC(array_size, sizeof(float_value)); + string_array = SF_CALLOC(array_size, sizeof(string_value)); + binary_array = SF_CALLOC(array_size, sizeof(binary_value)); + bool_array = SF_CALLOC(array_size, sizeof(bool_value)); + + for (i = 0; i < array_size; i++) + { + int8_array[i] = int8_value; + uint8_array[i] = uint8_value; + int64_array[i] = int64_value; + uint64_array[i] = uint64_value; + float_array[i] = float_value; + memcpy(string_array + sizeof(string_value) * i, string_value, sizeof(string_value)); + memcpy(binary_array + sizeof(binary_value) * i, binary_value, sizeof(binary_value)); + bool_array[i] = bool_value; + } + + snowflake_bind_input_init(&int8_input); + snowflake_bind_input_init(&uint8_input); + snowflake_bind_input_init(&int64_input); + snowflake_bind_input_init(&uint64_input); + snowflake_bind_input_init(&float_input); + snowflake_bind_input_init(&string_input); + snowflake_bind_input_init(&binary_input); + snowflake_bind_input_init(&bool_input); + + int8_input.idx = 1; + int8_input.c_type = SF_C_TYPE_INT8; + int8_input.value = int8_array; + + uint8_input.idx = 2; + uint8_input.c_type = SF_C_TYPE_UINT8; + uint8_input.value = uint8_array; + + int64_input.idx = 3; + int64_input.c_type = SF_C_TYPE_INT64; + int64_input.value = int64_array; + + uint64_input.idx = 4; + uint64_input.c_type = SF_C_TYPE_UINT64; + uint64_input.value = uint64_array; + + float_input.idx = 5; + float_input.c_type = SF_C_TYPE_FLOAT64; + float_input.value = float_array; + + string_input.idx = 6; + string_input.c_type = SF_C_TYPE_STRING; + string_input.value = string_array; + string_input.len = sizeof(string_value); + + binary_input.idx = 7; + binary_input.c_type = SF_C_TYPE_BINARY; + binary_input.value = binary_array; + binary_input.len = sizeof(binary_value); + + bool_input.idx = 8; + bool_input.c_type = SF_C_TYPE_BOOLEAN; + bool_input.value = bool_array; + + input_array[0] = int8_input; + input_array[1] = uint8_input; + input_array[2] = int64_input; + input_array[3] = uint64_input; + input_array[4] = float_input; + input_array[5] = string_input; + input_array[6] = binary_input; + input_array[7] = bool_input; + + expected_results[0] = int8_expected_result; + expected_results[1] = uint8_expected_result; + expected_results[2] = int64_expected_result; + expected_results[3] = uint64_expected_result; + expected_results[4] = float_expected_result; + expected_results[5] = string_expected_result; + expected_results[6] = binary_expected_result; + expected_results[7] = bool_expected_result; + + /* Connect with all parameters set */ + SF_CONNECT* sf = setup_snowflake_connection(); + status = snowflake_connect(sf); + assert_int_equal(status, SF_STATUS_SUCCESS); + + /* Create a statement once and reused */ + SF_STMT* stmt = snowflake_stmt(sf); + status = snowflake_query( + stmt, + "create or replace temporary table t (c1 number, c2 number, c3 number, c4 number, c5 float, c6 string, c7 binary, c8 boolean)", + 0 + ); + assert_int_equal(status, SF_STATUS_SUCCESS); + + int64 paramset_size = array_size; + status = snowflake_stmt_set_attr(stmt, SF_STMT_PARAMSET_SIZE, ¶mset_size); + status = snowflake_prepare( + stmt, + "insert into t values(?, ?, ?, ?, ?, ?, ?, ?)", + 0 + ); + assert_int_equal(status, SF_STATUS_SUCCESS); + + status = snowflake_bind_param_array(stmt, input_array, sizeof(input_array) / sizeof(SF_BIND_INPUT)); + assert_int_equal(status, SF_STATUS_SUCCESS); + + status = snowflake_execute(stmt); + assert_int_equal(status, SF_STATUS_SUCCESS); + assert_int_equal(snowflake_affected_rows(stmt), array_size); + + status = snowflake_query(stmt, "select * from t", 0); + assert_int_equal(status, SF_STATUS_SUCCESS); + assert_int_equal(snowflake_num_rows(stmt), array_size); + + for (i = 0; i < array_size; i++) + { + status = snowflake_fetch(stmt); + assert_int_equal(status, SF_STATUS_SUCCESS); + char* result = NULL; + for (j = 0; j < 8; j++) + { + snowflake_column_as_const_str(stmt, j + 1, &result); + assert_string_equal(result, expected_results[j]); + } + } + snowflake_stmt_term(stmt); + snowflake_term(sf); +} + +void test_array_binding_normal(void** unused) { + test_array_binding_core(1000); +} + +void test_array_binding_stage(void** unused) { + test_array_binding_core(100000); +} + int main(void) { initialize_test(SF_BOOLEAN_FALSE); const struct CMUnitTest tests[] = { cmocka_unit_test(test_bind_parameters), + cmocka_unit_test(test_array_binding_normal), + cmocka_unit_test(test_array_binding_stage), }; int ret = cmocka_run_group_tests(tests, NULL, NULL); snowflake_global_term();