From 0dfa35b4f0e2737808c749ac2bd62d87f1b18d49 Mon Sep 17 00:00:00 2001 From: notkriswagner Date: Sat, 15 Sep 2018 21:05:34 -0700 Subject: [PATCH 01/16] Implemented new fetch API. Added unit tests to check coverting different types of values including NULL fields. Reworked Snowflake types to make int32 type 4 bytes instead of 8 bytes. --- include/snowflake/basic_types.h | 15 +- include/snowflake/client.h | 65 +- lib/client.c | 598 ++++++++++++- tests/CMakeLists.txt | 3 +- tests/test_column_fetch.c | 993 ++++++++++++++++++++++ tests/test_perf_column_evaluation.c | 10 +- tests/test_perf_string_reads_and_writes.c | 9 +- tests/test_perf_type_conversion.c | 13 +- tests/utils/test_setup.c | 2 +- tests/utils/test_setup.h | 2 +- 10 files changed, 1664 insertions(+), 46 deletions(-) create mode 100644 tests/test_column_fetch.c diff --git a/include/snowflake/basic_types.h b/include/snowflake/basic_types.h index f6372df248..603595ff77 100644 --- a/include/snowflake/basic_types.h +++ b/include/snowflake/basic_types.h @@ -9,6 +9,9 @@ extern "C" { #endif +#include +#include +#include #include "platform.h" /** @@ -16,8 +19,8 @@ extern "C" { */ typedef char int8; typedef unsigned char uint8; -typedef unsigned long int uint32; -typedef long int int32; +typedef unsigned int uint32; +typedef int int32; typedef unsigned long long int uint64; typedef long long int int64; typedef double float64; @@ -27,6 +30,14 @@ typedef int8 sf_bool; extern const int8 SF_BOOLEAN_TRUE; extern const int8 SF_BOOLEAN_FALSE; +#define SF_UINT32_MAX UINT_MAX +#define SF_UINT64_MAX ULLONG_MAX +#define SF_INT32_MIN INT_MIN +#define SF_INT32_MAX INT_MAX +#define SF_INT64_MIN LLONG_MIN +#define SF_INT64_MAX LLONG_MAX +#define SF_HUGE_VAL HUGE_VAL +#define SF_HUGE_VALF HUGE_VALF /** * Boolean data type string representation for Snowflake */ diff --git a/include/snowflake/client.h b/include/snowflake/client.h index 865dfd29f0..611e3caad2 100644 --- a/include/snowflake/client.h +++ b/include/snowflake/client.h @@ -123,6 +123,10 @@ typedef enum SF_STATUS { SF_STATUS_ERROR_CONNECTION_NOT_EXIST = 240016, SF_STATUS_ERROR_STATEMENT_NOT_EXIST = 240017, SF_STATUS_ERROR_CONVERSION_FAILURE = 240018, + SF_STATUS_ERROR_OUT_OF_BOUNDS = 240019, + SF_STATUS_ERROR_MISSING_COLUMN_IN_ROW = 240020, + SF_STATUS_ERROR_INVALID_CONVERSION = 240021, + SF_STATUS_ERROR_OUT_OF_RANGE = 240022 } SF_STATUS; @@ -322,6 +326,7 @@ typedef struct SF_STMT { SF_CONNECT *connection; char *sql_text; void *raw_results; + void *cur_row; int64 chunk_rowcount; int64 total_rowcount; int64 total_fieldcount; @@ -535,7 +540,7 @@ int64 STDCALL snowflake_affected_rows(SF_STMT *sfstmt); * @param sfstmt SNOWFLAKE_RESULTSET context. * @return the number of rows. */ -uint64 STDCALL snowflake_num_rows(SF_STMT *sfstmt); +int64 STDCALL snowflake_num_rows(SF_STMT *sfstmt); /** * Returns the number of fields in the result set. @@ -543,7 +548,7 @@ uint64 STDCALL snowflake_num_rows(SF_STMT *sfstmt); * @param sfstmt SNOWFLAKE_RESULTSET context. * @return the number of fields. */ -uint64 STDCALL snowflake_num_fields(SF_STMT *sfstmt); +int64 STDCALL snowflake_num_fields(SF_STMT *sfstmt); /** * Returns a SQLState for the result set. @@ -693,6 +698,62 @@ const char *STDCALL snowflake_c_type_to_string(SF_C_TYPE type); */ SF_STATUS STDCALL _snowflake_check_connection_parameters(SF_CONNECT *sf); +/** + * Converts a column in the current row into a boolean value (if a valid conversion exists) + * + * @param stmt SF_STMT context + * @param idx Column index + * @param value_ptr Coverted column data is stored in this pointer (if conversion was successful) + * @return 0 if success, otherwise an errno is returned + */ +SF_STATUS STDCALL snowflake_column_as_boolean(SF_STMT *stmt, int idx, sf_bool *value_ptr); + + +SF_STATUS STDCALL snowflake_column_as_uint8(SF_STMT *stmt, int idx, uint8 *value_ptr); + + +SF_STATUS STDCALL snowflake_column_as_uint32(SF_STMT *stmt, int idx, uint32 *value_ptr); + + +SF_STATUS STDCALL snowflake_column_as_uint64(SF_STMT *stmt, int idx, uint64 *value_ptr); + + +SF_STATUS STDCALL snowflake_column_as_int8(SF_STMT *stmt, int idx, int8 *value_ptr); + + +SF_STATUS STDCALL snowflake_column_as_int32(SF_STMT *stmt, int idx, int32 *value_ptr); + + +SF_STATUS STDCALL snowflake_column_as_int64(SF_STMT *stmt, int idx, int64 *value_ptr); + + +SF_STATUS STDCALL snowflake_column_as_float32(SF_STMT *stmt, int idx, float32 *value_ptr); + + +SF_STATUS STDCALL snowflake_column_as_float64(SF_STMT *stmt, int idx, float64 *value_ptr); + +/** + * Returns the raw column data in the form of a const char pointer that the user can then use + * to read the string data or copy to another buffer + */ + +SF_STATUS STDCALL snowflake_column_as_const_str(SF_STMT *stmt, int idx, const char **value_ptr); + +SF_STATUS STDCALL snowflake_column_as_str(SF_STMT *stmt, int idx, char **value_ptr, size_t *value_len_ptr); + +/** + * Returns the length of the raw column data + * + * @param stmt + * @param idx + * @return + */ +SF_STATUS STDCALL snowflake_column_strlen(SF_STMT *stmt, int idx, size_t *value_ptr); + +// Returns whether or not the column data is null +SF_STATUS STDCALL snowflake_column_is_null(SF_STMT *stmt, int idx, sf_bool *value_ptr); + + #ifdef __cplusplus } #endif diff --git a/lib/client.c b/lib/client.c index 69615789c3..3af3b17ef0 100644 --- a/lib/client.c +++ b/lib/client.c @@ -260,6 +260,136 @@ static sf_bool _extract_timestamp( return SF_BOOLEAN_TRUE; } +/** + * Extracts a Snowflake internal representation of timestamp into + * seconds, nanoseconds and optionally Timezone offset. + * @param sec pointer of seconds + * @param nsec pointer of nanoseconds + * @param tzoffset pointer of Timezone offset. + * @param src source buffer including a Snowflake internal timestamp + * @param timezone Timezone for TIMESTAMP_LTZ + * @param scale Timestamp data type scale between 0 and 9 + * @return SF_BOOLEAN_TRUE if success otherwise SF_BOOLEAN_FALSE + */ +static sf_bool _extract_timestamp_dynamic( + char **result_ptr, size_t *result_len_ptr, SF_DB_TYPE sftype, + const char *src, const char *timezone, int64 scale) { + sf_bool ret = SF_BOOLEAN_FALSE; + time_t nsec = 0L; + time_t sec = 0L; + int64 tzoffset = 0; + struct tm tm_obj; + struct tm *tm_ptr = NULL; + char tzname[64]; + char *tzptr = (char *) timezone; + size_t len = 0; + char *value = NULL; + + memset(&tm_obj, 0, sizeof(tm_obj)); + + /* Search for a decimal point */ + char *ptr = strchr(src, (int) '.'); + if (ptr == NULL) { + ret = SF_BOOLEAN_FALSE; + goto cleanup; + } + sec = strtoll(src, NULL, 10); + + /* Search for a space for TIMESTAMP_TZ */ + char *sptr = strchr(ptr + 1, (int) ' '); + nsec = strtoll(ptr + 1, NULL, 10); + if (sptr != NULL) { + /* TIMESTAMP_TZ */ + nsec = strtoll(ptr + 1, NULL, 10); + tzoffset = strtoll(sptr + 1, NULL, 10) - TIMEZONE_OFFSET_RANGE; + } + if (sec < 0 && nsec > 0) { + nsec = pow10_int64[scale] - nsec; + sec--; + } + log_info("sec: %lld, nsec: %lld", sec, nsec); + + if (sftype == SF_DB_TYPE_TIMESTAMP_TZ) { + /* make up Timezone name from the tzoffset */ + ldiv_t dm = ldiv((long) tzoffset, 60L); + sprintf(tzname, "UTC%c%02ld:%02ld", + dm.quot > 0 ? '+' : '-', labs(dm.quot), labs(dm.rem)); + tzptr = tzname; + } + + /* replace a dot character with NULL */ + if (sftype == SF_DB_TYPE_TIMESTAMP_NTZ || + sftype == SF_DB_TYPE_TIME) { + tm_ptr = sf_gmtime(&sec, &tm_obj); + } else if (sftype == SF_DB_TYPE_TIMESTAMP_LTZ || + sftype == SF_DB_TYPE_TIMESTAMP_TZ) { + /* set the environment variable TZ to the session timezone + * so that localtime_tz honors it. + */ + _mutex_lock(&gmlocaltime_lock); + const char *prev_tz_ptr = sf_getenv("TZ"); + sf_setenv("TZ", tzptr); + sf_tzset(); + sec += tzoffset * 60 * 2; /* adjust for TIMESTAMP_TZ */ + tm_ptr = sf_localtime(&sec, &tm_obj); + if (prev_tz_ptr != NULL) { + sf_setenv("TZ", prev_tz_ptr); /* cannot set to NULL */ + } else { + sf_unsetenv("TZ"); + } + sf_tzset(); + _mutex_unlock(&gmlocaltime_lock); + } + if (tm_ptr == NULL) { + len = 0; + ret = SF_BOOLEAN_FALSE; + goto cleanup; + } + const char *fmt0; + size_t max_len = 1; + if (sftype != SF_DB_TYPE_TIME) { + max_len += 21; + fmt0 = "%Y-%m-%d %H:%M:%S"; + } else { + max_len += 8; + fmt0 = "%H:%M:%S"; + } + /* adjust scale */ + char fmt[20]; + sprintf(fmt, ".%%0%lldld", scale); + + // Add space for scale if scale is greater than 0 + max_len += (scale > 0) ? 1 + scale : 0; + // Add space for timezone if SF_DB_TYPE_TIMESTAMP_TZ is set + max_len += (sftype == SF_DB_TYPE_TIMESTAMP_TZ) ? 7 : 0; + // Allocate string buffer to store date using our calculated max length + value = SF_CALLOC(1, max_len); + len = strftime(value, max_len, fmt0, &tm_obj); + if (scale > 0) { + len += snprintf( + &((char *) value)[len], + max_len - len, fmt, + nsec); + } + if (sftype == SF_DB_TYPE_TIMESTAMP_TZ) { + /* Timezone info */ + ldiv_t dm = ldiv((long) tzoffset, 60L); + len += snprintf( + &((char *) value)[len], + max_len - len, + " %c%02ld:%02ld", + dm.quot > 0 ? '+' : '-', labs(dm.quot), labs(dm.rem)); + } + + ret = SF_BOOLEAN_TRUE; + +cleanup: + *result_len_ptr = len; + *result_ptr = value; + return ret; +} + + /** * Reset the connection parameters with the returned parameteres * @param sf SF_CONNECT object @@ -1304,22 +1434,24 @@ SF_STATUS STDCALL snowflake_fetch(SF_STMT *sfstmt) { } } - // Check that we can write to the provided result bindings - for (i = 0; i < sfstmt->total_fieldcount; i++) { - result = sf_array_list_get(sfstmt->results, (size_t) i + 1); - if (result == NULL) { - continue; - } else { - if (result->c_type != sfstmt->desc[i].c_type && - result->c_type != SF_C_TYPE_STRING) { - // TODO add error msg - goto cleanup; - } - } - } + // TODO Remove this block of code +// // Check that we can write to the provided result bindings +// for (i = 0; i < sfstmt->total_fieldcount; i++) { +// result = sf_array_list_get(sfstmt->results, (size_t) i + 1); +// if (result == NULL) { +// continue; +// } else { +// if (result->c_type != sfstmt->desc[i].c_type && +// result->c_type != SF_C_TYPE_STRING) { +// goto cleanup; +// } +// } +// } // Get next result row row = snowflake_cJSON_DetachItemFromArray(sfstmt->raw_results, 0); + snowflake_cJSON_free((cJSON *) sfstmt->cur_row); + sfstmt->cur_row = row; sfstmt->chunk_rowcount--; sfstmt->total_row_index++; @@ -1476,7 +1608,6 @@ SF_STATUS STDCALL snowflake_fetch(SF_STMT *sfstmt) { ret = SF_STATUS_SUCCESS; cleanup: - snowflake_cJSON_Delete(row); return ret; } @@ -1891,22 +2022,18 @@ SF_ERROR_STRUCT *STDCALL snowflake_stmt_error(SF_STMT *sfstmt) { return &sfstmt->error; } -uint64 STDCALL snowflake_num_rows(SF_STMT *sfstmt) { - // TODO fix int vs uint stuff +int64 STDCALL snowflake_num_rows(SF_STMT *sfstmt) { if (!sfstmt) { - // TODO change to -1? - return 0; + return -1; } - return (uint64) sfstmt->total_rowcount; + return sfstmt->total_rowcount; } -uint64 STDCALL snowflake_num_fields(SF_STMT *sfstmt) { - // TODO fix int vs uint stuff +int64 STDCALL snowflake_num_fields(SF_STMT *sfstmt) { if (!sfstmt) { - // TODO change to -1? - return 0; + return -1; } - return (uint64) sfstmt->total_fieldcount; + return sfstmt->total_fieldcount; } uint64 STDCALL snowflake_num_params(SF_STMT *sfstmt) { @@ -2006,3 +2133,426 @@ SF_STATUS STDCALL snowflake_propagate_error(SF_CONNECT *sf, SF_STMT *sfstmt) { } return SF_STATUS_SUCCESS; } + + +/** + * + * Start of new fetch API + * + */ + + +// Make sure that idx is in bounds and that column exists +SF_STATUS STDCALL _snowflake_get_column(SF_STMT *stmt, int idx, cJSON **column_ptr) { + if (idx > snowflake_num_fields(stmt) || idx <= 0) { + return SF_STATUS_ERROR_OUT_OF_BOUNDS; + } + + cJSON *column = snowflake_cJSON_GetArrayItem(stmt->cur_row, idx - 1); + if (!column) { + *column_ptr = NULL; + return SF_STATUS_ERROR_MISSING_COLUMN_IN_ROW; + } + + *column_ptr = column; + return SF_STATUS_SUCCESS; +} + +SF_STATUS STDCALL snowflake_column_as_boolean(SF_STMT *stmt, int idx, sf_bool *value_ptr) { + SF_STATUS status; + cJSON *column = NULL; + + // Get column + if ((status = _snowflake_get_column(stmt, idx, &column)) != SF_STATUS_SUCCESS) { + return status; + } + + sf_bool value = SF_BOOLEAN_FALSE; + if (snowflake_cJSON_IsNull(column)) { + *value_ptr = value; + return SF_STATUS_SUCCESS; + } + + char *endptr; + switch (stmt->desc[idx - 1].c_type) { + case SF_C_TYPE_BOOLEAN: + value = strcmp("1", column->valuestring) == 0 ? SF_BOOLEAN_TRUE: SF_BOOLEAN_FALSE; + break; + case SF_C_TYPE_FLOAT64: + errno = 0; + float64 float_val = strtod(column->valuestring, &endptr); + // Check for errors + if (endptr == column->valuestring) { + return SF_STATUS_ERROR_INVALID_CONVERSION; + } + if ((float_val == SF_HUGE_VAL || float_val == -SF_HUGE_VAL) && errno == ERANGE) { + return SF_STATUS_ERROR_OUT_OF_RANGE; + } + // Determine if true or false + value = (float_val == 0.0) ? SF_BOOLEAN_FALSE : SF_BOOLEAN_TRUE; + break; + case SF_C_TYPE_INT64: + errno = 0; + int64 int_val = strtoll(column->valuestring, &endptr, 10); + // Check for errors + if (endptr == column->valuestring) { + return SF_STATUS_ERROR_INVALID_CONVERSION; + } + if ((int_val == SF_INT64_MAX || int_val == SF_INT64_MIN) && errno == ERANGE) { + return SF_STATUS_ERROR_OUT_OF_RANGE; + } + // Determine if true or false + value = (int_val == 0) ? SF_BOOLEAN_FALSE : SF_BOOLEAN_TRUE; + break; + case SF_C_TYPE_STRING: + if (strlen(column->valuestring) == 0) { + value = SF_BOOLEAN_FALSE; + } else { + value = SF_BOOLEAN_TRUE; + } + break; + default: + return SF_STATUS_ERROR_INVALID_CONVERSION; + } + *value_ptr = value; + return SF_STATUS_SUCCESS; +} + +SF_STATUS STDCALL snowflake_column_as_uint8(SF_STMT *stmt, int idx, uint8 *value_ptr) { + SF_STATUS status; + cJSON *column = NULL; + + // Get column + if ((status = _snowflake_get_column(stmt, idx, &column)) != SF_STATUS_SUCCESS) { + return status; + } + + *value_ptr = (!snowflake_cJSON_IsNull(column)) ? (uint8) column->valuestring[0] : (uint8) 0; + return SF_STATUS_SUCCESS; +} + +SF_STATUS STDCALL snowflake_column_as_uint32(SF_STMT *stmt, int idx, uint32 *value_ptr) { + SF_STATUS status; + cJSON *column = NULL; + + // Get column + if ((status = _snowflake_get_column(stmt, idx, &column)) != SF_STATUS_SUCCESS) { + return status; + } + + uint32 value = 0; + if (snowflake_cJSON_IsNull(column)) { + *value_ptr = value; + return SF_STATUS_SUCCESS; + } + + char *endptr; + errno = 0; + value = strtoul(column->valuestring, &endptr, 10); + // Check for errors + if (endptr == column->valuestring) { + return SF_STATUS_ERROR_INVALID_CONVERSION; + } + if ((value == SF_UINT32_MAX || value == 0) && errno == ERANGE) { + return SF_STATUS_ERROR_OUT_OF_RANGE; + } + // Everything checks out, set value and return success + *value_ptr = value; + return SF_STATUS_SUCCESS; +} + +SF_STATUS STDCALL snowflake_column_as_uint64(SF_STMT *stmt, int idx, uint64 *value_ptr) { + SF_STATUS status; + cJSON *column = NULL; + + // Get column + if ((status = _snowflake_get_column(stmt, idx, &column)) != SF_STATUS_SUCCESS) { + return status; + } + + uint64 value = 0; + if (snowflake_cJSON_IsNull(column)) { + *value_ptr = value; + return SF_STATUS_SUCCESS; + } + + char *endptr; + errno = 0; + value = strtoull(column->valuestring, &endptr, 10); + // Check for errors + if (endptr == column->valuestring) { + return SF_STATUS_ERROR_INVALID_CONVERSION; + } + if ((value == SF_UINT64_MAX || value == 0) && errno == ERANGE) { + return SF_STATUS_ERROR_OUT_OF_RANGE; + } + // Everything checks out, set value and return success + *value_ptr = value; + return SF_STATUS_SUCCESS; +} + +SF_STATUS STDCALL snowflake_column_as_int8(SF_STMT *stmt, int idx, int8 *value_ptr) { + SF_STATUS status; + cJSON *column = NULL; + + // Get column + if ((status = _snowflake_get_column(stmt, idx, &column)) != SF_STATUS_SUCCESS) { + return status; + } + + *value_ptr = (!snowflake_cJSON_IsNull(column)) ? (int8) column->valuestring[0] : (int8) 0; + return SF_STATUS_SUCCESS; +} + +SF_STATUS STDCALL snowflake_column_as_int32(SF_STMT *stmt, int idx, int32 *value_ptr) { + SF_STATUS status; + cJSON *column = NULL; + + // Get column + if ((status = _snowflake_get_column(stmt, idx, &column)) != SF_STATUS_SUCCESS) { + return status; + } + + int64 value = 0; + if (snowflake_cJSON_IsNull(column)) { + *value_ptr = (int32) value; + return SF_STATUS_SUCCESS; + } + + char *endptr; + errno = 0; + value = strtol(column->valuestring, &endptr, 10); + // Check for errors + if (endptr == column->valuestring) { + return SF_STATUS_ERROR_INVALID_CONVERSION; + } + if (((value == SF_INT64_MAX || value == SF_INT64_MIN) && errno == ERANGE) || (value > SF_INT32_MAX || value < SF_INT32_MIN)) { + return SF_STATUS_ERROR_OUT_OF_RANGE; + } + // Everything checks out, set value and return success + *value_ptr = (int32) value; + return SF_STATUS_SUCCESS; +} + +SF_STATUS STDCALL snowflake_column_as_int64(SF_STMT *stmt, int idx, int64 *value_ptr) { + SF_STATUS status; + cJSON *column = NULL; + + // Get column + if ((status = _snowflake_get_column(stmt, idx, &column)) != SF_STATUS_SUCCESS) { + return status; + } + + int64 value = 0; + if (snowflake_cJSON_IsNull(column)) { + *value_ptr = value; + return SF_STATUS_SUCCESS; + } + + char *endptr; + errno = 0; + value = strtoll(column->valuestring, &endptr, 10); + // Check for errors + if (endptr == column->valuestring) { + return SF_STATUS_ERROR_INVALID_CONVERSION; + } + if ((value == SF_INT64_MAX || value == SF_INT64_MIN) && errno == ERANGE) { + return SF_STATUS_ERROR_OUT_OF_RANGE; + } + // Everything checks out, set value and return success + *value_ptr = value; + return SF_STATUS_SUCCESS; +} + +SF_STATUS STDCALL snowflake_column_as_float32(SF_STMT *stmt, int idx, float32 *value_ptr) { + SF_STATUS status; + cJSON *column = NULL; + + // Get column + if ((status = _snowflake_get_column(stmt, idx, &column)) != SF_STATUS_SUCCESS) { + return status; + } + + float32 value = 0.0; + if (snowflake_cJSON_IsNull(column)) { + *value_ptr = value; + return SF_STATUS_SUCCESS; + } + + char *endptr; + errno = 0; + value = strtof(column->valuestring, &endptr); + // Check for errors + if (endptr == column->valuestring) { + return SF_STATUS_ERROR_INVALID_CONVERSION; + } + if (errno == ERANGE || value == INFINITY || value == -INFINITY) { + return SF_STATUS_ERROR_OUT_OF_RANGE; + } + // Everything checks out, set value and return success + *value_ptr = value; + return SF_STATUS_SUCCESS; +} + +SF_STATUS STDCALL snowflake_column_as_float64(SF_STMT *stmt, int idx, float64 *value_ptr) { + SF_STATUS status; + cJSON *column = NULL; + + // Get column + if ((status = _snowflake_get_column(stmt, idx, &column)) != SF_STATUS_SUCCESS) { + return status; + } + + float64 value = 0.0; + if (snowflake_cJSON_IsNull(column)) { + *value_ptr = value; + return SF_STATUS_SUCCESS; + } + + char *endptr; + errno = 0; + value = strtod(column->valuestring, &endptr); + // Check for errors + if (endptr == column->valuestring) { + return SF_STATUS_ERROR_INVALID_CONVERSION; + } + if (errno == ERANGE || value == INFINITY || value == -INFINITY) { + return SF_STATUS_ERROR_OUT_OF_RANGE; + } + // Everything checks out, set value and return success + *value_ptr = value; + return SF_STATUS_SUCCESS; +} + +SF_STATUS STDCALL snowflake_column_as_const_str(SF_STMT *stmt, int idx, const char **value_ptr) { + SF_STATUS status; + cJSON *column = NULL; + + // Get column + if ((status = _snowflake_get_column(stmt, idx, &column)) != SF_STATUS_SUCCESS) { + return status; + } + + *value_ptr = (!snowflake_cJSON_IsNull(column)) ? column->valuestring : NULL; + + return SF_STATUS_SUCCESS; +} + +SF_STATUS STDCALL snowflake_column_as_str(SF_STMT *stmt, int idx, char **value_ptr, size_t *value_len_ptr) { + SF_STATUS status; + cJSON *column = NULL; + + // Get column + if ((status = _snowflake_get_column(stmt, idx, &column)) != SF_STATUS_SUCCESS) { + return status; + } + + char *value = NULL; + size_t value_len = 0; + + if (snowflake_cJSON_IsNull(column)) { + goto cleanup; + } + + time_t sec = 0L; + struct tm tm_obj; + struct tm *tm_ptr; + memset(&tm_obj, 0, sizeof(tm_obj)); + + switch (stmt->desc[idx - 1].type) { + case SF_DB_TYPE_BOOLEAN: + if (strcmp(column->valuestring, "0") == 0) { + /* False */ + value_len = strlen(SF_BOOLEAN_FALSE_STR); + value = calloc(1, value_len + 1); + strncpy(value, SF_BOOLEAN_FALSE_STR, value_len + 1); + } else { + /* True */ + value_len = strlen(SF_BOOLEAN_TRUE_STR); + value = calloc(1, value_len + 1); + strncpy(value, SF_BOOLEAN_TRUE_STR, value_len + 1); + } + break; + case SF_DB_TYPE_DATE: + sec = + (time_t) strtol(column->valuestring, NULL, 10) * + 86400L; + _mutex_lock(&gmlocaltime_lock); + tm_ptr = sf_gmtime(&sec, &tm_obj); + _mutex_unlock(&gmlocaltime_lock); + if (tm_ptr == NULL) { + SET_SNOWFLAKE_ERROR(&stmt->error, + SF_STATUS_ERROR_CONVERSION_FAILURE, + "Failed to convert a date value to a string.", + SF_SQLSTATE_GENERAL_ERROR); + value = NULL; + value_len = 0; + goto cleanup; + } + value = calloc(1, 13); + value_len = strftime(value, 13, "%Y-%m-%d", &tm_obj); + break; + case SF_DB_TYPE_TIME: + case SF_DB_TYPE_TIMESTAMP_NTZ: + case SF_DB_TYPE_TIMESTAMP_LTZ: + case SF_DB_TYPE_TIMESTAMP_TZ: + if (!_extract_timestamp_dynamic( + &value, + &value_len, + stmt->desc[idx - 1].type, + column->valuestring, + stmt->connection->timezone, + stmt->desc[idx - 1].scale)) { + SET_SNOWFLAKE_ERROR(&stmt->error, + SF_STATUS_ERROR_CONVERSION_FAILURE, + "Failed to convert a timestamp value to a string.", + SF_SQLSTATE_GENERAL_ERROR); + value = NULL; + value_len = 0; + goto cleanup; + } + break; + default: + value_len = strlen(column->valuestring); + value = calloc(1, value_len + 1); + strncpy(value, column->valuestring, value_len + 1); + break; + } + +cleanup: + *value_ptr = value; + *value_len_ptr = value_len; + return SF_STATUS_SUCCESS; +} + +SF_STATUS STDCALL snowflake_column_strlen(SF_STMT *stmt, int idx, size_t *value_ptr) { + SF_STATUS status; + cJSON *column = NULL; + + // Get column + if ((status = _snowflake_get_column(stmt, idx, &column)) != SF_STATUS_SUCCESS) { + return status; + } + + if (snowflake_cJSON_IsNull(column)) { + *value_ptr = 0; + } else { + *value_ptr = strlen(snowflake_cJSON_GetArrayItem(stmt->cur_row, idx - 1)->valuestring); + } + + return SF_STATUS_SUCCESS; +} + +SF_STATUS STDCALL snowflake_column_is_null(SF_STMT *stmt, int idx, sf_bool *value_ptr) { + SF_STATUS status; + cJSON *column = NULL; + + // Get column + if ((status = _snowflake_get_column(stmt, idx, &column)) != SF_STATUS_SUCCESS) { + return status; + } + + *value_ptr = snowflake_cJSON_IsNull(column) ? SF_BOOLEAN_TRUE : SF_BOOLEAN_FALSE; + + return SF_STATUS_SUCCESS; +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 0903f8cc03..11355894d0 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -34,7 +34,8 @@ SET(TESTS_C test_timestamp_tz test_timezone test_adjust_fetch_data - test_issue_76) + test_issue_76 + test_column_fetch) SET(TESTS_CXX test_unit_jwt diff --git a/tests/test_column_fetch.c b/tests/test_column_fetch.c new file mode 100644 index 0000000000..30f1c16c6b --- /dev/null +++ b/tests/test_column_fetch.c @@ -0,0 +1,993 @@ +/* + * Copyright (c) 2018 Snowflake Computing, Inc. All rights reserved. + */ +#include +#include "utils/test_setup.h" + +void test_column_as_boolean(void **unused) { + SF_STATUS status; + SF_CONNECT *sf = NULL; + SF_STMT *sfstmt = NULL; + + // Setup connection, run query, and get results back + setup_and_run_query(&sf, &sfstmt, "select 1, 0, 'some string', '', " + "to_boolean('yes'), to_boolean('no'), " + "0.000001, 0.0, NULL;"); + + // Stores the result from the fetch operation + sf_bool out; + + while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { + // Should be true + if (snowflake_column_as_boolean(sfstmt, 1, &out)) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(out, SF_BOOLEAN_TRUE); + + // Should be false + if (snowflake_column_as_boolean(sfstmt, 2, &out)) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(out, SF_BOOLEAN_FALSE); + + // Should be true + if (snowflake_column_as_boolean(sfstmt, 3, &out)) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(out, SF_BOOLEAN_TRUE); + + // Should be false + if (snowflake_column_as_boolean(sfstmt, 4, &out)) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(out, SF_BOOLEAN_FALSE); + + // Should be true + if (snowflake_column_as_boolean(sfstmt, 5, &out)) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(out, SF_BOOLEAN_TRUE); + + // Should be false + if (snowflake_column_as_boolean(sfstmt, 6, &out)) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(out, SF_BOOLEAN_FALSE); + + // Should be true + if (snowflake_column_as_boolean(sfstmt, 7, &out)) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(out, SF_BOOLEAN_TRUE); + + // Should be false + if (snowflake_column_as_boolean(sfstmt, 8, &out)) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(out, SF_BOOLEAN_FALSE); + + // Should be false + if (snowflake_column_as_boolean(sfstmt, 9, &out)) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(out, SF_BOOLEAN_FALSE); + + // Out of bounds column check + if (!(status = snowflake_column_as_boolean(sfstmt, 10, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_OUT_OF_BOUNDS); + + if (!(status = snowflake_column_as_boolean(sfstmt, -1, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_OUT_OF_BOUNDS); + } + + snowflake_stmt_term(sfstmt); + snowflake_term(sf); +} + +void test_column_as_int8(void **unused) { + SF_STATUS status; + SF_CONNECT *sf = NULL; + SF_STMT *sfstmt = NULL; + + // Setup connection, run query, and get results back + setup_and_run_query(&sf, &sfstmt, "select to_char('s'), '', NULL"); + + // Stores the result from the fetch operation + int8 out; + + while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { + // Basic Case with a non-empty string + if (snowflake_column_as_int8(sfstmt, 1, &out)) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(out, 115); + + // Case with an empty string + if (snowflake_column_as_int8(sfstmt, 2, &out)) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(out, 0); + + // Case with a null column + if (snowflake_column_as_int8(sfstmt, 3, &out)) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(out, 0); + + // Out of bounds check + if (!(status = snowflake_column_as_int8(sfstmt, 4, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_OUT_OF_BOUNDS); + + if (!(status = snowflake_column_as_int8(sfstmt, -1, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_OUT_OF_BOUNDS); + } + + snowflake_stmt_term(sfstmt); + snowflake_term(sf); +} + +void test_column_as_int32(void **unused) { + SF_STATUS status; + SF_CONNECT *sf = NULL; + SF_STMT *sfstmt = NULL; + + // Setup connection, run query, and get results back + setup_and_run_query(&sf, &sfstmt, "select 1, 0, 2147483647, -2147483648, " + "10.01, NULL, 'some string', 10000000000000000000, " + "-10000000000000000000"); + + // Stores the result from the fetch operation + int32 out; + + while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { + // Basic Case + if (snowflake_column_as_int32(sfstmt, 1, &out)) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(out, 1); + + // Case where valid conversion leads to a 0 + if (snowflake_column_as_int32(sfstmt, 2, &out)) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(out, 0); + + // Case where a valid conversion leads to the max value for an int64 + if ((status = snowflake_column_as_int32(sfstmt, 3, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_SUCCESS); + assert(out == SF_INT32_MAX); + + // Case where a valid conversion leads to the min value for an int64 + if ((status = snowflake_column_as_int32(sfstmt, 4, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_SUCCESS); + assert(out == SF_INT32_MIN); + + // Trying to convert a float to an int + if (snowflake_column_as_int32(sfstmt, 5, &out)) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(10, out); + + // Trying to convert a NULL to an int + if (snowflake_column_as_int32(sfstmt, 6, &out)) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(0, out); + + // ************************************************** + // Start of invalid conversions + // ************************************************** + + // Trying to convert non-numerical string to an int + if (!(status = snowflake_column_as_int32(sfstmt, 7, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_INVALID_CONVERSION); + + // Trying to convert a number that is out of range for int64 + if (!(status = snowflake_column_as_int32(sfstmt, 8, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_OUT_OF_RANGE); + + // Trying to convert a number that is out of range for int64 + if (!(status = snowflake_column_as_int32(sfstmt, 9, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_OUT_OF_RANGE); + + // Out of bounds check + if (!(status = snowflake_column_as_int32(sfstmt, 10, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_OUT_OF_BOUNDS); + + if (!(status = snowflake_column_as_int32(sfstmt, -1, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_OUT_OF_BOUNDS); + } + + snowflake_stmt_term(sfstmt); + snowflake_term(sf); +} + +void test_column_as_int64(void **unused) { + SF_STATUS status; + SF_CONNECT *sf = NULL; + SF_STMT *sfstmt = NULL; + + // Setup connection, run query, and get results back + setup_and_run_query(&sf, &sfstmt, "select 1, 0, 9223372036854775807, -9223372036854775808, " + "10.01, NULL, 'some string', 10000000000000000000, " + "-10000000000000000000"); + + // Stores the result from the fetch operation + int64 out; + + while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { + // Basic Case + if (snowflake_column_as_int64(sfstmt, 1, &out)) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(out, 1); + + // Case where valid conversion leads to a 0 + if (snowflake_column_as_int64(sfstmt, 2, &out)) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(out, 0); + + // Case where a valid conversion leads to the max value for an int64 + if ((status = snowflake_column_as_int64(sfstmt, 3, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_SUCCESS); + assert(out == SF_INT64_MAX); + + // Case where a valid conversion leads to the min value for an int64 + if ((status = snowflake_column_as_int64(sfstmt, 4, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_SUCCESS); + assert(out == SF_INT64_MIN); + + // Trying to convert a float to an int + if (snowflake_column_as_int64(sfstmt, 5, &out)) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(10, out); + + // Trying to convert a NULL to an int + if (snowflake_column_as_int64(sfstmt, 6, &out)) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(0, out); + + // ************************************************** + // Start of invalid conversions + // ************************************************** + + // Trying to convert non-numerical string to an int + if (!(status = snowflake_column_as_int64(sfstmt, 7, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_INVALID_CONVERSION); + + // Trying to convert a number that is out of range for int64 + if (!(status = snowflake_column_as_int64(sfstmt, 8, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_OUT_OF_RANGE); + + // Trying to convert a number that is out of range for int64 + if (!(status = snowflake_column_as_int64(sfstmt, 9, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_OUT_OF_RANGE); + + // Out of bounds check + if (!(status = snowflake_column_as_int64(sfstmt, 10, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_OUT_OF_BOUNDS); + + if (!(status = snowflake_column_as_int64(sfstmt, -1, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_OUT_OF_BOUNDS); + } + + snowflake_stmt_term(sfstmt); + snowflake_term(sf); +} + +void test_column_as_uint8(void **unused) { + SF_STATUS status; + SF_CONNECT *sf = NULL; + SF_STMT *sfstmt = NULL; + + // Setup connection, run query, and get results back + setup_and_run_query(&sf, &sfstmt, "select to_char('s'), '', NULL"); + + // Stores the result from the fetch operation + uint8 out; + + while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { + // Basic Case with a non-empty string + if (snowflake_column_as_uint8(sfstmt, 1, &out)) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(out, 115); + + // Case with an empty string + if (snowflake_column_as_uint8(sfstmt, 2, &out)) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(out, 0); + + // Case with a null column + if (snowflake_column_as_uint8(sfstmt, 3, &out)) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(out, 0); + + // Out of bounds check + if (!(status = snowflake_column_as_uint8(sfstmt, 4, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_OUT_OF_BOUNDS); + + if (!(status = snowflake_column_as_uint8(sfstmt, -1, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_OUT_OF_BOUNDS); + } + + snowflake_stmt_term(sfstmt); + snowflake_term(sf); +} + +void test_column_as_uint32(void **unused) { + SF_STATUS status; + SF_CONNECT *sf = NULL; + SF_STMT *sfstmt = NULL; + + // Setup connection, run query, and get results back + setup_and_run_query(&sf, &sfstmt, "select 1, 0, 4294967295, -1, 10.01, NULL, " + "'some string', 20000000000000000000"); + + // Stores the result from the fetch operation + uint32 out; + + while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { + // Basic Case + if (snowflake_column_as_uint32(sfstmt, 1, &out)) { + dump_error(&(sfstmt->error)); + } + assert(out == 1); + + // Case where valid conversion leads to a 0 + if (snowflake_column_as_uint32(sfstmt, 2, &out)) { + dump_error(&(sfstmt->error)); + } + assert(out == 0); + + // Case where a valid conversion leads to the max value for an int64 + if ((status = snowflake_column_as_uint32(sfstmt, 3, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_SUCCESS); + assert(out == SF_UINT32_MAX); + + // Trying to convert a negative number to an uint64 + if ((status = snowflake_column_as_uint32(sfstmt, 4, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_SUCCESS); + assert(out == SF_UINT32_MAX); + + // Trying to convert a float to an uint64 + if (snowflake_column_as_uint32(sfstmt, 5, &out)) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(10, out); + + // Trying to convert a NULL to an uint64 + if (snowflake_column_as_uint32(sfstmt, 6, &out)) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(0, out); + + // ************************************************** + // Start of invalid conversions + // ************************************************** + + // Trying to convert non-numerical string to an uint64 + if (!(status = snowflake_column_as_uint32(sfstmt, 7, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_INVALID_CONVERSION); + + // Trying to convert a number that is out of range for uint64 + if (!(status = snowflake_column_as_uint32(sfstmt, 8, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_OUT_OF_RANGE); + + // Out of bounds check + if (!(status = snowflake_column_as_uint32(sfstmt, 9, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_OUT_OF_BOUNDS); + + if (!(status = snowflake_column_as_uint32(sfstmt, -1, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_OUT_OF_BOUNDS); + } + + snowflake_stmt_term(sfstmt); + snowflake_term(sf); +} + +void test_column_as_uint64(void **unused) { + SF_STATUS status; + SF_CONNECT *sf = NULL; + SF_STMT *sfstmt = NULL; + + // Setup connection, run query, and get results back + setup_and_run_query(&sf, &sfstmt, "select 1, 0, 18446744073709551615, -1, 10.01, NULL, " + "'some string', 20000000000000000000"); + + // Stores the result from the fetch operation + uint64 out; + + while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { + // Basic Case + if (snowflake_column_as_uint64(sfstmt, 1, &out)) { + dump_error(&(sfstmt->error)); + } + assert(out == 1); + + // Case where valid conversion leads to a 0 + if (snowflake_column_as_uint64(sfstmt, 2, &out)) { + dump_error(&(sfstmt->error)); + } + assert(out == 0); + + // Case where a valid conversion leads to the max value for an int64 + if ((status = snowflake_column_as_uint64(sfstmt, 3, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_SUCCESS); + assert(out == SF_UINT64_MAX); + + // Trying to convert a negative number to an uint64 + if ((status = snowflake_column_as_uint64(sfstmt, 4, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_SUCCESS); + assert(out == SF_UINT64_MAX); + + // Trying to convert a float to an uint64 + if (snowflake_column_as_uint64(sfstmt, 5, &out)) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(10, out); + + // Trying to convert a NULL to an uint64 + if (snowflake_column_as_uint64(sfstmt, 6, &out)) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(0, out); + + // ************************************************** + // Start of invalid conversions + // ************************************************** + + // Trying to convert non-numerical string to an uint64 + if (!(status = snowflake_column_as_uint64(sfstmt, 7, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_INVALID_CONVERSION); + + // Trying to convert a number that is out of range for uint64 + if (!(status = snowflake_column_as_uint64(sfstmt, 8, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_OUT_OF_RANGE); + + // Out of bounds check + if (!(status = snowflake_column_as_uint64(sfstmt, 9, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_OUT_OF_BOUNDS); + + if (!(status = snowflake_column_as_uint64(sfstmt, -1, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_OUT_OF_BOUNDS); + } + + snowflake_stmt_term(sfstmt); + snowflake_term(sf); +} + +void test_column_as_float32(void **unused) { + SF_STATUS status; + SF_CONNECT *sf = NULL; + SF_STMT *sfstmt = NULL; + + // Setup connection, run query, and get results back + setup_and_run_query(&sf, &sfstmt, "select 1, 0, 1.1, as_double(3.40282e+38)," + "as_double(1.176e-38), NULL, 'some string', " + "2e500"); + + // Stores the result from the fetch operation + float32 out; + + while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { + // Basic Case to convert an int + if (snowflake_column_as_float32(sfstmt, 1, &out)) { + dump_error(&(sfstmt->error)); + } + assert(out == 1.0); + + // Case where valid conversion leads to a 0 + if (snowflake_column_as_float32(sfstmt, 2, &out)) { + dump_error(&(sfstmt->error)); + } + assert(out == 0.0); + + // Case where string to float conversion is not perfect + if (snowflake_column_as_float32(sfstmt, 3, &out)) { + dump_error(&(sfstmt->error)); + } + assert(out == (float32) 1.1); + + // Case where a valid conversion leads to the max value for a float64 + if ((status = snowflake_column_as_float32(sfstmt, 4, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_SUCCESS); + + // Case where a valid conversion leads to the min positive value for a float64 + if ((status = snowflake_column_as_float32(sfstmt, 5, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_SUCCESS); + + // Case where a valid conversion leads to the min positive value for a float64 + if ((status = snowflake_column_as_float32(sfstmt, 6, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(out, 0.0); + + // ************************************************** + // Start of invalid conversions + // ************************************************** + + // Trying to convert non-numerical string to an uint64 + if (!(status = snowflake_column_as_float32(sfstmt, 7, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_INVALID_CONVERSION); + + // Trying to convert a number that is out of range for float32 + if (!(status = snowflake_column_as_float32(sfstmt, 8, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_OUT_OF_RANGE); + + // Out of bounds check + if (!(status = snowflake_column_as_float32(sfstmt, 9, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_OUT_OF_BOUNDS); + + if (!(status = snowflake_column_as_float32(sfstmt, -1, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_OUT_OF_BOUNDS); + } + + snowflake_stmt_term(sfstmt); + snowflake_term(sf); +} + +void test_column_as_float64(void **unused) { + SF_STATUS status; + SF_CONNECT *sf = NULL; + SF_STMT *sfstmt = NULL; + + // Setup connection, run query, and get results back + setup_and_run_query(&sf, &sfstmt, "select 1, 0, 1.1, as_double(1.79769e+308)," + "as_double(2.225074e-308), NULL, 'some string', " + "2e500"); + + // Stores the result from the fetch operation + float64 out; + + while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { + // Basic Case to convert an int + if (snowflake_column_as_float64(sfstmt, 1, &out)) { + dump_error(&(sfstmt->error)); + } + assert(out == 1.0); + + // Case where valid conversion leads to a 0 + if (snowflake_column_as_float64(sfstmt, 2, &out)) { + dump_error(&(sfstmt->error)); + } + assert(out == 0.0); + + // Basic Case + if (snowflake_column_as_float64(sfstmt, 3, &out)) { + dump_error(&(sfstmt->error)); + } + assert(out == 1.1); + + // Case where a valid conversion leads to the max value for a float64 + if ((status = snowflake_column_as_float64(sfstmt, 4, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_SUCCESS); + + // Case where a valid conversion leads to the min positive value for a float64 + if ((status = snowflake_column_as_float64(sfstmt, 5, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_SUCCESS); + + // Case where a valid conversion leads to the min positive value for a float64 + if ((status = snowflake_column_as_float64(sfstmt, 6, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(out, 0.0); + + // ************************************************** + // Start of invalid conversions + // ************************************************** + + // Trying to convert non-numerical string to an uint64 + if (!(status = snowflake_column_as_float64(sfstmt, 7, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_INVALID_CONVERSION); + + // Trying to convert a number that is out of range for float64 + if (!(status = snowflake_column_as_float64(sfstmt, 8, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_OUT_OF_RANGE); + + // Out of bounds check + if (!(status = snowflake_column_as_float64(sfstmt, 9, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_OUT_OF_BOUNDS); + + if (!(status = snowflake_column_as_float64(sfstmt, -1, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_OUT_OF_BOUNDS); + } + + snowflake_stmt_term(sfstmt); + snowflake_term(sf); +} + +void test_column_as_const_str(void **unused) { + SF_STATUS status; + SF_CONNECT *sf = NULL; + SF_STMT *sfstmt = NULL; + + // Setup connection, run query, and get results back + setup_and_run_query(&sf, &sfstmt, "select 'some string that is not empty', '', " + "as_double(1.1), as_integer(to_variant(10)), " + "to_boolean(1), NULL"); + + // Stores the result from the fetch operation + const char *out; + + while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { + // Basic Case + if (snowflake_column_as_const_str(sfstmt, 1, &out)) { + dump_error(&(sfstmt->error)); + } + assert_string_equal("some string that is not empty", out); + + // Case where string is empty + if (snowflake_column_as_const_str(sfstmt, 2, &out)) { + dump_error(&(sfstmt->error)); + } + assert_string_equal("", out); + + // Case where a valid conversion leads to the max value for an int64 + if (snowflake_column_as_const_str(sfstmt, 3, &out)) { + dump_error(&(sfstmt->error)); + } + assert_string_equal("1.1", out); + + // Trying to convert non-numerical string to an uint64 + if (snowflake_column_as_const_str(sfstmt, 4, &out)) { + dump_error(&(sfstmt->error)); + } + assert_string_equal("10", out); + + // Trying to convert a float to an uint64 + if (snowflake_column_as_const_str(sfstmt, 5, &out)) { + dump_error(&(sfstmt->error)); + } + assert_string_equal("1", out); + + // Get the value of a NULL column + if (snowflake_column_as_const_str(sfstmt, 6, &out)) { + dump_error(&(sfstmt->error)); + } + assert(out == NULL); + + // Out of bounds check + if (!(status = snowflake_column_as_const_str(sfstmt, 7, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_OUT_OF_BOUNDS); + + if (!(status = snowflake_column_as_const_str(sfstmt, -1, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_OUT_OF_BOUNDS); + } + + snowflake_stmt_term(sfstmt); + snowflake_term(sf); +} + +void test_column_is_null(void **unused) { + SF_STATUS status; + SF_CONNECT *sf = NULL; + SF_STMT *sfstmt = NULL; + + // Setup connection, run query, and get results back + setup_and_run_query(&sf, &sfstmt, "select 1, NULL, to_boolean(0)"); + + // Stores the result from the fetch operation + sf_bool out; + + while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { + // Basic Case (not null) + if (snowflake_column_is_null(sfstmt, 1, &out)) { + dump_error(&(sfstmt->error)); + } + assert_false(out); + + // Case where column is null + if (snowflake_column_is_null(sfstmt, 2, &out)) { + dump_error(&(sfstmt->error)); + } + assert_true(out); + + // Case where a valid conversion leads to the max value for an int64 + if (snowflake_column_is_null(sfstmt, 3, &out)) { + dump_error(&(sfstmt->error)); + } + assert_false(out); + + // Out of bounds check + if (!(status = snowflake_column_is_null(sfstmt, 4, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_OUT_OF_BOUNDS); + + if (!(status = snowflake_column_is_null(sfstmt, -1, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_OUT_OF_BOUNDS); + } + + snowflake_stmt_term(sfstmt); + snowflake_term(sf); +} + +void test_column_strlen(void **unused) { + SF_STATUS status; + SF_CONNECT *sf = NULL; + SF_STMT *sfstmt = NULL; + + // Setup connection, run query, and get results back + setup_and_run_query(&sf, &sfstmt, "select 'some string', '', NULL"); + + // Stores the result from the fetch operation + size_t out; + + while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { + // Basic Case with a non-empty string + if (snowflake_column_strlen(sfstmt, 1, &out)) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(out, 11); + + // Case with an empty string + if (snowflake_column_strlen(sfstmt, 2, &out)) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(out, 0); + + // Case with a null column + if (snowflake_column_strlen(sfstmt, 3, &out)) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(out, 0); + + // Out of bounds check + if (!(status = snowflake_column_strlen(sfstmt, 4, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_OUT_OF_BOUNDS); + + if (!(status = snowflake_column_strlen(sfstmt, -1, &out))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_OUT_OF_BOUNDS); + } + + snowflake_stmt_term(sfstmt); + snowflake_term(sf); +} + +void test_column_as_str(void **unused) { + SF_STATUS status; + SF_CONNECT *sf = NULL; + SF_STMT *sfstmt = NULL; + + // Setup connection, run query, and get results back + setup_and_run_query(&sf, &sfstmt, "select 'some string', '', NULL, " + "to_boolean(0), to_boolean(1), " + "date_from_parts(2018, 09, 14), " + "timestamp_ltz_from_parts(2014, 03, 20, 15, 30, 45, 493679329), " + "timestamp_ntz_from_parts(2014, 03, 20, 15, 30, 45, 493679329), " + "timestamp_tz_from_parts(2014, 03, 20, 15, 30, 45, 493679329, 'America/Los_Angeles')"); + + // Stores the result from the fetch operation + char *out = NULL; + size_t out_len = 0; + + while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { + // Basic Case with a non-empty string + if (snowflake_column_as_str(sfstmt, 1, &out, &out_len)) { + dump_error(&(sfstmt->error)); + } + assert_string_equal("some string", out); + assert_int_equal(11, out_len); + free(out); + out = NULL; + out_len = 0; + + // Case with an empty string + if (snowflake_column_as_str(sfstmt, 2, &out, &out_len)) { + dump_error(&(sfstmt->error)); + } + assert_string_equal("", out); + assert_int_equal(0, out_len); + free(out); + out = NULL; + out_len = 0; + + // Case with a NULL value + if (snowflake_column_as_str(sfstmt, 3, &out, &out_len)) { + dump_error(&(sfstmt->error)); + } + // No free occurs because no memory was allocated to the 'out' variable + assert(out == NULL); + assert_int_equal(0, out_len); + + // Case converting a boolean false + if (snowflake_column_as_str(sfstmt, 4, &out, &out_len)) { + dump_error(&(sfstmt->error)); + } + assert_string_equal("", out); + assert_int_equal(0, out_len); + free(out); + out = NULL; + out_len = 0; + + // Case converting a boolean true + if (snowflake_column_as_str(sfstmt, 5, &out, &out_len)) { + dump_error(&(sfstmt->error)); + } + assert_string_equal("1", out); + assert_int_equal(1, out_len); + free(out); + out = NULL; + out_len = 0; + + // Case with a date type + if (snowflake_column_as_str(sfstmt, 6, &out, &out_len)) { + dump_error(&(sfstmt->error)); + } + assert_string_equal("2018-09-14", out); + assert_int_equal(10, out_len); + free(out); + out = NULL; + out_len = 0; + + // Case with a TIMESTAMP_LTZ type + if (snowflake_column_as_str(sfstmt, 7, &out, &out_len)) { + dump_error(&(sfstmt->error)); + } + assert_string_equal("2014-03-20 15:30:45.493679329", out); + assert_int_equal(29, out_len); + free(out); + out = NULL; + out_len = 0; + + // Case with a TIMESTAMP_NTZ type + if (snowflake_column_as_str(sfstmt, 8, &out, &out_len)) { + dump_error(&(sfstmt->error)); + } + assert_string_equal("2014-03-20 15:30:45.493679329", out); + assert_int_equal(29, out_len); + free(out); + out = NULL; + out_len = 0; + + // Case with a TIMESTAMP_TZ type + if (snowflake_column_as_str(sfstmt, 9, &out, &out_len)) { + dump_error(&(sfstmt->error)); + } + assert_string_equal("2014-03-20 15:30:45.493679329 -07:00", out); + assert_int_equal(36, out_len); + free(out); + out = NULL; + out_len = 0; + + // Out of bounds check + if (!(status = snowflake_column_as_str(sfstmt, 10, &out, &out_len))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_OUT_OF_BOUNDS); + + if (!(status = snowflake_column_as_str(sfstmt, -1, &out, &out_len))) { + dump_error(&(sfstmt->error)); + } + assert_int_equal(status, SF_STATUS_ERROR_OUT_OF_BOUNDS); + } + + snowflake_stmt_term(sfstmt); + snowflake_term(sf); +} + +int main(void) { + initialize_test(SF_BOOLEAN_FALSE); + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_column_as_boolean), + cmocka_unit_test(test_column_as_int8), + cmocka_unit_test(test_column_as_int32), + cmocka_unit_test(test_column_as_int64), + cmocka_unit_test(test_column_as_uint8), + cmocka_unit_test(test_column_as_uint32), + cmocka_unit_test(test_column_as_uint64), + cmocka_unit_test(test_column_as_float32), + cmocka_unit_test(test_column_as_float64), + cmocka_unit_test(test_column_as_const_str), + cmocka_unit_test(test_column_is_null), + cmocka_unit_test(test_column_strlen), + cmocka_unit_test(test_column_as_str), + }; + int ret = cmocka_run_group_tests(tests, NULL, NULL); + snowflake_global_term(); + return ret; +} diff --git a/tests/test_perf_column_evaluation.c b/tests/test_perf_column_evaluation.c index f0ed01fb22..c1502f2f2f 100644 --- a/tests/test_perf_column_evaluation.c +++ b/tests/test_perf_column_evaluation.c @@ -18,7 +18,7 @@ void test_eval_all_cols(void **unused) { FILE *dev_null = fopen("/dev/null", "w"); // Setup connection, run query, and get results back - col_conv_setup(&sf, &sfstmt, COL_EVAL_QUERY); + setup_and_run_query(&sf, &sfstmt, COL_EVAL_QUERY); clock_gettime(clk_id, &begin); @@ -56,7 +56,7 @@ void test_eval_half_cols(void **unused) { FILE *dev_null = fopen("/dev/null", "w"); // Setup connection, run query, and get results back - col_conv_setup(&sf, &sfstmt, COL_EVAL_QUERY); + setup_and_run_query(&sf, &sfstmt, COL_EVAL_QUERY); clock_gettime(clk_id, &begin); @@ -98,7 +98,7 @@ void test_skip_rows_half(void **unused) { FILE *dev_null = fopen("/dev/null", "w"); // Setup connection, run query, and get results back - col_conv_setup(&sf, &sfstmt, COL_EVAL_QUERY); + setup_and_run_query(&sf, &sfstmt, COL_EVAL_QUERY); clock_gettime(clk_id, &begin); @@ -138,7 +138,7 @@ void test_skip_all_rows(void **unused) { FILE *dev_null = fopen("/dev/null", "w"); // Setup connection, run query, and get results back - col_conv_setup(&sf, &sfstmt, COL_EVAL_QUERY); + setup_and_run_query(&sf, &sfstmt, COL_EVAL_QUERY); clock_gettime(clk_id, &begin); @@ -174,7 +174,7 @@ void test_skip_all_rows_no_bind(void **unused) { FILE *dev_null = fopen("/dev/null", "w"); // Setup connection, run query, and get results back - col_conv_setup(&sf, &sfstmt, COL_EVAL_QUERY); + setup_and_run_query(&sf, &sfstmt, COL_EVAL_QUERY); clock_gettime(clk_id, &begin); diff --git a/tests/test_perf_string_reads_and_writes.c b/tests/test_perf_string_reads_and_writes.c index bf52447571..1111a54983 100644 --- a/tests/test_perf_string_reads_and_writes.c +++ b/tests/test_perf_string_reads_and_writes.c @@ -29,7 +29,7 @@ void test_col_string_read_fixed_size(void **unused) { struct timespec begin, end; clockid_t clk_id = CLOCK_MONOTONIC; - col_conv_setup(&sf, &sfstmt, COL_EVAL_QUERY); + setup_and_run_query(&sf, &sfstmt, COL_EVAL_QUERY); // Begin timing clock_gettime(clk_id, &begin); @@ -65,7 +65,7 @@ void test_col_string_manipulate_fixed_size(void **unused) { struct timespec begin, end; clockid_t clk_id = CLOCK_MONOTONIC; - col_conv_setup(&sf, &sfstmt, COL_EVAL_QUERY); + setup_and_run_query(&sf, &sfstmt, COL_EVAL_QUERY); // Begin timing clock_gettime(clk_id, &begin); @@ -105,7 +105,8 @@ void test_col_buffer_copy_unknown_size_dynamic_memory(void **unused) { clockid_t clk_id = CLOCK_MONOTONIC; // Randomly select string size based on provided gen seed - col_conv_setup(&sf, &sfstmt, "select randstr(uniform(1, 1048575, 8888),random()) from table(generator(rowcount=>500));"); + setup_and_run_query(&sf, &sfstmt, + "select randstr(uniform(1, 1048575, 8888),random()) from table(generator(rowcount=>500));"); // Begin timing clock_gettime(clk_id, &begin); @@ -158,7 +159,7 @@ void test_col_buffer_copy_concat_multiple_rows(void **unused) { } // Get all public domain books. Sort by text_part_id to ensure that you concatenate book in right order - col_conv_setup(&sf, &sfstmt, "select * from public_domain_books order by id, text_part;"); + setup_and_run_query(&sf, &sfstmt, "select * from public_domain_books order by id, text_part;"); // Begin timing clock_gettime(clk_id, &begin); diff --git a/tests/test_perf_type_conversion.c b/tests/test_perf_type_conversion.c index 232feb175f..3fb1f7ef29 100644 --- a/tests/test_perf_type_conversion.c +++ b/tests/test_perf_type_conversion.c @@ -15,7 +15,7 @@ void test_col_conv_int64_type(void **unused) { clockid_t clk_id = CLOCK_MONOTONIC; // Setup connection, run query, and get results back - col_conv_setup(&sf, &sfstmt, "select seq4() from table(generator(rowcount=>12000));"); + setup_and_run_query(&sf, &sfstmt, "select seq4() from table(generator(rowcount=>12000));"); clock_gettime(clk_id, &begin); @@ -45,7 +45,7 @@ void test_col_conv_float64_type(void **unused) { struct timespec begin, end; clockid_t clk_id = CLOCK_MONOTONIC; - col_conv_setup(&sf, &sfstmt, "select as_double(10.01) from table(generator(rowcount=>12000));"); + setup_and_run_query(&sf, &sfstmt, "select as_double(10.01) from table(generator(rowcount=>12000));"); clock_gettime(clk_id, &begin); @@ -75,7 +75,7 @@ void test_col_conv_str_type(void **unused) { struct timespec begin, end; clockid_t clk_id = CLOCK_MONOTONIC; - col_conv_setup(&sf, &sfstmt, "select randstr(999,random()) from table(generator(rowcount=>4000));"); + setup_and_run_query(&sf, &sfstmt, "select randstr(999,random()) from table(generator(rowcount=>4000));"); // Begin timing clock_gettime(clk_id, &begin); @@ -106,7 +106,8 @@ void test_col_conv_timestamp_type(void **unused) { struct timespec begin, end; clockid_t clk_id = CLOCK_MONOTONIC; - col_conv_setup(&sf, &sfstmt, "select dateadd(day, uniform(1, 500, random()), current_timestamp) from table(generator(rowcount=>4000));"); + setup_and_run_query(&sf, &sfstmt, + "select dateadd(day, uniform(1, 500, random()), current_timestamp) from table(generator(rowcount=>4000));"); // Begin timing clock_gettime(clk_id, &begin); @@ -137,7 +138,7 @@ void test_col_conv_multi_type(void **unused) { struct timespec begin, end; clockid_t clk_id = CLOCK_MONOTONIC; - col_conv_setup(&sf, &sfstmt, "select seq4() from table(generator(rowcount=>12000));"); + setup_and_run_query(&sf, &sfstmt, "select seq4() from table(generator(rowcount=>12000));"); // Begin timing clock_gettime(clk_id, &begin); @@ -185,7 +186,7 @@ void test_col_conv_multi_types_per_row(void **unused) { struct timespec begin, end; clockid_t clk_id = CLOCK_MONOTONIC; - col_conv_setup(&sf, &sfstmt, "select seq4() from table(generator(rowcount=>12000));"); + setup_and_run_query(&sf, &sfstmt, "select seq4() from table(generator(rowcount=>12000));"); clock_gettime(clk_id, &begin); diff --git a/tests/utils/test_setup.c b/tests/utils/test_setup.c index b8532ab80c..504e67acde 100644 --- a/tests/utils/test_setup.c +++ b/tests/utils/test_setup.c @@ -79,7 +79,7 @@ void dump_error(SF_ERROR_STRUCT *error) { error->line); } -void col_conv_setup(SF_CONNECT **sfp, SF_STMT **sfstmtp, const char* query) { +void setup_and_run_query(SF_CONNECT **sfp, SF_STMT **sfstmtp, const char *query) { SF_STATUS status; SF_CONNECT *sf; SF_STMT *sfstmt; diff --git a/tests/utils/test_setup.h b/tests/utils/test_setup.h index 5c3066045b..46fceacffb 100644 --- a/tests/utils/test_setup.h +++ b/tests/utils/test_setup.h @@ -27,7 +27,7 @@ SF_CONNECT *setup_snowflake_connection(); SF_CONNECT *setup_snowflake_connection_with_autocommit( const char *timezone, sf_bool autocommit); -void col_conv_setup(SF_CONNECT **sfp, SF_STMT **sfstmtp, const char* query); +void setup_and_run_query(SF_CONNECT **sfp, SF_STMT **sfstmtp, const char *query); void process_results(struct timespec begin, struct timespec end, int num_iterations, const char *label); From a4a73453621a4e97ccead8b73a954a091d764f43 Mon Sep 17 00:00:00 2001 From: notkriswagner Date: Sat, 15 Sep 2018 21:15:28 -0700 Subject: [PATCH 02/16] Updated int32 reference to an int64 to satisfy call to curl_easy_getinfo() --- lib/connection.c | 4 ++-- lib/connection.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/connection.c b/lib/connection.c index dcf83f83b7..6e793f892d 100644 --- a/lib/connection.c +++ b/lib/connection.c @@ -746,7 +746,7 @@ sf_bool STDCALL http_perform(CURL *curl, CURLcode res; sf_bool ret = SF_BOOLEAN_FALSE; sf_bool retry = SF_BOOLEAN_FALSE; - int32 http_code = 0; + int64 http_code = 0; DECORRELATE_JITTER_BACKOFF djb = { 1, //base 16 //cap @@ -941,7 +941,7 @@ sf_bool STDCALL http_perform(CURL *curl, return ret; } -sf_bool STDCALL is_retryable_http_code(int32 code) { +sf_bool STDCALL is_retryable_http_code(int64 code) { return ((code >= 500 && code < 600) || code == 400 || code == 403 || code == 408) ? SF_BOOLEAN_TRUE : SF_BOOLEAN_FALSE; } diff --git a/lib/connection.h b/lib/connection.h index 17ca734214..0d96ff66da 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -379,7 +379,7 @@ sf_bool STDCALL http_perform(CURL *curl, SF_REQUEST_TYPE request_type, char *url * @param code The HTTP code to test. * @return Retryable/Non-retryable. 1 = Retryable; 0 = Non-retryable */ -sf_bool STDCALL is_retryable_http_code(int32 code); +sf_bool STDCALL is_retryable_http_code(int64 code); /** * Renews a session once the session token has expired. From c58d70da15fabfdc2ad09a70dcd49385afe12e11 Mon Sep 17 00:00:00 2001 From: notkriswagner Date: Sat, 15 Sep 2018 21:20:33 -0700 Subject: [PATCH 03/16] Second try at fixing curl warning. --- lib/connection.c | 4 ++-- lib/connection.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/connection.c b/lib/connection.c index 6e793f892d..60caf0197d 100644 --- a/lib/connection.c +++ b/lib/connection.c @@ -746,7 +746,7 @@ sf_bool STDCALL http_perform(CURL *curl, CURLcode res; sf_bool ret = SF_BOOLEAN_FALSE; sf_bool retry = SF_BOOLEAN_FALSE; - int64 http_code = 0; + long int http_code = 0; DECORRELATE_JITTER_BACKOFF djb = { 1, //base 16 //cap @@ -941,7 +941,7 @@ sf_bool STDCALL http_perform(CURL *curl, return ret; } -sf_bool STDCALL is_retryable_http_code(int64 code) { +sf_bool STDCALL is_retryable_http_code(long int code) { return ((code >= 500 && code < 600) || code == 400 || code == 403 || code == 408) ? SF_BOOLEAN_TRUE : SF_BOOLEAN_FALSE; } diff --git a/lib/connection.h b/lib/connection.h index 0d96ff66da..e1d31ba489 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -379,7 +379,7 @@ sf_bool STDCALL http_perform(CURL *curl, SF_REQUEST_TYPE request_type, char *url * @param code The HTTP code to test. * @return Retryable/Non-retryable. 1 = Retryable; 0 = Non-retryable */ -sf_bool STDCALL is_retryable_http_code(int64 code); +sf_bool STDCALL is_retryable_http_code(long int code); /** * Renews a session once the session token has expired. From a336ea8f7d2ca2416ddc5c3de6a4f96b83ecba53 Mon Sep 17 00:00:00 2001 From: notkriswagner Date: Sat, 15 Sep 2018 21:47:56 -0700 Subject: [PATCH 04/16] Fixed memory leak from not freeing cur_row in SF_STMT struct. --- lib/client.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/client.c b/lib/client.c index 3af3b17ef0..0a1a8cc265 100644 --- a/lib/client.c +++ b/lib/client.c @@ -1166,6 +1166,12 @@ static void STDCALL _snowflake_stmt_reset(SF_STMT *sfstmt) { } sfstmt->sql_text = NULL; + if (sfstmt->cur_row) { + snowflake_cJSON_Delete(sfstmt->cur_row); + sfstmt->cur_row = NULL; + } + sfstmt->cur_row = NULL; + if (sfstmt->raw_results) { snowflake_cJSON_Delete(sfstmt->raw_results); sfstmt->raw_results = NULL; From 164552861313a7bf25f45b5f1e0caa696fc93086 Mon Sep 17 00:00:00 2001 From: notkriswagner Date: Sat, 15 Sep 2018 21:54:41 -0700 Subject: [PATCH 05/16] In int32 conversion function, replaced SF_INT64_{MIN,MAX} with LONG_{MIN,MAX} to satisfy differences between strtol behavior on Windows and Unix systems. --- lib/client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/client.c b/lib/client.c index 0a1a8cc265..68089de7af 100644 --- a/lib/client.c +++ b/lib/client.c @@ -2332,7 +2332,7 @@ SF_STATUS STDCALL snowflake_column_as_int32(SF_STMT *stmt, int idx, int32 *value if (endptr == column->valuestring) { return SF_STATUS_ERROR_INVALID_CONVERSION; } - if (((value == SF_INT64_MAX || value == SF_INT64_MIN) && errno == ERANGE) || (value > SF_INT32_MAX || value < SF_INT32_MIN)) { + if (((value == LONG_MAX || value == LONG_MIN) && errno == ERANGE) || (value > SF_INT32_MAX || value < SF_INT32_MIN)) { return SF_STATUS_ERROR_OUT_OF_RANGE; } // Everything checks out, set value and return success From 829ae18edb0fc45c530684921cc7af7c37f16e53 Mon Sep 17 00:00:00 2001 From: notkriswagner Date: Sat, 15 Sep 2018 23:23:42 -0700 Subject: [PATCH 06/16] Removed obsolete test. Added check to see if cur_row is NULL before freeing. --- tests/test_bind_results.c | 78 --------------------------------------- 1 file changed, 78 deletions(-) delete mode 100644 tests/test_bind_results.c diff --git a/tests/test_bind_results.c b/tests/test_bind_results.c deleted file mode 100644 index 13c6b7ed07..0000000000 --- a/tests/test_bind_results.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2018 Snowflake Computing, Inc. All rights reserved. - */ - -#include -#include "utils/test_setup.h" - -void test_bind_results(void **unused) { - /* init */ - SF_BIND_OUTPUT results[2]; - int64 result1; - char result2[1000]; - - /* Connect with all parameters set */ - SF_CONNECT *sf = setup_snowflake_connection(); - SF_STATUS status = snowflake_connect(sf); - if (status != SF_STATUS_SUCCESS) { - dump_error(&(sf->error)); - } - assert_int_equal(status, SF_STATUS_SUCCESS); - - SF_STMT *sfstmt = snowflake_stmt(sf); - /* NOTE: the numeric type here should fit into int64 otherwise - * it is taken as a float */ - status = snowflake_query( - sfstmt, - "select seq4(),randstr(999,random()) from table(generator(rowcount=>10))", - 0 - ); - if (status != SF_STATUS_SUCCESS) { - dump_error(&(sfstmt->error)); - } - assert_int_equal(status, SF_STATUS_SUCCESS); - - // Initialize bind outputs - results[0].idx = 1; - results[0].max_length = sizeof(result1); - results[0].c_type = SF_C_TYPE_INT64; - results[0].value = &result1; - - results[1].idx = 2; - results[1].max_length = sizeof(result2); - results[1].c_type = SF_C_TYPE_STRING; - results[1].value = &result2; - - // Bind array of outputs - status = snowflake_bind_result_array(sfstmt, results, 2); - if (status != SF_STATUS_SUCCESS) { - dump_error(&(sfstmt->error)); - } - assert_int_equal(status, SF_STATUS_SUCCESS); - - int counter = 0; - while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { - assert_int_equal(result1, counter); - assert_int_equal(strlen(result2), 999); - // printf("column 1: %lld, column 2: %s\n", result1, result2); - ++counter; - } - assert_int_equal(counter, 10); - if (status != SF_STATUS_EOF) { - dump_error(&(sfstmt->error)); - } - assert_int_equal(status, SF_STATUS_EOF); - snowflake_stmt_term(sfstmt); - snowflake_term(sf); - -} - -int main(void) { - initialize_test(SF_BOOLEAN_FALSE); - const struct CMUnitTest tests[] = { - cmocka_unit_test(test_bind_results), - }; - int ret = cmocka_run_group_tests(tests, NULL, NULL); - snowflake_global_term(); - return ret; -} From 01072f5a7cacaca69b285f605ecb8e40fa4f7c49 Mon Sep 17 00:00:00 2001 From: notkriswagner Date: Sat, 15 Sep 2018 23:59:02 -0700 Subject: [PATCH 07/16] Removed SF_BIND_OUTPUT struct along with output binding functions. Removed results field from SF_STMT. Updated test_adjust_fetch_data to work with new API. --- include/snowflake/Column.hpp | 1 - include/snowflake/client.h | 38 +--- lib/client.c | 319 +-------------------------------- tests/test_adjust_fetch_data.c | 18 +- 4 files changed, 20 insertions(+), 356 deletions(-) diff --git a/include/snowflake/Column.hpp b/include/snowflake/Column.hpp index 351282e466..f534dfa99b 100644 --- a/include/snowflake/Column.hpp +++ b/include/snowflake/Column.hpp @@ -61,7 +61,6 @@ namespace Snowflake { const char *asCString(); private: - SF_BIND_OUTPUT m_column; SF_COLUMN_DESC *m_desc; }; } diff --git a/include/snowflake/client.h b/include/snowflake/client.h index 611e3caad2..8d40a7a006 100644 --- a/include/snowflake/client.h +++ b/include/snowflake/client.h @@ -332,7 +332,6 @@ typedef struct SF_STMT { int64 total_fieldcount; int64 total_row_index; void *params; - void *results; SF_COLUMN_DESC *desc; void *stmt_attrs; sf_bool is_dml; @@ -360,14 +359,14 @@ typedef struct { /** * Bind output parameter context */ -typedef struct { - size_t idx; /* One based index of the columns */ - SF_C_TYPE c_type; /* expected data type in C */ - size_t max_length; /* maximum buffer size provided by application */ - void *value; /* input and output: buffer to stoare a value */ - size_t len; /* output: actual value length */ - sf_bool is_null; /* output: SF_BOOLEAN_TRUE if is null else SF_BOOLEAN_FALSE */ -} SF_BIND_OUTPUT; +//typedef struct { +// size_t idx; /* One based index of the columns */ +// SF_C_TYPE c_type; /* expected data type in C */ +// size_t max_length; /* maximum buffer size provided by application */ +// void *value; /* input and output: buffer to stoare a value */ +// size_t len; /* output: actual value length */ +// sf_bool is_null; /* output: SF_BOOLEAN_TRUE if is null else SF_BOOLEAN_FALSE */ +//} SF_BIND_OUTPUT; /** @@ -645,27 +644,6 @@ SF_STATUS STDCALL snowflake_bind_param( SF_STATUS snowflake_bind_param_array( SF_STMT *sfstmt, SF_BIND_INPUT *sfbind_array, size_t size); -/** - * Binds buffers with the statement for result set processing. - * - * @param sfstmt SNOWFLAKE_STMT context. - * @param sfbind_array SNOWFLAKE_BIND_OUTPUT context array. - * @return 0 if success, otherwise an errno is returned. - */ -SF_STATUS STDCALL snowflake_bind_result( - SF_STMT *sfstmt, SF_BIND_OUTPUT *sfbind); - -/** - * Binds an array of buffers with the statement for result set processing. - * - * @param sfstmt SF_STMT context. - * @param sfbind_array SF_BIND_OUTPUT array of result buffers. - * @param size size_t size of the result buffer array (sfbind_array). - * @return 0 if success, otherwise an errno is returned. - */ -SF_STATUS snowflake_bind_result_array( - SF_STMT *sfstmt, SF_BIND_OUTPUT *sfbind_array, size_t size); - /** * Returns a query id associated with the statement after execution. If not * executed, NULL is returned. diff --git a/lib/client.c b/lib/client.c index 68089de7af..c5816988b4 100644 --- a/lib/client.c +++ b/lib/client.c @@ -151,115 +151,6 @@ static void log_lock_func(void *udata, int lock) { } } -/** - * Extracts a Snowflake internal representation of timestamp into - * seconds, nanoseconds and optionally Timezone offset. - * @param sec pointer of seconds - * @param nsec pointer of nanoseconds - * @param tzoffset pointer of Timezone offset. - * @param src source buffer including a Snowflake internal timestamp - * @param timezone Timezone for TIMESTAMP_LTZ - * @param scale Timestamp data type scale between 0 and 9 - * @return SF_BOOLEAN_TRUE if success otherwise SF_BOOLEAN_FALSE - */ -static sf_bool _extract_timestamp( - SF_BIND_OUTPUT *result, SF_DB_TYPE sftype, - const char *src, const char *timezone, int64 scale) { - time_t nsec = 0L; - time_t sec = 0L; - int64 tzoffset = 0; - struct tm tm_obj; - struct tm *tm_ptr = NULL; - char tzname[64]; - char *tzptr = (char *) timezone; - - memset(&tm_obj, 0, sizeof(tm_obj)); - - /* Search for a decimal point */ - char *ptr = strchr(src, (int) '.'); - if (ptr == NULL) { - return SF_BOOLEAN_FALSE; - } - sec = strtoll(src, NULL, 10); - - /* Search for a space for TIMESTAMP_TZ */ - char *sptr = strchr(ptr + 1, (int) ' '); - nsec = strtoll(ptr + 1, NULL, 10); - if (sptr != NULL) { - /* TIMESTAMP_TZ */ - nsec = strtoll(ptr + 1, NULL, 10); - tzoffset = strtoll(sptr + 1, NULL, 10) - TIMEZONE_OFFSET_RANGE; - } - if (sec < 0 && nsec > 0) { - nsec = pow10_int64[scale] - nsec; - sec--; - } - log_info("sec: %lld, nsec: %lld", sec, nsec); - - if (sftype == SF_DB_TYPE_TIMESTAMP_TZ) { - /* make up Timezone name from the tzoffset */ - ldiv_t dm = ldiv((long) tzoffset, 60L); - sprintf(tzname, "UTC%c%02ld:%02ld", - dm.quot > 0 ? '+' : '-', labs(dm.quot), labs(dm.rem)); - tzptr = tzname; - } - - /* replace a dot character with NULL */ - if (sftype == SF_DB_TYPE_TIMESTAMP_NTZ || - sftype == SF_DB_TYPE_TIME) { - tm_ptr = sf_gmtime(&sec, &tm_obj); - } else if (sftype == SF_DB_TYPE_TIMESTAMP_LTZ || - sftype == SF_DB_TYPE_TIMESTAMP_TZ) { - /* set the environment variable TZ to the session timezone - * so that localtime_tz honors it. - */ - _mutex_lock(&gmlocaltime_lock); - const char *prev_tz_ptr = sf_getenv("TZ"); - sf_setenv("TZ", tzptr); - sf_tzset(); - sec += tzoffset * 60 * 2; /* adjust for TIMESTAMP_TZ */ - tm_ptr = sf_localtime(&sec, &tm_obj); - if (prev_tz_ptr != NULL) { - sf_setenv("TZ", prev_tz_ptr); /* cannot set to NULL */ - } else { - sf_unsetenv("TZ"); - } - sf_tzset(); - _mutex_unlock(&gmlocaltime_lock); - } - if (tm_ptr == NULL) { - result->len = 0; - return SF_BOOLEAN_FALSE; - } - const char *fmt0; - if (sftype != SF_DB_TYPE_TIME) { - fmt0 = "%Y-%m-%d %H:%M:%S"; - } else { - fmt0 = "%H:%M:%S"; - } - /* adjust scale */ - char fmt[20]; - sprintf(fmt, ".%%0%lldld", scale); - result->len = strftime(result->value, - result->max_length, fmt0, &tm_obj); - if (scale > 0) { - result->len += snprintf( - &((char *) result->value)[result->len], - result->max_length - result->len, fmt, - nsec); - } - if (sftype == SF_DB_TYPE_TIMESTAMP_TZ) { - /* Timezone info */ - ldiv_t dm = ldiv((long) tzoffset, 60L); - result->len += snprintf( - &((char *) result->value)[result->len], - result->max_length - result->len, - " %c%02ld:%02ld", - dm.quot > 0 ? '+' : '-', labs(dm.quot), labs(dm.rem)); - } - return SF_BOOLEAN_TRUE; -} - /** * Extracts a Snowflake internal representation of timestamp into * seconds, nanoseconds and optionally Timezone offset. @@ -1183,11 +1074,6 @@ static void STDCALL _snowflake_stmt_reset(SF_STMT *sfstmt) { } sfstmt->params = NULL; - if (sfstmt->results) { - sf_array_list_deallocate(sfstmt->results); /* binding columns */ - } - sfstmt->results = NULL; - _snowflake_stmt_desc_reset(sfstmt); if (sfstmt->stmt_attrs) { @@ -1306,36 +1192,6 @@ SF_STATUS snowflake_bind_param_array( return SF_STATUS_SUCCESS; } -SF_STATUS STDCALL snowflake_bind_result( - SF_STMT *sfstmt, SF_BIND_OUTPUT *sfbind) { - if (!sfstmt) { - return SF_STATUS_ERROR_STATEMENT_NOT_EXIST; - } - clear_snowflake_error(&sfstmt->error); - if (sfstmt->results == NULL) { - sfstmt->results = sf_array_list_init(); - } - sf_array_list_set(sfstmt->results, sfbind, sfbind->idx); - return SF_STATUS_SUCCESS; -} - -SF_STATUS snowflake_bind_result_array( - SF_STMT *sfstmt, SF_BIND_OUTPUT *sfbind_array, size_t size) { - size_t i; - if (!sfstmt) { - return SF_STATUS_ERROR_STATEMENT_NOT_EXIST; - } - clear_snowflake_error(&sfstmt->error); - if (sfstmt->results == NULL) { - sfstmt->results = sf_array_list_init(); - } - for (i = 0; i < size; i++) { - sf_array_list_set(sfstmt->results, &sfbind_array[i], - sfbind_array[i].idx); - } - return SF_STATUS_SUCCESS; -} - SF_STATUS STDCALL snowflake_query( SF_STMT *sfstmt, const char *command, size_t command_size) { if (!sfstmt) { @@ -1363,8 +1219,6 @@ SF_STATUS STDCALL snowflake_fetch(SF_STMT *sfstmt) { int64 i; uint64 index; cJSON *row = NULL; - cJSON *raw_result; - SF_BIND_OUTPUT *result; // Check for chunk_downloader error if (sfstmt->chunk_downloader && get_error(sfstmt->chunk_downloader)) { @@ -1440,177 +1294,14 @@ SF_STATUS STDCALL snowflake_fetch(SF_STMT *sfstmt) { } } - // TODO Remove this block of code -// // Check that we can write to the provided result bindings -// for (i = 0; i < sfstmt->total_fieldcount; i++) { -// result = sf_array_list_get(sfstmt->results, (size_t) i + 1); -// if (result == NULL) { -// continue; -// } else { -// if (result->c_type != sfstmt->desc[i].c_type && -// result->c_type != SF_C_TYPE_STRING) { -// goto cleanup; -// } -// } -// } - // Get next result row row = snowflake_cJSON_DetachItemFromArray(sfstmt->raw_results, 0); - snowflake_cJSON_free((cJSON *) sfstmt->cur_row); + if (sfstmt->cur_row != NULL) { + snowflake_cJSON_free(sfstmt->cur_row); + } sfstmt->cur_row = row; sfstmt->chunk_rowcount--; sfstmt->total_row_index++; - - // Write to results - for (i = 0; i < sfstmt->total_fieldcount; i++) { - result = sf_array_list_get(sfstmt->results, (size_t) i + 1); - log_info("snowflake type: %s, C type: %s", - snowflake_type_to_string(sfstmt->desc[i].type), - result != NULL ? - snowflake_c_type_to_string(result->c_type) : NULL); - if (result == NULL) { - // not bound. skipping - continue; - } - time_t sec = 0L; - struct tm tm_obj; - struct tm *tm_ptr; - size_t slen = (size_t)0; - memset(&tm_obj, 0, sizeof(tm_obj)); - - raw_result = snowflake_cJSON_GetArrayItem(row, (int) i); - if (raw_result->valuestring == NULL) { - log_info("setting value and len = NULL"); - ((char *) result->value)[0] = '\0'; // empty string - result->len = (size_t) 0; - result->is_null = SF_BOOLEAN_TRUE; - continue; - } - result->is_null = SF_BOOLEAN_FALSE; - switch (result->c_type) { - case SF_C_TYPE_INT8: - switch (sfstmt->desc[i].type) { - case SF_DB_TYPE_BOOLEAN: - *(int8 *) result->value = snowflake_cJSON_IsTrue( - raw_result) - ? SF_BOOLEAN_TRUE - : SF_BOOLEAN_FALSE; - break; - default: - // field is a char? - *(int8 *) result->value = (int8) raw_result->valuestring[0]; - break; - } - result->len = sizeof(int8); - break; - case SF_C_TYPE_UINT8: - *(uint8 *) result->value = (uint8) raw_result->valuestring[0]; - result->len = sizeof(uint8); - break; - case SF_C_TYPE_INT64: - *(int64 *) result->value = (int64) strtoll( - raw_result->valuestring, NULL, 10); - result->len = sizeof(int64); - break; - case SF_C_TYPE_UINT64: - *(uint64 *) result->value = (uint64) strtoull( - raw_result->valuestring, NULL, 10); - result->len = sizeof(uint64); - break; - case SF_C_TYPE_FLOAT64: - *(float64 *) result->value = (float64) strtod( - raw_result->valuestring, NULL); - result->len = sizeof(float64); - break; - case SF_C_TYPE_STRING: - switch (sfstmt->desc[i].type) { - case SF_DB_TYPE_BOOLEAN: - log_info("value: %p, max_length: %lld, len: %lld", - result->value, result->max_length, - result->len); - if (strcmp(raw_result->valuestring, "0") == 0) { - /* False */ - strncpy(result->value, SF_BOOLEAN_FALSE_STR, - result->max_length); - result->len = sizeof(SF_BOOLEAN_FALSE_STR) - 1; - } else { - /* True */ - strncpy(result->value, SF_BOOLEAN_TRUE_STR, - result->max_length); - result->len = sizeof(SF_BOOLEAN_TRUE_STR) - 1; - } - break; - case SF_DB_TYPE_DATE: - sec = - (time_t) strtol(raw_result->valuestring, NULL, 10) * - 86400L; - _mutex_lock(&gmlocaltime_lock); - tm_ptr = sf_gmtime(&sec, &tm_obj); - _mutex_unlock(&gmlocaltime_lock); - if (tm_ptr == NULL) { - SET_SNOWFLAKE_ERROR(&sfstmt->error, - SF_STATUS_ERROR_CONVERSION_FAILURE, - "Failed to convert a date value to a string.", - SF_SQLSTATE_GENERAL_ERROR); - result->len = 0; - goto cleanup; - } - result->len = strftime( - result->value, result->max_length, - "%Y-%m-%d", &tm_obj); - break; - case SF_DB_TYPE_TIME: - case SF_DB_TYPE_TIMESTAMP_NTZ: - case SF_DB_TYPE_TIMESTAMP_LTZ: - case SF_DB_TYPE_TIMESTAMP_TZ: - if (!_extract_timestamp( - result, - sfstmt->desc[i].type, - raw_result->valuestring, - sfstmt->connection->timezone, - sfstmt->desc[i].scale)) { - SET_SNOWFLAKE_ERROR(&sfstmt->error, - SF_STATUS_ERROR_CONVERSION_FAILURE, - "Failed to convert a timestamp value to a string.", - SF_SQLSTATE_GENERAL_ERROR); - result->len = 0; - goto cleanup; - } - break; - default: - slen = strlen(raw_result->valuestring); - log_debug("slen: %llu, maxbuflen: %llu, realloc func: %p", - slen, - result->max_length, - (void*)sfstmt->user_realloc_func); - result->len = slen; - if (sfstmt->user_realloc_func != NULL && - slen > result->max_length) { - // reallocate the result buffer if the output - // buffer size is not large enough. - result->value = sfstmt->user_realloc_func( - result->value, slen + 1); - result->max_length = slen + 1; - } - strncpy(result->value, raw_result->valuestring, - result->max_length); - break; - } - break; - case SF_C_TYPE_BOOLEAN: - *(sf_bool *) result->value = snowflake_cJSON_IsTrue(raw_result) - ? SF_BOOLEAN_TRUE - : SF_BOOLEAN_FALSE; - result->len = sizeof(sf_bool); - break; - case SF_C_TYPE_TIMESTAMP: - /* TODO: may need Snowflake TIMESTAMP struct like super set of strust tm */ - break; - default: - break; - } - } - ret = SF_STATUS_SUCCESS; cleanup: @@ -2527,7 +2218,9 @@ SF_STATUS STDCALL snowflake_column_as_str(SF_STMT *stmt, int idx, char **value_p cleanup: *value_ptr = value; - *value_len_ptr = value_len; + if (value_len_ptr) { + *value_len_ptr = value_len; + } return SF_STATUS_SUCCESS; } diff --git a/tests/test_adjust_fetch_data.c b/tests/test_adjust_fetch_data.c index 87500ae95b..9aeae7f1e9 100644 --- a/tests/test_adjust_fetch_data.c +++ b/tests/test_adjust_fetch_data.c @@ -29,28 +29,22 @@ void test_select_long_data_with_small_initial_buffer(void **unused) { dump_error(&(sfstmt->error)); } assert_int_equal(status, SF_STATUS_SUCCESS); - - size_t init_size = 10; - SF_BIND_OUTPUT c1 = { - .idx = 1, - .c_type = SF_C_TYPE_STRING, - .value = calloc(1, init_size), - .len = init_size, - .max_length = init_size - }; - snowflake_bind_result(sfstmt, &c1); + + char *value = NULL; assert_int_equal(snowflake_num_rows(sfstmt), 2); int counter = 0; while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { - assert_int_equal(strlen(c1.value), 100); + snowflake_column_as_str(sfstmt, 1, &value, NULL); + assert_int_equal(strlen(value), 100); + free(value); + value = NULL; ++counter; } if (status != SF_STATUS_EOF) { dump_error(&(sfstmt->error)); } assert_int_equal(status, SF_STATUS_EOF); - free(c1.value); snowflake_stmt_term(sfstmt); snowflake_term(sf); } From cf692ce0eb02ae653f250c26a4ac962723a095c3 Mon Sep 17 00:00:00 2001 From: notkriswagner Date: Sun, 16 Sep 2018 11:28:47 -0700 Subject: [PATCH 08/16] Updated test cases to use new fetch API. --- include/snowflake/client.h | 1 + tests/CMakeLists.txt | 1 - tests/test_binary.c | 34 ++------ tests/test_bool.c | 40 +++------ tests/test_crud.c | 29 +------ tests/test_large_result_set.c | 16 ---- tests/test_null.c | 79 +++++------------- tests/test_number.c | 79 ++++++------------ tests/test_parallel_upload_download.cpp | 17 +--- tests/test_perf_column_evaluation.c | 54 ++++--------- tests/test_perf_string_reads_and_writes.c | 78 +++++------------- tests/test_perf_type_conversion.c | 98 +++++++---------------- tests/test_select1.c | 10 +-- tests/test_simple_put.cpp | 46 +++-------- tests/test_stmt_with_bad_connect.c | 6 +- tests/test_time.c | 35 ++------ tests/test_timestamp_ltz.c | 35 ++------ tests/test_timestamp_ntz.c | 35 ++------ tests/test_timestamp_tz.c | 35 ++------ tests/test_timezone.c | 12 +-- tests/test_transaction.c | 16 +--- tests/test_variant.c | 38 +++------ 22 files changed, 194 insertions(+), 600 deletions(-) diff --git a/include/snowflake/client.h b/include/snowflake/client.h index 8d40a7a006..106126e0eb 100644 --- a/include/snowflake/client.h +++ b/include/snowflake/client.h @@ -359,6 +359,7 @@ typedef struct { /** * Bind output parameter context */ + // TODO Remove //typedef struct { // size_t idx; /* One based index of the columns */ // SF_C_TYPE c_type; /* expected data type in C */ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 11355894d0..436b883c9e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -13,7 +13,6 @@ SET(TESTS_C test_connect test_connect_negative test_bind_params - test_bind_results test_change_current test_select1 test_check_ctypes diff --git a/tests/test_binary.c b/tests/test_binary.c index 6d9b7e48c0..f66bac6877 100644 --- a/tests/test_binary.c +++ b/tests/test_binary.c @@ -94,38 +94,16 @@ void test_selectbin(void **unused) { } assert_int_equal(status, SF_STATUS_SUCCESS); - SF_BIND_OUTPUT c1 = {0}; - char c1buf[1024]; - c1.idx = 1; - c1.c_type = SF_C_TYPE_STRING; - c1.value = (void *) c1buf; - c1.len = sizeof(c1buf); - c1.max_length = sizeof(c1buf); - status = snowflake_bind_result(sfstmt, &c1); - if (status != SF_STATUS_SUCCESS) { - dump_error(&(sfstmt->error)); - } - assert_int_equal(status, SF_STATUS_SUCCESS); - - SF_BIND_OUTPUT c2 = {0}; - char c2buf[1024]; - c2.idx = 2; - c2.c_type = SF_C_TYPE_STRING; - c2.value = (void *) c2buf; - c2.len = sizeof(c2buf); - c2.max_length = sizeof(c2buf); - status = snowflake_bind_result(sfstmt, &c2); - if (status != SF_STATUS_SUCCESS) { - dump_error(&(sfstmt->error)); - } - assert_int_equal(status, SF_STATUS_SUCCESS); - assert_int_equal(snowflake_num_rows(sfstmt), sizeof(test_cases) / sizeof(TEST_CASE_TO_STRING)); while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { - TEST_CASE_TO_STRING v = test_cases[atoll(c1.value) - 1]; - assert_string_equal(v.c2out, c2.value); + int64 c1 = 0; + const char *c2 = NULL; + snowflake_column_as_int64(sfstmt, 1, &c1); + snowflake_column_as_const_str(sfstmt, 2, &c2); + TEST_CASE_TO_STRING v = test_cases[c1 - 1]; + assert_string_equal(v.c2out, c2); } if (status != SF_STATUS_EOF) { dump_error(&(sfstmt->error)); diff --git a/tests/test_bool.c b/tests/test_bool.c index b83a077773..ce6bb0cc1f 100644 --- a/tests/test_bool.c +++ b/tests/test_bool.c @@ -101,41 +101,23 @@ void test_bool(void **unused) { } assert_int_equal(status, SF_STATUS_SUCCESS); - SF_BIND_OUTPUT c1 = {0}; - char c1buf[1024]; - c1.idx = 1; - c1.c_type = SF_C_TYPE_STRING; - c1.value = (void *) c1buf; - c1.len = sizeof(c1buf); - c1.max_length = sizeof(c1buf); - status = snowflake_bind_result(sfstmt, &c1); - if (status != SF_STATUS_SUCCESS) { - dump_error(&(sfstmt->error)); - } - assert_int_equal(status, SF_STATUS_SUCCESS); - - SF_BIND_OUTPUT c2 = {0}; - char c2buf[1024]; - c2.idx = 2; - c2.c_type = SF_C_TYPE_STRING; - c2.value = (void *) c2buf; - c2.len = sizeof(c2buf); - c2.max_length = sizeof(c2buf); - status = snowflake_bind_result(sfstmt, &c2); - if (status != SF_STATUS_SUCCESS) { - dump_error(&(sfstmt->error)); - } - assert_int_equal(status, SF_STATUS_SUCCESS); - assert_int_equal(snowflake_num_rows(sfstmt), sizeof(test_cases) / sizeof(TEST_CASE_TO_STRING)); + int64 c1 = 0; + char *c2 = NULL; while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { - TEST_CASE_TO_STRING v = test_cases[atoll(c1.value) - 1]; + snowflake_column_as_int64(sfstmt, 1, &c1); + TEST_CASE_TO_STRING v = test_cases[c1 - 1]; if (v.c2out != NULL) { - assert_string_equal(v.c2out, c2.value); + snowflake_column_as_str(sfstmt, 2, &c2, NULL); + assert_string_equal(v.c2out, c2); + free(c2); + c2 = NULL; } else { - assert_true(v.c2_is_null == c2.is_null); + sf_bool is_null; + snowflake_column_is_null(sfstmt, 2, &is_null); + assert_true(v.c2_is_null == is_null); } } if (status != SF_STATUS_EOF) { diff --git a/tests/test_crud.c b/tests/test_crud.c index 74adfc5534..217893b9ed 100644 --- a/tests/test_crud.c +++ b/tests/test_crud.c @@ -11,31 +11,8 @@ void _fetch_data(SF_STMT *sfstmt, int64 expected_sum) { } assert_int_equal(status, SF_STATUS_SUCCESS); - int64 c1v = 0; - SF_BIND_OUTPUT c1 = {0}; - c1.idx = 1; - c1.max_length = sizeof(c1v); - c1.c_type = SF_C_TYPE_INT64; - c1.value = &c1v; - status = snowflake_bind_result(sfstmt, &c1); - if (status != SF_STATUS_SUCCESS) { - dump_error(&(sfstmt->error)); - } - assert_int_equal(status, SF_STATUS_SUCCESS); - - char c2v[1000]; - SF_BIND_OUTPUT c2 = {0}; - c2.idx = 2; - c2.max_length = sizeof(c2v); - c2.c_type = SF_C_TYPE_STRING; - c2.value = &c2v; - status = snowflake_bind_result(sfstmt, &c2); - if (status != SF_STATUS_SUCCESS) { - dump_error(&(sfstmt->error)); - } - assert_int_equal(status, SF_STATUS_SUCCESS); - - uint64 num_fields = snowflake_num_fields(sfstmt); + int64 c1 = 0; + int64 num_fields = snowflake_num_fields(sfstmt); assert_int_equal(num_fields, 2); SF_COLUMN_DESC *descs = snowflake_desc(sfstmt); @@ -69,7 +46,7 @@ void _fetch_data(SF_STMT *sfstmt, int64 expected_sum) { int64 total = 0; while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { // printf("c1: %lld, c2: %s\n", c1v, c2v); - total += c1v; + total += c1; } assert_int_equal(total, expected_sum); if (status != SF_STATUS_EOF) { diff --git a/tests/test_large_result_set.c b/tests/test_large_result_set.c index 4651f13671..4a6cd5075e 100644 --- a/tests/test_large_result_set.c +++ b/tests/test_large_result_set.c @@ -29,22 +29,6 @@ void test_large_result_set(void **unused) { } assert_int_equal(status, SF_STATUS_SUCCESS); - SF_BIND_OUTPUT c1 = {0}; - int64 out = 0; - c1.idx = 1; - c1.c_type = SF_C_TYPE_INT64; - c1.value = (void *) &out; - c1.max_length = sizeof(out); - snowflake_bind_result(sfstmt, &c1); - - SF_BIND_OUTPUT c2 = {0}; - char c2buf[1001]; - c2.idx = 2; - c2.c_type = SF_C_TYPE_STRING; - c2.value = (void *) c2buf; - c2.max_length = sizeof(c2buf); - snowflake_bind_result(sfstmt, &c2); - uint64 counter = 0; while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { // printf("output: %lld, %s\n", out, c2buf); diff --git a/tests/test_null.c b/tests/test_null.c index ac34f35a18..10c01c2444 100644 --- a/tests/test_null.c +++ b/tests/test_null.c @@ -2,6 +2,7 @@ * Copyright (c) 2018 Snowflake Computing, Inc. All rights reserved. */ #include +#include #include "utils/test_setup.h" @@ -22,7 +23,7 @@ typedef struct test_case_to_string { void test_null(void **unused) { TEST_CASE_TO_STRING test_cases[] = { - {.c1in = 1, .c2in = NULL, .c3in = NULL, .c4in = NULL, .c2out = "", .c2_is_null = SF_BOOLEAN_TRUE, .c3out="", .c3_is_null=SF_BOOLEAN_TRUE, .c4out= "", .c4_is_null = SF_BOOLEAN_TRUE} + {.c1in = 1, .c2in = NULL, .c3in = NULL, .c4in = NULL, .c2out = NULL, .c2_is_null = SF_BOOLEAN_TRUE, .c3out=NULL, .c3_is_null=SF_BOOLEAN_TRUE, .c4out= NULL, .c4_is_null = SF_BOOLEAN_TRUE} }; SF_CONNECT *sf = setup_snowflake_connection(); @@ -118,69 +119,27 @@ void test_null(void **unused) { dump_error(&(sfstmt->error)); } assert_int_equal(status, SF_STATUS_SUCCESS); - - SF_BIND_OUTPUT c1 = {0}; - char c1buf[1024]; - c1.idx = 1; - c1.c_type = SF_C_TYPE_STRING; - c1.value = (void *) c1buf; - c1.len = sizeof(c1buf); - c1.max_length = sizeof(c1buf); - status = snowflake_bind_result(sfstmt, &c1); - if (status != SF_STATUS_SUCCESS) { - dump_error(&(sfstmt->error)); - } - assert_int_equal(status, SF_STATUS_SUCCESS); - - SF_BIND_OUTPUT c2 = {0}; - char c2buf[1024]; - c2.idx = 2; - c2.c_type = SF_C_TYPE_STRING; - c2.value = (void *) c2buf; - c2.len = sizeof(c2buf); - c2.max_length = sizeof(c2buf); - status = snowflake_bind_result(sfstmt, &c2); - if (status != SF_STATUS_SUCCESS) { - dump_error(&(sfstmt->error)); - } - assert_int_equal(status, SF_STATUS_SUCCESS); - - SF_BIND_OUTPUT c3 = {0}; - char c3buf[1024]; - c3.idx = 3; - c3.c_type = SF_C_TYPE_INT64; - c3.value = (void *) c3buf; - c3.len = sizeof(c3buf); - c3.max_length = sizeof(c3buf); - status = snowflake_bind_result(sfstmt, &c3); - if (status != SF_STATUS_SUCCESS) { - dump_error(&(sfstmt->error)); - } - assert_int_equal(status, SF_STATUS_SUCCESS); - - SF_BIND_OUTPUT c4 = {0}; - char c4buf[1024]; - c4.idx = 4; - c4.c_type = SF_C_TYPE_BOOLEAN; - c4.value = (void *) c4buf; - c4.len = sizeof(c4buf); - c4.max_length = sizeof(c4buf); - status = snowflake_bind_result(sfstmt, &c4); - if (status != SF_STATUS_SUCCESS) { - dump_error(&(sfstmt->error)); - } - assert_int_equal(status, SF_STATUS_SUCCESS); assert_int_equal(snowflake_num_rows(sfstmt), sizeof(test_cases) / sizeof(TEST_CASE_TO_STRING)); + sf_bool is_null = SF_BOOLEAN_FALSE; + int64 c1 = 0; + char *null_val = NULL; while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { - TEST_CASE_TO_STRING v = test_cases[atoll(c1.value) - 1]; - assert_true(v.c2_is_null == c2.is_null); - assert_string_equal(v.c2out, c2.value); - assert_true(v.c3_is_null == c3.is_null); - assert_string_equal(v.c3out, c3.value); - assert_true(v.c4_is_null == c4.is_null); - assert_string_equal(v.c4out, c4.value); + snowflake_column_as_int64(sfstmt, 1, &c1); + TEST_CASE_TO_STRING v = test_cases[c1 - 1]; + snowflake_column_is_null(sfstmt, 2, &is_null); + snowflake_column_as_str(sfstmt, 2, &null_val, NULL); + assert_true(v.c2_is_null == is_null); + assert(v.c2out == null_val); + snowflake_column_is_null(sfstmt, 3, &is_null); + snowflake_column_as_str(sfstmt, 3, &null_val, NULL); + assert_true(v.c3_is_null == is_null); + assert(v.c3out == null_val); + snowflake_column_is_null(sfstmt, 4, &is_null); + snowflake_column_as_str(sfstmt, 4, &null_val, NULL); + assert_true(v.c4_is_null == is_null); + assert(v.c4out == null_val); } if (status != SF_STATUS_EOF) { dump_error(&(sfstmt->error)); diff --git a/tests/test_number.c b/tests/test_number.c index 2d6ecd8c41..c6992080b7 100644 --- a/tests/test_number.c +++ b/tests/test_number.c @@ -2,6 +2,7 @@ * Copyright (c) 2018 Snowflake Computing, Inc. All rights reserved. */ #include +#include #include "utils/test_setup.h" @@ -115,64 +116,34 @@ void test_number(void **unused) { dump_error(&(sfstmt->error)); } assert_int_equal(status, SF_STATUS_SUCCESS); - - SF_BIND_OUTPUT c1 = {0}; - char c1buf[1024]; - c1.idx = 1; - c1.c_type = SF_C_TYPE_STRING; - c1.value = (void *) c1buf; - c1.len = sizeof(c1buf); - c1.max_length = sizeof(c1buf); - status = snowflake_bind_result(sfstmt, &c1); - if (status != SF_STATUS_SUCCESS) { - dump_error(&(sfstmt->error)); - } - assert_int_equal(status, SF_STATUS_SUCCESS); - - SF_BIND_OUTPUT c2 = {0}; - char c2buf[1024]; - c2.idx = 2; - c2.c_type = SF_C_TYPE_STRING; - c2.value = (void *) c2buf; - c2.len = sizeof(c2buf); - c2.max_length = sizeof(c2buf); - status = snowflake_bind_result(sfstmt, &c2); - if (status != SF_STATUS_SUCCESS) { - dump_error(&(sfstmt->error)); - } - assert_int_equal(status, SF_STATUS_SUCCESS); - - SF_BIND_OUTPUT c3 = {0}; - char c3buf[1024]; - c3.idx = 3; - c3.c_type = SF_C_TYPE_STRING; - c3.value = (void *) c3buf; - c3.max_length = sizeof(c3buf); - status = snowflake_bind_result(sfstmt, &c3); - if (status != SF_STATUS_SUCCESS) { - dump_error(&(sfstmt->error)); - } - assert_int_equal(status, SF_STATUS_SUCCESS); - - SF_BIND_OUTPUT c4 = {0}; - char c4buf[1024]; - c4.idx = 4; - c4.c_type = SF_C_TYPE_STRING; - c4.value = (void *) c4buf; - c4.max_length = sizeof(c4buf); - status = snowflake_bind_result(sfstmt, &c4); - if (status != SF_STATUS_SUCCESS) { - dump_error(&(sfstmt->error)); - } - assert_int_equal(status, SF_STATUS_SUCCESS); assert_int_equal(snowflake_num_rows(sfstmt), sizeof(test_cases) / sizeof(TEST_CASE_TO_STRING)); + int64 c1 = 0; + char *str = NULL; + int64 int_val = 0; + float64 float_val = 0.0; while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { - TEST_CASE_TO_STRING v = test_cases[atoll(c1.value) - 1]; - assert_string_equal(v.c2out, c2.value); - assert_string_equal(v.c3out, c3.value); - assert_string_equal(v.c4out, c4.value); + snowflake_column_as_int64(sfstmt, 1, &c1); + TEST_CASE_TO_STRING v = test_cases[c1 - 1]; + snowflake_column_as_float64(sfstmt, 2, &float_val); + snowflake_column_as_str(sfstmt, 2, &str, NULL); + assert(float_val = v.c2in); + assert_string_equal(v.c2out, str); + free(str); + str = NULL; + snowflake_column_as_int64(sfstmt, 3, &int_val); + snowflake_column_as_str(sfstmt, 3, &str, NULL); + assert(int_val = v.c3in); + assert_string_equal(v.c3out, str); + free(str); + str = NULL; + snowflake_column_as_float64(sfstmt, 4, &float_val); + snowflake_column_as_str(sfstmt, 4, &str, NULL); + assert(float_val = v.c4in); + assert_string_equal(v.c4out, str); + free(str); + str = NULL; } if (status != SF_STATUS_EOF) { dump_error(&(sfstmt->error)); diff --git a/tests/test_parallel_upload_download.cpp b/tests/test_parallel_upload_download.cpp index c27fa743d2..c597449868 100755 --- a/tests/test_parallel_upload_download.cpp +++ b/tests/test_parallel_upload_download.cpp @@ -95,21 +95,12 @@ void test_parallel_upload_download_core(int fileNumber) ret = snowflake_query(sfstmt, copyCommand.c_str(), copyCommand.size()); assert_int_equal(SF_STATUS_SUCCESS, ret); - char out_c2[7]; - SF_BIND_OUTPUT c2; - c2.idx = 2; - c2.c_type = SF_C_TYPE_STRING; - c2.max_length = sizeof(out_c2); - c2.value = (void *) out_c2; - c2.len = sizeof(out_c2); - - ret = snowflake_bind_result(sfstmt, &c2); - assert_int_equal(SF_STATUS_SUCCESS, ret); - assert_int_equal(snowflake_num_rows(sfstmt), fileNumber); - + + const char *c2 = nullptr; while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { - assert_string_equal((char *) c2.value, "LOADED"); + snowflake_column_as_const_str(sfstmt, 2, &c2); + assert_string_equal(c2, "LOADED"); } assert_int_equal(status, SF_STATUS_EOF); diff --git a/tests/test_perf_column_evaluation.c b/tests/test_perf_column_evaluation.c index c1502f2f2f..712d631877 100644 --- a/tests/test_perf_column_evaluation.c +++ b/tests/test_perf_column_evaluation.c @@ -6,7 +6,7 @@ #include "utils/test_setup.h" const char *COL_EVAL_QUERY = "select seq4(), seq4(), seq4(), seq4(), seq4(), seq4() from table(generator(rowcount=>4000));"; -const size_t NUM_COLS = 6; +const int NUM_COLS = 6; const int NUM_ROWS = 4000; void test_eval_all_cols(void **unused) { @@ -23,18 +23,12 @@ void test_eval_all_cols(void **unused) { clock_gettime(clk_id, &begin); // Configure result binding and bind - SF_BIND_OUTPUT columns[NUM_COLS]; int64 out[NUM_COLS]; - - for (size_t i = 0; i < NUM_COLS; i++) { - columns[i].idx = i + 1; - columns[i].c_type = SF_C_TYPE_INT64; - columns[i].value = (void *) &out[i]; - columns[i].max_length = sizeof(out[i]); - } - snowflake_bind_result_array(sfstmt, columns, NUM_COLS); while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { + for (int i = 0; i < NUM_COLS; i++) { + snowflake_column_as_int64(sfstmt, i + 1, &out[i]); + } fprintf(dev_null, "%lli, %lli, %lli, %lli, %lli, %lli", out[0], out[1], out[2], out[3], out[4], out[5]); } @@ -61,21 +55,18 @@ void test_eval_half_cols(void **unused) { clock_gettime(clk_id, &begin); // Configure result binding and bind - SF_BIND_OUTPUT columns[NUM_COLS]; int64 out[NUM_COLS]; - for (size_t i = 0; i < NUM_COLS; i++) { - columns[i].idx = i + 1; - columns[i].c_type = SF_C_TYPE_INT64; - columns[i].value = (void *) &out[i]; - columns[i].max_length = sizeof(out[i]); - } - snowflake_bind_result_array(sfstmt, columns, NUM_COLS); - while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { if (sfstmt->total_row_index % 2) { + for (int i = 0; i < 3; i++) { + snowflake_column_as_int64(sfstmt, i + 1, &out[i]); + } fprintf(dev_null, "%lli, %lli, %lli", out[0], out[1], out[2]); } else { + for (int i = 3; i < NUM_COLS; i++) { + snowflake_column_as_int64(sfstmt, i + 1, &out[i]); + } fprintf(dev_null, "%lli, %lli, %lli", out[3], out[4], out[5]); } } @@ -103,19 +94,13 @@ void test_skip_rows_half(void **unused) { clock_gettime(clk_id, &begin); // Configure result binding and bind - SF_BIND_OUTPUT columns[NUM_COLS]; int64 out[NUM_COLS]; - for (size_t i = 0; i < NUM_COLS; i++) { - columns[i].idx = i + 1; - columns[i].c_type = SF_C_TYPE_INT64; - columns[i].value = (void *) &out[i]; - columns[i].max_length = sizeof(out[i]); - } - snowflake_bind_result_array(sfstmt, columns, NUM_COLS); - while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { if (sfstmt->total_row_index % 2) { + for (int i = 0; i < NUM_COLS; i++) { + snowflake_column_as_int64(sfstmt, i + 1, &out[i]); + } fprintf(dev_null, "%lli, %lli, %lli, %lli, %lli, %lli", out[0], out[1], out[2], out[3], out[4], out[5]); } } @@ -143,18 +128,13 @@ void test_skip_all_rows(void **unused) { clock_gettime(clk_id, &begin); // Configure result binding and bind - SF_BIND_OUTPUT columns[NUM_COLS]; int64 out[NUM_COLS]; - for (size_t i = 0; i < NUM_COLS; i++) { - columns[i].idx = i + 1; - columns[i].c_type = SF_C_TYPE_INT64; - columns[i].value = (void *) &out[i]; - columns[i].max_length = sizeof(out[i]); + while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { + for (int i = 0; i < NUM_COLS; i++) { + snowflake_column_as_int64(sfstmt, i + 1, &out[i]); + } } - snowflake_bind_result_array(sfstmt, columns, NUM_COLS); - - while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) {} clock_gettime(clk_id, &end); diff --git a/tests/test_perf_string_reads_and_writes.c b/tests/test_perf_string_reads_and_writes.c index 1111a54983..b0de1a531c 100644 --- a/tests/test_perf_string_reads_and_writes.c +++ b/tests/test_perf_string_reads_and_writes.c @@ -34,18 +34,10 @@ void test_col_string_read_fixed_size(void **unused) { // Begin timing clock_gettime(clk_id, &begin); - // Configure result binding and bind - SF_BIND_OUTPUT c1 = {0}; - char out[STRING_FIELD_FIXED_SIZE]; - c1.idx = 1; - c1.c_type = SF_C_TYPE_STRING; - c1.value = (void *) &out; - c1.max_length = sizeof(out); - snowflake_bind_result(sfstmt, &c1); - size_t len; while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { - if ((len = strlen(out)) != STRING_FIELD_FIXED_SIZE - 1) { + snowflake_column_strlen(sfstmt, 1, &len); + if (len != STRING_FIELD_FIXED_SIZE - 1) { fail_msg("Query in %s should have a text field with %i characters, instead has %zd", "test_col_string_read_fixed_size", STRING_FIELD_FIXED_SIZE - 1, len); } } @@ -71,22 +63,19 @@ void test_col_string_manipulate_fixed_size(void **unused) { clock_gettime(clk_id, &begin); // Configure result binding and bind - SF_BIND_OUTPUT c1 = {0}; - char out[STRING_FIELD_MAX_SIZE]; - c1.idx = 1; - c1.c_type = SF_C_TYPE_STRING; - c1.value = (void *) &out; - c1.max_length = sizeof(out); - snowflake_bind_result(sfstmt, &c1); + char *out = NULL; size_t len; while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { // Convert string to lowercase then discard + snowflake_column_as_str(sfstmt, 1, &out, NULL); char *p = out; while (*p) { *p = (char) tolower(*p); p++; } + free(out); + out = NULL; } clock_gettime(clk_id, &end); @@ -112,23 +101,14 @@ void test_col_buffer_copy_unknown_size_dynamic_memory(void **unused) { clock_gettime(clk_id, &begin); int row = 0; - // Configure result binding and bind - SF_BIND_OUTPUT c1 = {0}; - // Use max buffer size since we have to assume the largest buffer size for each row - char out[STRING_FIELD_MAX_SIZE]; - c1.idx = 1; - c1.c_type = SF_C_TYPE_STRING; - c1.value = (void *) &out; - c1.max_length = sizeof(out); - snowflake_bind_result(sfstmt, &c1); - // Create array of char pointers to hold dynamically created buffers char *out_buff[NUM_ROWS]; - size_t len; + char *out = NULL; while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { // Create dynamic buffer and copy over - out_buff[row] = (char *) malloc(c1.len + 1); - strncpy(out_buff[row], out, c1.len + 1); + snowflake_column_as_str(sfstmt, 1, &out, NULL); + out_buff[row] = out; + out = NULL; row++; } @@ -164,41 +144,26 @@ void test_col_buffer_copy_concat_multiple_rows(void **unused) { clock_gettime(clk_id, &begin); int row = 0; - // Configure result binding and bind - SF_BIND_OUTPUT columns[3]; - // Store ID to use as array index - int64 id; - columns[0].idx = 1; - columns[0].c_type = SF_C_TYPE_INT64; - columns[0].value = (void *) &id; - columns[0].max_length = sizeof(id); - snowflake_bind_result(sfstmt, &columns[0]); + int64 id = 0; // Use max buffer size since we have to assume the largest buffer size for each row of text - char *title = calloc(1, STRING_FIELD_MAX_SIZE + 1); - columns[1].idx = 2; - columns[1].c_type = SF_C_TYPE_STRING; - columns[1].value = (void *) title; - columns[1].max_length = STRING_FIELD_MAX_SIZE + 1; - snowflake_bind_result(sfstmt, &columns[1]); + char *title = NULL; // Use max buffer size since we have to assume the largest buffer size for each row of text - char *text = calloc(1, STRING_FIELD_MAX_SIZE + 1); - columns[2].idx = 3; - columns[2].c_type = SF_C_TYPE_STRING; - columns[2].value = (void *) text; - columns[2].max_length = STRING_FIELD_MAX_SIZE + 1; - snowflake_bind_result(sfstmt, &columns[2]); + const char *text = NULL; + size_t text_len = 0; while (snowflake_fetch(sfstmt) == SF_STATUS_SUCCESS) { // Set title if NULL if (!books[id].title) { - books[id].title = (char *) malloc(columns[1].len + 1); - strncpy(books[id].title, title, columns[1].len + 1); + snowflake_column_as_str(sfstmt, 2, &title, NULL); + books[id].title = title; } // Copy text into the right buffer - books[id].text = (char *) realloc(books[id].text, columns[2].len + books[id].text_len + 1); - memcpy(&books[id].text[books[id].text_len], text, columns[2].len); - books[id].text_len += columns[2].len; + snowflake_column_as_const_str(sfstmt, 3, &text); + snowflake_column_strlen(sfstmt, 3, &text_len); + books[id].text = (char *) realloc(books[id].text, text_len + books[id].text_len + 1); + memcpy(&books[id].text[books[id].text_len], text, text_len); + books[id].text_len += text_len; books[id].text[books[id].text_len] = '\0'; } @@ -219,7 +184,6 @@ void test_col_buffer_copy_concat_multiple_rows(void **unused) { } free(title); - free(text); snowflake_stmt_term(sfstmt); snowflake_term(sf); } diff --git a/tests/test_perf_type_conversion.c b/tests/test_perf_type_conversion.c index 3fb1f7ef29..7bc271b4f1 100644 --- a/tests/test_perf_type_conversion.c +++ b/tests/test_perf_type_conversion.c @@ -18,17 +18,11 @@ void test_col_conv_int64_type(void **unused) { setup_and_run_query(&sf, &sfstmt, "select seq4() from table(generator(rowcount=>12000));"); clock_gettime(clk_id, &begin); - - // Configure result binding and bind - SF_BIND_OUTPUT c1 = {0}; + int64 out = 0; - c1.idx = 1; - c1.c_type = SF_C_TYPE_INT64; - c1.value = (void *) &out; - c1.max_length = sizeof(out); - snowflake_bind_result(sfstmt, &c1); - - while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) {} + while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { + snowflake_column_as_int64(sfstmt, 1, &out); + } clock_gettime(clk_id, &end); @@ -48,17 +42,11 @@ void test_col_conv_float64_type(void **unused) { setup_and_run_query(&sf, &sfstmt, "select as_double(10.01) from table(generator(rowcount=>12000));"); clock_gettime(clk_id, &begin); - - // Configure result binding and bind - SF_BIND_OUTPUT c1 = {0}; + float64 out = 0; - c1.idx = 1; - c1.c_type = SF_C_TYPE_FLOAT64; - c1.value = (void *) &out; - c1.max_length = sizeof(out); - snowflake_bind_result(sfstmt, &c1); - - while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) {} + while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { + snowflake_column_as_float64(sfstmt, 1, &out); + } clock_gettime(clk_id, &end); @@ -79,17 +67,11 @@ void test_col_conv_str_type(void **unused) { // Begin timing clock_gettime(clk_id, &begin); - - // Configure result binding and bind - SF_BIND_OUTPUT c1 = {0}; - char out[1000]; - c1.idx = 1; - c1.c_type = SF_C_TYPE_STRING; - c1.value = (void *) &out; - c1.max_length = sizeof(out); - snowflake_bind_result(sfstmt, &c1); - - while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) {} + + const char *out; + while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { + snowflake_column_as_const_str(sfstmt, 1, &out); + } clock_gettime(clk_id, &end); @@ -111,17 +93,13 @@ void test_col_conv_timestamp_type(void **unused) { // Begin timing clock_gettime(clk_id, &begin); - - // Configure result binding and bind - SF_BIND_OUTPUT c1 = {0}; - char out[1000]; - c1.idx = 1; - c1.c_type = SF_C_TYPE_STRING; - c1.value = (void *) &out; - c1.max_length = sizeof(out); - snowflake_bind_result(sfstmt, &c1); - - while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) {} + + char *out = NULL; + while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { + snowflake_column_as_str(sfstmt, 1, &out, NULL); + free(out); + out = NULL; + } clock_gettime(clk_id, &end); @@ -143,30 +121,17 @@ void test_col_conv_multi_type(void **unused) { // Begin timing clock_gettime(clk_id, &begin); - // Configure result binding and bind - SF_BIND_OUTPUT c1; - char out_str[1000]; - c1.idx = 1; - c1.c_type = SF_C_TYPE_STRING; - c1.value = (void *) &out_str; - c1.max_length = sizeof(out_str); - + const char *out_str; int64 out_int; - - snowflake_bind_result(sfstmt, &c1); - + while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { // Bind different result type every time to convert to multiple types switch(sfstmt->total_row_index % 2) { case 0: - c1.c_type = SF_C_TYPE_STRING; - c1.value = (void *) &out_str; - c1.max_length = sizeof(out_str); + snowflake_column_as_const_str(sfstmt, 1, &out_str); break; case 1: - c1.c_type = SF_C_TYPE_INT64; - c1.value = (void *) &out_int; - c1.max_length = sizeof(out_int); + snowflake_column_as_int64(sfstmt, 1, &out_int); break; } } @@ -190,21 +155,14 @@ void test_col_conv_multi_types_per_row(void **unused) { clock_gettime(clk_id, &begin); - // Configure result binding and bind - SF_BIND_OUTPUT c1 = {0}; - char out_str[1000]; - c1.idx = 1; - c1.c_type = SF_C_TYPE_STRING; - c1.value = (void *) &out_str; - c1.max_length = sizeof(out_str); - snowflake_bind_result(sfstmt, &c1); - + const char *out_str; int64 out_int; float64 out_float; while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { - out_int = strtol(out_str, NULL, 0); - out_float = strtod(out_str, NULL); + snowflake_column_as_int64(sfstmt, 1, &out_int); + snowflake_column_as_float64(sfstmt, 1, &out_float); + snowflake_column_as_const_str(sfstmt, 1, &out_str); } clock_gettime(clk_id, &end); diff --git a/tests/test_select1.c b/tests/test_select1.c index ffad05ff35..fea2cb5ad2 100644 --- a/tests/test_select1.c +++ b/tests/test_select1.c @@ -21,18 +21,12 @@ void test_select1(void **unused) { assert_int_equal(status, SF_STATUS_SUCCESS); int64 out = 0; - SF_BIND_OUTPUT c1 = { - .idx = 1, - .c_type = SF_C_TYPE_INT64, - .value = (void *) &out, - .len = sizeof(out) - }; - snowflake_bind_result(sfstmt, &c1); assert_int_equal(snowflake_num_rows(sfstmt), 1); int counter = 0; while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { - assert_int_equal(*(int64 *) c1.value, 1); + snowflake_column_as_int64(sfstmt, 1, &out); + assert_int_equal(out, 1); ++counter; } if (status != SF_STATUS_EOF) { diff --git a/tests/test_simple_put.cpp b/tests/test_simple_put.cpp index 78d4ede1f1..513bf6b26d 100755 --- a/tests/test_simple_put.cpp +++ b/tests/test_simple_put.cpp @@ -98,45 +98,21 @@ void test_simple_put_core(const char * fileName, ret = snowflake_query(sfstmt, selectCommand.c_str(), selectCommand.size()); assert_int_equal(SF_STATUS_SUCCESS, ret); - char out_c1[5]; - SF_BIND_OUTPUT c1; - c1.idx = 1; - c1.c_type = SF_C_TYPE_STRING; - c1.max_length = sizeof(out_c1); - c1.value = (void *) out_c1; - c1.len = sizeof(out_c1); - - char out_c2[5]; - SF_BIND_OUTPUT c2; - c2.idx = 2; - c2.c_type = SF_C_TYPE_STRING; - c2.max_length = sizeof(out_c2); - c2.value = (void *) out_c2; - c2.len = sizeof(out_c2); - - char out_c3[20]; - SF_BIND_OUTPUT c3; - c3.idx = 3; - c3.c_type = SF_C_TYPE_STRING; - c3.max_length = sizeof(out_c3); - c3.value = (void *) out_c3; - c3.len = sizeof(out_c3); - - ret = snowflake_bind_result(sfstmt, &c1); - assert_int_equal(SF_STATUS_SUCCESS, ret); - ret = snowflake_bind_result(sfstmt, &c2); - assert_int_equal(SF_STATUS_SUCCESS, ret); - ret = snowflake_bind_result(sfstmt, &c3); - assert_int_equal(SF_STATUS_SUCCESS, ret); - + const char *out_c1; + const char *out_c2; + const char *out_c3; assert_int_equal(snowflake_num_rows(sfstmt), 1); ret = snowflake_fetch(sfstmt); assert_int_equal(SF_STATUS_SUCCESS, ret); - - assert_string_equal((char *) c1.value, "1"); - assert_string_equal((char *) c2.value, "2"); - assert_string_equal((char *) c3.value, "test_string"); + + snowflake_column_as_const_str(sfstmt, 1, &out_c1); + snowflake_column_as_const_str(sfstmt, 2, &out_c2); + snowflake_column_as_const_str(sfstmt, 3, &out_c3); + + assert_string_equal(out_c1, "1"); + assert_string_equal(out_c2, "2"); + assert_string_equal(out_c3, "test_string"); ret = snowflake_fetch(sfstmt); assert_int_equal(SF_STATUS_EOF, ret); diff --git a/tests/test_stmt_with_bad_connect.c b/tests/test_stmt_with_bad_connect.c index 4855e0c636..c6d5539d38 100644 --- a/tests/test_stmt_with_bad_connect.c +++ b/tests/test_stmt_with_bad_connect.c @@ -11,12 +11,7 @@ void test_no_connect_and_retry(void **unused) { SF_CONNECT *sf = snowflake_init(); SF_STMT *sfstmt = snowflake_stmt(sf); snowflake_prepare(sfstmt, "select 1;", 0); - SF_BIND_OUTPUT c1 = {0}; int64 out = 0; - c1.idx = 1; - c1.c_type = SF_C_TYPE_INT64; - c1.value = (void *) &out; - snowflake_bind_result(sfstmt, &c1); SF_STATUS status = snowflake_execute(sfstmt); assert_int_not_equal(status, SF_STATUS_SUCCESS); // must fail SF_ERROR_STRUCT *error = snowflake_stmt_error(sfstmt); @@ -59,6 +54,7 @@ void test_no_connect_and_retry(void **unused) { int counter = 0; while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { + snowflake_column_as_int64(sfstmt, 1, &out); ++counter; } assert_int_equal(counter, 1); diff --git a/tests/test_time.c b/tests/test_time.c index f449c7d004..9f82031047 100644 --- a/tests/test_time.c +++ b/tests/test_time.c @@ -102,38 +102,17 @@ void test_time(void **unused) { dump_error(&(sfstmt->error)); } assert_int_equal(status, SF_STATUS_SUCCESS); - - SF_BIND_OUTPUT c1 = {0}; - char c1buf[1024]; - c1.idx = 1; - c1.c_type = SF_C_TYPE_STRING; - c1.value = (void *) c1buf; - c1.len = sizeof(c1buf); - c1.max_length = sizeof(c1buf); - status = snowflake_bind_result(sfstmt, &c1); - if (status != SF_STATUS_SUCCESS) { - dump_error(&(sfstmt->error)); - } - assert_int_equal(status, SF_STATUS_SUCCESS); - - SF_BIND_OUTPUT c2 = {0}; - char c2buf[1024]; - c2.idx = 2; - c2.c_type = SF_C_TYPE_STRING; - c2.value = (void *) c2buf; - c2.len = sizeof(c2buf); - c2.max_length = sizeof(c2buf); - status = snowflake_bind_result(sfstmt, &c2); - if (status != SF_STATUS_SUCCESS) { - dump_error(&(sfstmt->error)); - } - assert_int_equal(status, SF_STATUS_SUCCESS); + + int64 c1_out; + const char *c2_out = NULL; assert_int_equal(snowflake_num_rows(sfstmt), no_error_test_cases); while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { - TEST_CASE_TO_STRING v = test_cases[atoll(c1.value) - 1]; + snowflake_column_as_int64(sfstmt, 1, &c1_out); + snowflake_column_as_const_str(sfstmt, 2, &c2_out); + TEST_CASE_TO_STRING v = test_cases[c1_out - 1]; assert_int_equal(status, SF_STATUS_SUCCESS); - assert_string_equal(v.c2out, c2.value); + assert_string_equal(v.c2out, c2_out); } if (status != SF_STATUS_EOF) { dump_error(&(sfstmt->error)); diff --git a/tests/test_timestamp_ltz.c b/tests/test_timestamp_ltz.c index 6608f0a17c..68da344c56 100644 --- a/tests/test_timestamp_ltz.c +++ b/tests/test_timestamp_ltz.c @@ -118,31 +118,8 @@ void test_timestamp_ltz(void** unused) { } assert_int_equal(status, SF_STATUS_SUCCESS); - SF_BIND_OUTPUT c1 = {0}; - char c1buf[1024]; - c1.idx = 1; - c1.c_type = SF_C_TYPE_STRING; - c1.value = (void *) c1buf; - c1.len = sizeof(c1buf); - c1.max_length = sizeof(c1buf); - status = snowflake_bind_result(sfstmt, &c1); - if (status != SF_STATUS_SUCCESS) { - dump_error(&(sfstmt->error)); - } - assert_int_equal(status, SF_STATUS_SUCCESS); - - SF_BIND_OUTPUT c2 = {0}; - char c2buf[1024]; - c2.idx = 2; - c2.c_type = SF_C_TYPE_STRING; - c2.value = (void *) c2buf; - c2.len = sizeof(c2buf); - c2.max_length = sizeof(c2buf); - status = snowflake_bind_result(sfstmt, &c2); - if (status != SF_STATUS_SUCCESS) { - dump_error(&(sfstmt->error)); - } - assert_int_equal(status, SF_STATUS_SUCCESS); + char *c2buf = NULL; + sf_bool is_null; assert_int_equal(snowflake_num_rows(sfstmt), no_error_test_cases); int counter = 0; @@ -155,10 +132,14 @@ void test_timestamp_ltz(void** unused) { assert_int_equal(status, SF_STATUS_SUCCESS); if (v.c2out == NULL) { // expecting NULL - assert_true(c2.is_null); + snowflake_column_is_null(sfstmt, 2, &is_null); + assert_true(is_null); } else { // expecting not null - assert_string_equal(v.c2out, c2.value); + snowflake_column_as_str(sfstmt, 2, &c2buf, NULL); + assert_string_equal(v.c2out, c2buf); + free(c2buf); + c2buf = NULL; } } if (status != SF_STATUS_EOF) { diff --git a/tests/test_timestamp_ntz.c b/tests/test_timestamp_ntz.c index 6ce48b5baa..c7f1fcaa9e 100644 --- a/tests/test_timestamp_ntz.c +++ b/tests/test_timestamp_ntz.c @@ -112,31 +112,8 @@ void test_timestamp_ntz(void **unused) { } assert_int_equal(status, SF_STATUS_SUCCESS); - SF_BIND_OUTPUT c1 = {0}; - char c1buf[1024]; - c1.idx = 1; - c1.c_type = SF_C_TYPE_STRING; - c1.value = (void *) c1buf; - c1.len = sizeof(c1buf); - c1.max_length = sizeof(c1buf); - status = snowflake_bind_result(sfstmt, &c1); - if (status != SF_STATUS_SUCCESS) { - dump_error(&(sfstmt->error)); - } - assert_int_equal(status, SF_STATUS_SUCCESS); - - SF_BIND_OUTPUT c2 = {0}; - char c2buf[1024]; - c2.idx = 2; - c2.c_type = SF_C_TYPE_STRING; - c2.value = (void *) c2buf; - c2.len = sizeof(c2buf); - c2.max_length = sizeof(c2buf); - status = snowflake_bind_result(sfstmt, &c2); - if (status != SF_STATUS_SUCCESS) { - dump_error(&(sfstmt->error)); - } - assert_int_equal(status, SF_STATUS_SUCCESS); + char *c2buf = NULL; + sf_bool is_null; assert_int_equal(snowflake_num_rows(sfstmt), no_error_test_cases); int counter = 0; @@ -149,10 +126,14 @@ void test_timestamp_ntz(void **unused) { assert_int_equal(status, SF_STATUS_SUCCESS); if (v.c2out == NULL) { // expecting NULL - assert_true(c2.is_null); + snowflake_column_is_null(sfstmt, 2, &is_null); + assert_true(is_null); } else { // expecting not null - assert_string_equal(v.c2out, c2.value); + snowflake_column_as_str(sfstmt, 2, &c2buf, NULL); + assert_string_equal(v.c2out, c2buf); + free(c2buf); + c2buf = NULL; } } if (status != SF_STATUS_EOF) { diff --git a/tests/test_timestamp_tz.c b/tests/test_timestamp_tz.c index fe95cd4e2d..b1da173f7d 100644 --- a/tests/test_timestamp_tz.c +++ b/tests/test_timestamp_tz.c @@ -119,31 +119,8 @@ void test_timestamp_tz(void **unused) { } assert_int_equal(status, SF_STATUS_SUCCESS); - SF_BIND_OUTPUT c1 = {0}; - char c1buf[1024]; - c1.idx = 1; - c1.c_type = SF_C_TYPE_STRING; - c1.value = (void *) c1buf; - c1.len = sizeof(c1buf); - c1.max_length = sizeof(c1buf); - status = snowflake_bind_result(sfstmt, &c1); - if (status != SF_STATUS_SUCCESS) { - dump_error(&(sfstmt->error)); - } - assert_int_equal(status, SF_STATUS_SUCCESS); - - SF_BIND_OUTPUT c2 = {0}; - char c2buf[1024]; - c2.idx = 2; - c2.c_type = SF_C_TYPE_STRING; - c2.value = (void *) c2buf; - c2.len = sizeof(c2buf); - c2.max_length = sizeof(c2buf); - status = snowflake_bind_result(sfstmt, &c2); - if (status != SF_STATUS_SUCCESS) { - dump_error(&(sfstmt->error)); - } - assert_int_equal(status, SF_STATUS_SUCCESS); + char *c2buf = NULL; + sf_bool is_null; assert_int_equal(snowflake_num_rows(sfstmt), no_error_test_cases); int counter = 0; @@ -156,10 +133,14 @@ void test_timestamp_tz(void **unused) { assert_int_equal(status, SF_STATUS_SUCCESS); if (v.c2out == NULL) { // expecting NULL - assert_true(c2.is_null); + snowflake_column_is_null(sfstmt, 2, &is_null); + assert_true(is_null); } else { // expecting not null - assert_string_equal(v.c2out, c2.value); + snowflake_column_as_str(sfstmt, 2, &c2buf, NULL); + assert_string_equal(v.c2out, c2buf); + free(c2buf); + c2buf = NULL; } } if (status != SF_STATUS_EOF) { diff --git a/tests/test_timezone.c b/tests/test_timezone.c index 8339399024..f9daf53cb3 100644 --- a/tests/test_timezone.c +++ b/tests/test_timezone.c @@ -31,18 +31,12 @@ void test_timezone(void **unused) { } assert_int_equal(status, SF_STATUS_SUCCESS); - SF_BIND_OUTPUT c2 = {0}; - char c2buf[1024]; - c2.idx = 2; - c2.c_type = SF_C_TYPE_STRING; - c2.value = (void *) c2buf; - c2.len = sizeof(c2buf); - c2.max_length = sizeof(c2buf); - snowflake_bind_result(sfstmt, &c2); + const char *c2buf = NULL; assert_int_equal(snowflake_num_rows(sfstmt), 1); while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { - assert_string_equal(local_timezone, c2.value); + snowflake_column_as_const_str(sfstmt, 2, &c2buf); + assert_string_equal(local_timezone, c2buf); } if (status != SF_STATUS_EOF) { dump_error(&(sfstmt->error)); diff --git a/tests/test_transaction.c b/tests/test_transaction.c index 3935648377..7364d36319 100644 --- a/tests/test_transaction.c +++ b/tests/test_transaction.c @@ -109,19 +109,7 @@ void test_transaction(void **unused) { dump_error(&(sfstmt->error)); } assert_int_equal(status, SF_STATUS_SUCCESS); - - SF_BIND_OUTPUT v1 = {0}; - - v1.idx = 1; - v1.c_type = SF_C_TYPE_INT64; - v1.value = &v; - v1.max_length = sizeof(v1); - status = snowflake_bind_result(sfstmt, &v1); - if (status != SF_STATUS_SUCCESS) { - dump_error(&(sfstmt->error)); - } - assert_int_equal(status, SF_STATUS_SUCCESS); - + status = snowflake_fetch(sfstmt); if (status != SF_STATUS_SUCCESS) { dump_error(&(sfstmt->error)); @@ -142,7 +130,6 @@ void test_transaction(void **unused) { } assert_int_equal(status, SF_STATUS_SUCCESS); - status = snowflake_bind_result(sfstmt, &v1); if (status != SF_STATUS_SUCCESS) { dump_error(&(sfstmt->error)); } @@ -153,6 +140,7 @@ void test_transaction(void **unused) { dump_error(&(sfstmt->error)); } assert_int_equal(status, SF_STATUS_SUCCESS); + snowflake_column_as_int64(sfstmt, 1, &v); assert_int_equal(v, 3); status = snowflake_query( diff --git a/tests/test_variant.c b/tests/test_variant.c index 9ee87d293a..ab5410b176 100644 --- a/tests/test_variant.c +++ b/tests/test_variant.c @@ -78,38 +78,18 @@ void test_variant(void **unused) { } assert_int_equal(status, SF_STATUS_SUCCESS); - SF_BIND_OUTPUT c1 = {0}; - char c1buf[1024]; - c1.idx = 1; - c1.c_type = SF_C_TYPE_STRING; - c1.value = (void *) c1buf; - c1.len = sizeof(c1buf); - c1.max_length = sizeof(c1buf); - snowflake_bind_result(sfstmt, &c1); - - SF_BIND_OUTPUT c2 = {0}; - char c2buf[1024]; - c2.idx = 2; - c2.c_type = SF_C_TYPE_STRING; - c2.value = (void *) c2buf; - c2.len = sizeof(c2buf); - c2.max_length = sizeof(c2buf); - snowflake_bind_result(sfstmt, &c2); - - SF_BIND_OUTPUT c3 = {0}; - char c3buf[1024]; - c3.idx = 3; - c3.c_type = SF_C_TYPE_STRING; - c3.value = (void *) c3buf; - c3.max_length = sizeof(c3buf); - snowflake_bind_result(sfstmt, &c3); - + const char *c1buf = NULL; + const char *c2buf = NULL; + const char *c3buf = NULL; assert_int_equal(snowflake_num_rows(sfstmt), 1); while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { - assert_string_equal(c1.value, "{\n \"test1\": 1\n}"); - assert_string_equal(c2.value, "[\n \"[1,2,3]\"\n]"); - assert_string_equal(c3.value, "\"[456,789]\""); + snowflake_column_as_const_str(sfstmt, 1, &c1buf); + snowflake_column_as_const_str(sfstmt, 2, &c2buf); + snowflake_column_as_const_str(sfstmt, 3, &c3buf); + assert_string_equal(c1buf, "{\n \"test1\": 1\n}"); + assert_string_equal(c2buf, "[\n \"[1,2,3]\"\n]"); + assert_string_equal(c3buf, "\"[456,789]\""); } if (status != SF_STATUS_EOF) { dump_error(&(sfstmt->error)); From 5e7fb92b9fb2a8284edfe7b6633678dac4e8cad4 Mon Sep 17 00:00:00 2001 From: notkriswagner Date: Sun, 16 Sep 2018 12:45:29 -0700 Subject: [PATCH 09/16] Fixed test errors from new API change. --- tests/test_bool.c | 4 ++-- tests/test_crud.c | 1 + tests/test_perf_string_reads_and_writes.c | 5 ++++- tests/test_time.c | 8 +++++--- tests/test_timestamp_ltz.c | 2 +- tests/test_timestamp_tz.c | 2 +- 6 files changed, 14 insertions(+), 8 deletions(-) diff --git a/tests/test_bool.c b/tests/test_bool.c index ce6bb0cc1f..b75ba3d18f 100644 --- a/tests/test_bool.c +++ b/tests/test_bool.c @@ -22,7 +22,7 @@ void test_bool(void **unused) { {.c1in = 2, .c2in = &SF_BOOLEAN_FALSE, .c2out = "", .c2_is_null=SF_BOOLEAN_FALSE}, {.c1in = 3, .c2in = &large_value, .c2out = "1", .c2_is_null=SF_BOOLEAN_FALSE}, {.c1in = 4, .c2in = &zero_value, .c2out = "", .c2_is_null=SF_BOOLEAN_FALSE}, - {.c1in = 5, .c2in = NULL, .c2out = "", .c2_is_null=SF_BOOLEAN_TRUE}, + {.c1in = 5, .c2in = NULL, .c2out = NULL, .c2_is_null=SF_BOOLEAN_TRUE}, {.c1in = 6, .c2in = &negative_value, .c2out = "1", .c2_is_null=SF_BOOLEAN_FALSE} }; @@ -109,7 +109,7 @@ void test_bool(void **unused) { while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { snowflake_column_as_int64(sfstmt, 1, &c1); TEST_CASE_TO_STRING v = test_cases[c1 - 1]; - if (v.c2out != NULL) { + if (v.c2in != NULL) { snowflake_column_as_str(sfstmt, 2, &c2, NULL); assert_string_equal(v.c2out, c2); free(c2); diff --git a/tests/test_crud.c b/tests/test_crud.c index 217893b9ed..e5a709083c 100644 --- a/tests/test_crud.c +++ b/tests/test_crud.c @@ -46,6 +46,7 @@ void _fetch_data(SF_STMT *sfstmt, int64 expected_sum) { int64 total = 0; while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { // printf("c1: %lld, c2: %s\n", c1v, c2v); + snowflake_column_as_int64(sfstmt, 1, &c1); total += c1; } assert_int_equal(total, expected_sum); diff --git a/tests/test_perf_string_reads_and_writes.c b/tests/test_perf_string_reads_and_writes.c index b0de1a531c..f0d22e77bb 100644 --- a/tests/test_perf_string_reads_and_writes.c +++ b/tests/test_perf_string_reads_and_writes.c @@ -157,6 +157,7 @@ void test_col_buffer_copy_concat_multiple_rows(void **unused) { if (!books[id].title) { snowflake_column_as_str(sfstmt, 2, &title, NULL); books[id].title = title; + title = NULL; } // Copy text into the right buffer snowflake_column_as_const_str(sfstmt, 3, &text); @@ -183,7 +184,9 @@ void test_col_buffer_copy_concat_multiple_rows(void **unused) { } } - free(title); + if (title) { + free(title); + } snowflake_stmt_term(sfstmt); snowflake_term(sf); } diff --git a/tests/test_time.c b/tests/test_time.c index 9f82031047..7d130acdea 100644 --- a/tests/test_time.c +++ b/tests/test_time.c @@ -102,17 +102,19 @@ void test_time(void **unused) { dump_error(&(sfstmt->error)); } assert_int_equal(status, SF_STATUS_SUCCESS); - + int64 c1_out; - const char *c2_out = NULL; + char *c2_out = NULL; assert_int_equal(snowflake_num_rows(sfstmt), no_error_test_cases); while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { snowflake_column_as_int64(sfstmt, 1, &c1_out); - snowflake_column_as_const_str(sfstmt, 2, &c2_out); + snowflake_column_as_str(sfstmt, 2, &c2_out, NULL); TEST_CASE_TO_STRING v = test_cases[c1_out - 1]; assert_int_equal(status, SF_STATUS_SUCCESS); assert_string_equal(v.c2out, c2_out); + free(c2_out); + c2_out = NULL; } if (status != SF_STATUS_EOF) { dump_error(&(sfstmt->error)); diff --git a/tests/test_timestamp_ltz.c b/tests/test_timestamp_ltz.c index 68da344c56..b354225a85 100644 --- a/tests/test_timestamp_ltz.c +++ b/tests/test_timestamp_ltz.c @@ -31,7 +31,7 @@ void test_timestamp_ltz(void** unused) { {.c1in = 6, .c2in = "9999-01-01 00:00:00.0000", .c2out = "9999-01-01 00:00:00.00000"}, {.c1in = 7, .c2in = "99999-12-31 23:59:59.9999", .c2out = "", .error_code=100035}, #endif // _WIN32 - {.c1in = 8, .c2in = NULL, .c2out = ""}, + {.c1in = 8, .c2in = NULL, .c2out = NULL}, /* // none of the platform supports this {.c1in = 9, .c2in = "9999-12-31 23:59:59.9999", .c2out = "9999-12-31 23:59:59.99990 -05:00"}, */ diff --git a/tests/test_timestamp_tz.c b/tests/test_timestamp_tz.c index b1da173f7d..2ec7e3fbd5 100644 --- a/tests/test_timestamp_tz.c +++ b/tests/test_timestamp_tz.c @@ -31,7 +31,7 @@ void test_timestamp_tz(void **unused) { #endif // __APPLE__ {.c1in = 6, .c2in = "9999-01-01 00:00:00.0000", .c2out = "9999-01-01 00:00:00.00000 -05:00"}, {.c1in = 7, .c2in = "99999-12-31 23:59:59.9999", .c2out = "", .error_code=100035}, - {.c1in = 8, .c2in = NULL, .c2out = ""}, + {.c1in = 8, .c2in = NULL, .c2out = NULL}, {.c1in = 9, .c2in = "2030-07-16 09:01:12.0987 +04:30", .c2out = "2030-07-16 09:01:12.09870 +04:30"}, {.c1in = 10, .c2in = "1804-10-23 13:08:48.8765 +04:30", .c2out = "1804-10-23 13:08:48.87650 +04:30"}, {.c1in = 11, .c2in = "1969-11-21 08:19:34.123 -02:30", .c2out = "1969-11-21 08:19:34.12300 -02:30"}, From e9e03a373fef008ec4c85ec322de2ec8fcfd3ce1 Mon Sep 17 00:00:00 2001 From: notkriswagner Date: Sun, 16 Sep 2018 13:53:36 -0700 Subject: [PATCH 10/16] Replaced erroneous call to snowflake_cJSON_free with snowflake_cJSON_Delete to fix memory leak. --- lib/client.c | 11 +++++------ tests/test_perf_type_conversion.c | 10 +++++----- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/lib/client.c b/lib/client.c index c5816988b4..63ca22a26c 100644 --- a/lib/client.c +++ b/lib/client.c @@ -1218,7 +1218,10 @@ SF_STATUS STDCALL snowflake_fetch(SF_STMT *sfstmt) { sf_bool get_chunk_success = SF_BOOLEAN_TRUE; int64 i; uint64 index; - cJSON *row = NULL; + if (sfstmt->cur_row != NULL) { + snowflake_cJSON_Delete(sfstmt->cur_row); + sfstmt->cur_row = NULL; + } // Check for chunk_downloader error if (sfstmt->chunk_downloader && get_error(sfstmt->chunk_downloader)) { @@ -1295,11 +1298,7 @@ SF_STATUS STDCALL snowflake_fetch(SF_STMT *sfstmt) { } // Get next result row - row = snowflake_cJSON_DetachItemFromArray(sfstmt->raw_results, 0); - if (sfstmt->cur_row != NULL) { - snowflake_cJSON_free(sfstmt->cur_row); - } - sfstmt->cur_row = row; + sfstmt->cur_row = snowflake_cJSON_DetachItemFromArray(sfstmt->raw_results, 0); sfstmt->chunk_rowcount--; sfstmt->total_row_index++; ret = SF_STATUS_SUCCESS; diff --git a/tests/test_perf_type_conversion.c b/tests/test_perf_type_conversion.c index 7bc271b4f1..1d6c77234f 100644 --- a/tests/test_perf_type_conversion.c +++ b/tests/test_perf_type_conversion.c @@ -18,7 +18,7 @@ void test_col_conv_int64_type(void **unused) { setup_and_run_query(&sf, &sfstmt, "select seq4() from table(generator(rowcount=>12000));"); clock_gettime(clk_id, &begin); - + int64 out = 0; while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { snowflake_column_as_int64(sfstmt, 1, &out); @@ -42,7 +42,7 @@ void test_col_conv_float64_type(void **unused) { setup_and_run_query(&sf, &sfstmt, "select as_double(10.01) from table(generator(rowcount=>12000));"); clock_gettime(clk_id, &begin); - + float64 out = 0; while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { snowflake_column_as_float64(sfstmt, 1, &out); @@ -67,7 +67,7 @@ void test_col_conv_str_type(void **unused) { // Begin timing clock_gettime(clk_id, &begin); - + const char *out; while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { snowflake_column_as_const_str(sfstmt, 1, &out); @@ -93,7 +93,7 @@ void test_col_conv_timestamp_type(void **unused) { // Begin timing clock_gettime(clk_id, &begin); - + char *out = NULL; while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { snowflake_column_as_str(sfstmt, 1, &out, NULL); @@ -123,7 +123,7 @@ void test_col_conv_multi_type(void **unused) { const char *out_str; int64 out_int; - + while ((status = snowflake_fetch(sfstmt)) == SF_STATUS_SUCCESS) { // Bind different result type every time to convert to multiple types switch(sfstmt->total_row_index % 2) { From fb01d71e458676c479737284b048d090ccd67fb8 Mon Sep 17 00:00:00 2001 From: notkriswagner Date: Mon, 17 Sep 2018 15:57:46 -0700 Subject: [PATCH 11/16] Added more NULL checking to the new fetch functions. Updated Doc String comments. --- include/snowflake/client.h | 148 +++++++++++++---- lib/client.c | 326 ++++++++++++++++++++++++++----------- tests/test_column_fetch.c | 2 +- 3 files changed, 350 insertions(+), 126 deletions(-) diff --git a/include/snowflake/client.h b/include/snowflake/client.h index 106126e0eb..a47816a021 100644 --- a/include/snowflake/client.h +++ b/include/snowflake/client.h @@ -126,8 +126,8 @@ typedef enum SF_STATUS { SF_STATUS_ERROR_OUT_OF_BOUNDS = 240019, SF_STATUS_ERROR_MISSING_COLUMN_IN_ROW = 240020, SF_STATUS_ERROR_INVALID_CONVERSION = 240021, - SF_STATUS_ERROR_OUT_OF_RANGE = 240022 - + SF_STATUS_ERROR_OUT_OF_RANGE = 240022, + SF_STATUS_ERROR_NULL_POINTER = 240023, } SF_STATUS; /** @@ -678,59 +678,145 @@ const char *STDCALL snowflake_c_type_to_string(SF_C_TYPE type); SF_STATUS STDCALL _snowflake_check_connection_parameters(SF_CONNECT *sf); /** - * Converts a column in the current row into a boolean value (if a valid conversion exists) + * Converts a column in the current row into a boolean value (if a valid conversion exists). + * A NULL column will evaluate to false. * - * @param stmt SF_STMT context + * @param sfstmt SF_STMT context * @param idx Column index * @param value_ptr Coverted column data is stored in this pointer (if conversion was successful) * @return 0 if success, otherwise an errno is returned */ -SF_STATUS STDCALL snowflake_column_as_boolean(SF_STMT *stmt, int idx, sf_bool *value_ptr); - - -SF_STATUS STDCALL snowflake_column_as_uint8(SF_STMT *stmt, int idx, uint8 *value_ptr); - - -SF_STATUS STDCALL snowflake_column_as_uint32(SF_STMT *stmt, int idx, uint32 *value_ptr); - - -SF_STATUS STDCALL snowflake_column_as_uint64(SF_STMT *stmt, int idx, uint64 *value_ptr); - - -SF_STATUS STDCALL snowflake_column_as_int8(SF_STMT *stmt, int idx, int8 *value_ptr); +SF_STATUS STDCALL snowflake_column_as_boolean(SF_STMT *sfstmt, int idx, sf_bool *value_ptr); +/** + * Stores the first character of the column in a uint8 variable. A NULL column will evaluate to 0 + * + * @param sfstmt SF_STMT context + * @param idx Column index + * @param value_ptr Coverted column data is stored in this pointer (if conversion was successful) + * @return 0 if success, otherwise an errno is returned + */ +SF_STATUS STDCALL snowflake_column_as_uint8(SF_STMT *sfstmt, int idx, uint8 *value_ptr); -SF_STATUS STDCALL snowflake_column_as_int32(SF_STMT *stmt, int idx, int32 *value_ptr); +/** + * Converts a column in the current row into a uint32 value (if a valid conversion exists). + * A NULL column will evaluate to 0. + * + * @param sfstmt SF_STMT context + * @param idx Column index + * @param value_ptr Coverted column data is stored in this pointer (if conversion was successful) + * @return 0 if success, otherwise an errno is returned + */ +SF_STATUS STDCALL snowflake_column_as_uint32(SF_STMT *sfstmt, int idx, uint32 *value_ptr); +/** + * Converts a column in the current row into a uint64 value (if a valid conversion exists). + * A NULL column will evaluate to 0. + * + * @param sfstmt SF_STMT context + * @param idx Column index + * @param value_ptr Coverted column data is stored in this pointer (if conversion was successful) + * @return 0 if success, otherwise an errno is returned + */ +SF_STATUS STDCALL snowflake_column_as_uint64(SF_STMT *sfstmt, int idx, uint64 *value_ptr); -SF_STATUS STDCALL snowflake_column_as_int64(SF_STMT *stmt, int idx, int64 *value_ptr); +/** + * Stores the first character of the column in a int8 variable. A NULL column will evaluate to 0 + * + * @param sfstmt SF_STMT context + * @param idx Column index + * @param value_ptr Coverted column data is stored in this pointer (if conversion was successful) + * @return 0 if success, otherwise an errno is returned + */ +SF_STATUS STDCALL snowflake_column_as_int8(SF_STMT *sfstmt, int idx, int8 *value_ptr); +/** + * Converts a column in the current row into a int32 value (if a valid conversion exists). + * A NULL column will evaluate to 0. + * + * @param sfstmt SF_STMT context + * @param idx Column index + * @param value_ptr Coverted column data is stored in this pointer (if conversion was successful) + * @return 0 if success, otherwise an errno is returned + */ +SF_STATUS STDCALL snowflake_column_as_int32(SF_STMT *sfstmt, int idx, int32 *value_ptr); -SF_STATUS STDCALL snowflake_column_as_float32(SF_STMT *stmt, int idx, float32 *value_ptr); +/** + * Converts a column in the current row into a int64 value (if a valid conversion exists). + * A NULL column will evaluate to 0. + * + * @param sfstmt SF_STMT context + * @param idx Column index + * @param value_ptr Coverted column data is stored in this pointer (if conversion was successful) + * @return 0 if success, otherwise an errno is returned + */ +SF_STATUS STDCALL snowflake_column_as_int64(SF_STMT *sfstmt, int idx, int64 *value_ptr); +/** + * Converts a column in the current row into a float32 value (if a valid conversion exists). + * A NULL column will evaluate to 0.0. + * + * @param sfstmt SF_STMT context + * @param idx Column index + * @param value_ptr Coverted column data is stored in this pointer (if conversion was successful) + * @return 0 if success, otherwise an errno is returned + */ +SF_STATUS STDCALL snowflake_column_as_float32(SF_STMT *sfstmt, int idx, float32 *value_ptr); -SF_STATUS STDCALL snowflake_column_as_float64(SF_STMT *stmt, int idx, float64 *value_ptr); +/** + * Converts a column in the current row into a float64 value (if a valid conversion exists). + * A NULL column will evaluate to 0.0. + * + * @param sfstmt SF_STMT context + * @param idx Column index + * @param value_ptr Coverted column data is stored in this pointer (if conversion was successful) + * @return 0 if success, otherwise an errno is returned + */ +SF_STATUS STDCALL snowflake_column_as_float64(SF_STMT *sfstmt, int idx, float64 *value_ptr); /** * Returns the raw column data in the form of a const char pointer that the user can then use - * to read the string data or copy to another buffer + * to read the string data or copy to another buffer. A NULL column will return a NULL pointer + * + * @param sfstmt SF_STMT context + * @param idx Column index + * @param value_ptr Raw column data is stored in this pointer + * @return 0 if success, otherwise an errno is returned */ +SF_STATUS STDCALL snowflake_column_as_const_str(SF_STMT *sfstmt, int idx, const char **value_ptr); -SF_STATUS STDCALL snowflake_column_as_const_str(SF_STMT *stmt, int idx, const char **value_ptr); - -SF_STATUS STDCALL snowflake_column_as_str(SF_STMT *stmt, int idx, char **value_ptr, size_t *value_len_ptr); +/** + * Allocates a new string buffer to store the result and stores that buffer address in value_ptr. + * The user must pass this buffer to free() once they are done using it, this memory is NOT freed by the library. + * Buffer pointed to by value_ptr will not be free'd by the library. + * + * @param sfstmt SF_STMT context + * @param idx Column index + * @param value_ptr Copied Column data is stored in this pointer (if conversion was successful) + * @param value_len_ptr + * @return + */ +SF_STATUS STDCALL snowflake_column_as_str(SF_STMT *sfstmt, int idx, char **value_ptr, size_t *value_len_ptr); /** * Returns the length of the raw column data * - * @param stmt - * @param idx - * @return + * @param sfstmt SF_STMT context + * @param idx Column index + * @param value_ptr Pointer to the length of the raw column data + * @return 0 if success, otherwise an errno is returned */ -SF_STATUS STDCALL snowflake_column_strlen(SF_STMT *stmt, int idx, size_t *value_ptr); +SF_STATUS STDCALL snowflake_column_strlen(SF_STMT *sfstmt, int idx, size_t *value_ptr); -// Returns whether or not the column data is null -SF_STATUS STDCALL snowflake_column_is_null(SF_STMT *stmt, int idx, sf_bool *value_ptr); +/** + * Returns whether or not the column data is null + * + * @param sfstmt SF_STMT context + * @param idx Column index + * @param value_ptr Column's NULL status is stored in this pointer + * @return 0 if success, otherwise an errno is returned + */ +SF_STATUS STDCALL snowflake_column_is_null(SF_STMT *sfstmt, int idx, sf_bool *value_ptr); #ifdef __cplusplus diff --git a/lib/client.c b/lib/client.c index 63ca22a26c..d381489e7e 100644 --- a/lib/client.c +++ b/lib/client.c @@ -1830,23 +1830,19 @@ SF_STATUS STDCALL snowflake_propagate_error(SF_CONNECT *sf, SF_STMT *sfstmt) { return SF_STATUS_SUCCESS; } - -/** - * - * Start of new fetch API - * - */ - - // Make sure that idx is in bounds and that column exists -SF_STATUS STDCALL _snowflake_get_column(SF_STMT *stmt, int idx, cJSON **column_ptr) { - if (idx > snowflake_num_fields(stmt) || idx <= 0) { +SF_STATUS STDCALL _snowflake_get_cJSON_column(SF_STMT *sfstmt, int idx, cJSON **column_ptr) { + if (idx > snowflake_num_fields(sfstmt) || idx <= 0) { + SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_OUT_OF_BOUNDS, + "Column index must be between 1 and snowflake_num_fields()", "", sfstmt->sfqid); return SF_STATUS_ERROR_OUT_OF_BOUNDS; } - cJSON *column = snowflake_cJSON_GetArrayItem(stmt->cur_row, idx - 1); + cJSON *column = snowflake_cJSON_GetArrayItem(sfstmt->cur_row, idx - 1); if (!column) { *column_ptr = NULL; + SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_MISSING_COLUMN_IN_ROW, + "Column is missing from row.", "", sfstmt->sfqid); return SF_STATUS_ERROR_MISSING_COLUMN_IN_ROW; } @@ -1854,48 +1850,77 @@ SF_STATUS STDCALL _snowflake_get_column(SF_STMT *stmt, int idx, cJSON **column_p return SF_STATUS_SUCCESS; } -SF_STATUS STDCALL snowflake_column_as_boolean(SF_STMT *stmt, int idx, sf_bool *value_ptr) { +// Does NULL checking and clears the SF_STMT error struct +SF_STATUS STDCALL _snowflake_column_null_checks(SF_STMT *sfstmt, void *value_ptr) { + if (!sfstmt) { + return SF_STATUS_ERROR_STATEMENT_NOT_EXIST; + } + clear_snowflake_error(&sfstmt->error); + if (value_ptr == NULL) { + SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_NULL_POINTER, + "value_ptr must not be NULL", "", sfstmt->sfqid); + return SF_STATUS_ERROR_NULL_POINTER; + } + + return SF_STATUS_SUCCESS; +} + +SF_STATUS STDCALL snowflake_column_as_boolean(SF_STMT *sfstmt, int idx, sf_bool *value_ptr) { SF_STATUS status; cJSON *column = NULL; + if ((status = _snowflake_column_null_checks(sfstmt, (void *) value_ptr)) != SF_STATUS_SUCCESS) { + return status; + } // Get column - if ((status = _snowflake_get_column(stmt, idx, &column)) != SF_STATUS_SUCCESS) { + if ((status = _snowflake_get_cJSON_column(sfstmt, idx, &column)) != SF_STATUS_SUCCESS) { return status; } sf_bool value = SF_BOOLEAN_FALSE; if (snowflake_cJSON_IsNull(column)) { - *value_ptr = value; - return SF_STATUS_SUCCESS; + status = SF_STATUS_SUCCESS; + goto cleanup; } char *endptr; - switch (stmt->desc[idx - 1].c_type) { + errno = 0; + switch (sfstmt->desc[idx - 1].c_type) { case SF_C_TYPE_BOOLEAN: value = strcmp("1", column->valuestring) == 0 ? SF_BOOLEAN_TRUE: SF_BOOLEAN_FALSE; break; - case SF_C_TYPE_FLOAT64: - errno = 0; + case SF_C_TYPE_FLOAT64: ; float64 float_val = strtod(column->valuestring, &endptr); // Check for errors if (endptr == column->valuestring) { - return SF_STATUS_ERROR_INVALID_CONVERSION; + SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_INVALID_CONVERSION, + "Cannot convert value into boolean from float64", "", sfstmt->sfqid); + status = SF_STATUS_ERROR_INVALID_CONVERSION; + goto cleanup; } - if ((float_val == SF_HUGE_VAL || float_val == -SF_HUGE_VAL) && errno == ERANGE) { - return SF_STATUS_ERROR_OUT_OF_RANGE; + if (errno == ERANGE || float_val == INFINITY || float_val == -INFINITY) { + SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_OUT_OF_RANGE, + "Value out of range for float64. Cannot convert value into boolean", "", sfstmt->sfqid); + status = SF_STATUS_ERROR_OUT_OF_RANGE; + goto cleanup; } // Determine if true or false value = (float_val == 0.0) ? SF_BOOLEAN_FALSE : SF_BOOLEAN_TRUE; break; - case SF_C_TYPE_INT64: - errno = 0; + case SF_C_TYPE_INT64: ; int64 int_val = strtoll(column->valuestring, &endptr, 10); // Check for errors if (endptr == column->valuestring) { - return SF_STATUS_ERROR_INVALID_CONVERSION; + SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_INVALID_CONVERSION, + "Cannot convert value into boolean from int64", "", sfstmt->sfqid); + status = SF_STATUS_ERROR_INVALID_CONVERSION; + goto cleanup; } if ((int_val == SF_INT64_MAX || int_val == SF_INT64_MIN) && errno == ERANGE) { - return SF_STATUS_ERROR_OUT_OF_RANGE; + SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_OUT_OF_RANGE, + "Value out of range for int64. Cannot convert value into boolean", "", sfstmt->sfqid); + status = SF_STATUS_ERROR_OUT_OF_RANGE; + goto cleanup; } // Determine if true or false value = (int_val == 0) ? SF_BOOLEAN_FALSE : SF_BOOLEAN_TRUE; @@ -1908,18 +1933,27 @@ SF_STATUS STDCALL snowflake_column_as_boolean(SF_STMT *stmt, int idx, sf_bool *v } break; default: - return SF_STATUS_ERROR_INVALID_CONVERSION; + SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_INVALID_CONVERSION, + "No valid conversion to boolean from data type", "", sfstmt->sfqid); + status = SF_STATUS_ERROR_INVALID_CONVERSION; + goto cleanup; } + +cleanup: *value_ptr = value; - return SF_STATUS_SUCCESS; + return status; } -SF_STATUS STDCALL snowflake_column_as_uint8(SF_STMT *stmt, int idx, uint8 *value_ptr) { +SF_STATUS STDCALL snowflake_column_as_uint8(SF_STMT *sfstmt, int idx, uint8 *value_ptr) { SF_STATUS status; cJSON *column = NULL; + if ((status = _snowflake_column_null_checks(sfstmt, (void *) value_ptr)) != SF_STATUS_SUCCESS) { + return status; + } + // Get column - if ((status = _snowflake_get_column(stmt, idx, &column)) != SF_STATUS_SUCCESS) { + if ((status = _snowflake_get_cJSON_column(sfstmt, idx, &column)) != SF_STATUS_SUCCESS) { return status; } @@ -1927,19 +1961,23 @@ SF_STATUS STDCALL snowflake_column_as_uint8(SF_STMT *stmt, int idx, uint8 *value return SF_STATUS_SUCCESS; } -SF_STATUS STDCALL snowflake_column_as_uint32(SF_STMT *stmt, int idx, uint32 *value_ptr) { +SF_STATUS STDCALL snowflake_column_as_uint32(SF_STMT *sfstmt, int idx, uint32 *value_ptr) { SF_STATUS status; cJSON *column = NULL; + if ((status = _snowflake_column_null_checks(sfstmt, (void *) value_ptr)) != SF_STATUS_SUCCESS) { + return status; + } + // Get column - if ((status = _snowflake_get_column(stmt, idx, &column)) != SF_STATUS_SUCCESS) { + if ((status = _snowflake_get_cJSON_column(sfstmt, idx, &column)) != SF_STATUS_SUCCESS) { return status; } - uint32 value = 0; + uint64 value = 0; if (snowflake_cJSON_IsNull(column)) { - *value_ptr = value; - return SF_STATUS_SUCCESS; + status = SF_STATUS_SUCCESS; + goto cleanup; } char *endptr; @@ -1947,29 +1985,42 @@ SF_STATUS STDCALL snowflake_column_as_uint32(SF_STMT *stmt, int idx, uint32 *val value = strtoul(column->valuestring, &endptr, 10); // Check for errors if (endptr == column->valuestring) { - return SF_STATUS_ERROR_INVALID_CONVERSION; + SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_INVALID_CONVERSION, + "Cannot convert value into uint32", "", sfstmt->sfqid); + status = SF_STATUS_ERROR_INVALID_CONVERSION; + goto cleanup; } - if ((value == SF_UINT32_MAX || value == 0) && errno == ERANGE) { - return SF_STATUS_ERROR_OUT_OF_RANGE; + if (((value == ULONG_MAX || value == 0) && errno == ERANGE) || value > SF_UINT32_MAX) { + SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_OUT_OF_RANGE, + "Value out of range for uint32", "", sfstmt->sfqid); + status = SF_STATUS_ERROR_OUT_OF_RANGE; + goto cleanup; } // Everything checks out, set value and return success - *value_ptr = value; - return SF_STATUS_SUCCESS; + status = SF_STATUS_SUCCESS; + +cleanup: + *value_ptr = (uint32) value; + return status; } -SF_STATUS STDCALL snowflake_column_as_uint64(SF_STMT *stmt, int idx, uint64 *value_ptr) { +SF_STATUS STDCALL snowflake_column_as_uint64(SF_STMT *sfstmt, int idx, uint64 *value_ptr) { SF_STATUS status; cJSON *column = NULL; + if ((status = _snowflake_column_null_checks(sfstmt, (void *) value_ptr)) != SF_STATUS_SUCCESS) { + return status; + } + // Get column - if ((status = _snowflake_get_column(stmt, idx, &column)) != SF_STATUS_SUCCESS) { + if ((status = _snowflake_get_cJSON_column(sfstmt, idx, &column)) != SF_STATUS_SUCCESS) { return status; } uint64 value = 0; if (snowflake_cJSON_IsNull(column)) { - *value_ptr = value; - return SF_STATUS_SUCCESS; + status = SF_STATUS_SUCCESS; + goto cleanup; } char *endptr; @@ -1977,22 +2028,35 @@ SF_STATUS STDCALL snowflake_column_as_uint64(SF_STMT *stmt, int idx, uint64 *val value = strtoull(column->valuestring, &endptr, 10); // Check for errors if (endptr == column->valuestring) { - return SF_STATUS_ERROR_INVALID_CONVERSION; + SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_INVALID_CONVERSION, + "Cannot convert value into uint64", "", sfstmt->sfqid); + status = SF_STATUS_ERROR_INVALID_CONVERSION; + goto cleanup; } if ((value == SF_UINT64_MAX || value == 0) && errno == ERANGE) { - return SF_STATUS_ERROR_OUT_OF_RANGE; + SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_OUT_OF_RANGE, + "Value out of range for uint64", "", sfstmt->sfqid); + status = SF_STATUS_ERROR_OUT_OF_RANGE; + goto cleanup; } // Everything checks out, set value and return success + status = SF_STATUS_SUCCESS; + +cleanup: *value_ptr = value; - return SF_STATUS_SUCCESS; + return status; } -SF_STATUS STDCALL snowflake_column_as_int8(SF_STMT *stmt, int idx, int8 *value_ptr) { +SF_STATUS STDCALL snowflake_column_as_int8(SF_STMT *sfstmt, int idx, int8 *value_ptr) { SF_STATUS status; cJSON *column = NULL; + if ((status = _snowflake_column_null_checks(sfstmt, (void *) value_ptr)) != SF_STATUS_SUCCESS) { + return status; + } + // Get column - if ((status = _snowflake_get_column(stmt, idx, &column)) != SF_STATUS_SUCCESS) { + if ((status = _snowflake_get_cJSON_column(sfstmt, idx, &column)) != SF_STATUS_SUCCESS) { return status; } @@ -2000,19 +2064,23 @@ SF_STATUS STDCALL snowflake_column_as_int8(SF_STMT *stmt, int idx, int8 *value_p return SF_STATUS_SUCCESS; } -SF_STATUS STDCALL snowflake_column_as_int32(SF_STMT *stmt, int idx, int32 *value_ptr) { +SF_STATUS STDCALL snowflake_column_as_int32(SF_STMT *sfstmt, int idx, int32 *value_ptr) { SF_STATUS status; cJSON *column = NULL; + if ((status = _snowflake_column_null_checks(sfstmt, (void *) value_ptr)) != SF_STATUS_SUCCESS) { + return status; + } + // Get column - if ((status = _snowflake_get_column(stmt, idx, &column)) != SF_STATUS_SUCCESS) { + if ((status = _snowflake_get_cJSON_column(sfstmt, idx, &column)) != SF_STATUS_SUCCESS) { return status; } int64 value = 0; if (snowflake_cJSON_IsNull(column)) { - *value_ptr = (int32) value; - return SF_STATUS_SUCCESS; + status = SF_STATUS_SUCCESS; + goto cleanup; } char *endptr; @@ -2020,29 +2088,42 @@ SF_STATUS STDCALL snowflake_column_as_int32(SF_STMT *stmt, int idx, int32 *value value = strtol(column->valuestring, &endptr, 10); // Check for errors if (endptr == column->valuestring) { - return SF_STATUS_ERROR_INVALID_CONVERSION; + SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_INVALID_CONVERSION, + "Cannot convert value into int32", "", sfstmt->sfqid); + status = SF_STATUS_ERROR_INVALID_CONVERSION; + goto cleanup; } if (((value == LONG_MAX || value == LONG_MIN) && errno == ERANGE) || (value > SF_INT32_MAX || value < SF_INT32_MIN)) { - return SF_STATUS_ERROR_OUT_OF_RANGE; + SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_OUT_OF_RANGE, + "Value out of range for int32", "", sfstmt->sfqid); + status = SF_STATUS_ERROR_OUT_OF_RANGE; + goto cleanup; } // Everything checks out, set value and return success + status = SF_STATUS_SUCCESS; + +cleanup: *value_ptr = (int32) value; - return SF_STATUS_SUCCESS; + return status; } -SF_STATUS STDCALL snowflake_column_as_int64(SF_STMT *stmt, int idx, int64 *value_ptr) { +SF_STATUS STDCALL snowflake_column_as_int64(SF_STMT *sfstmt, int idx, int64 *value_ptr) { SF_STATUS status; cJSON *column = NULL; + if ((status = _snowflake_column_null_checks(sfstmt, (void *) value_ptr)) != SF_STATUS_SUCCESS) { + return status; + } + // Get column - if ((status = _snowflake_get_column(stmt, idx, &column)) != SF_STATUS_SUCCESS) { + if ((status = _snowflake_get_cJSON_column(sfstmt, idx, &column)) != SF_STATUS_SUCCESS) { return status; } int64 value = 0; if (snowflake_cJSON_IsNull(column)) { - *value_ptr = value; - return SF_STATUS_SUCCESS; + status = SF_STATUS_SUCCESS; + goto cleanup; } char *endptr; @@ -2050,29 +2131,42 @@ SF_STATUS STDCALL snowflake_column_as_int64(SF_STMT *stmt, int idx, int64 *value value = strtoll(column->valuestring, &endptr, 10); // Check for errors if (endptr == column->valuestring) { - return SF_STATUS_ERROR_INVALID_CONVERSION; + SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_INVALID_CONVERSION, + "Cannot convert value into int64", "", sfstmt->sfqid); + status = SF_STATUS_ERROR_INVALID_CONVERSION; + goto cleanup; } if ((value == SF_INT64_MAX || value == SF_INT64_MIN) && errno == ERANGE) { - return SF_STATUS_ERROR_OUT_OF_RANGE; + SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_OUT_OF_RANGE, + "Value out of range for int64", "", sfstmt->sfqid); + status = SF_STATUS_ERROR_OUT_OF_RANGE; + goto cleanup; } // Everything checks out, set value and return success + status = SF_STATUS_SUCCESS; + +cleanup: *value_ptr = value; - return SF_STATUS_SUCCESS; + return status; } -SF_STATUS STDCALL snowflake_column_as_float32(SF_STMT *stmt, int idx, float32 *value_ptr) { +SF_STATUS STDCALL snowflake_column_as_float32(SF_STMT *sfstmt, int idx, float32 *value_ptr) { SF_STATUS status; cJSON *column = NULL; + if ((status = _snowflake_column_null_checks(sfstmt, (void *) value_ptr)) != SF_STATUS_SUCCESS) { + return status; + } + // Get column - if ((status = _snowflake_get_column(stmt, idx, &column)) != SF_STATUS_SUCCESS) { + if ((status = _snowflake_get_cJSON_column(sfstmt, idx, &column)) != SF_STATUS_SUCCESS) { return status; } float32 value = 0.0; if (snowflake_cJSON_IsNull(column)) { - *value_ptr = value; - return SF_STATUS_SUCCESS; + status = SF_STATUS_SUCCESS; + goto cleanup; } char *endptr; @@ -2080,29 +2174,42 @@ SF_STATUS STDCALL snowflake_column_as_float32(SF_STMT *stmt, int idx, float32 *v value = strtof(column->valuestring, &endptr); // Check for errors if (endptr == column->valuestring) { - return SF_STATUS_ERROR_INVALID_CONVERSION; + SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_INVALID_CONVERSION, + "Cannot convert value into float32", "", sfstmt->sfqid); + status = SF_STATUS_ERROR_INVALID_CONVERSION; + goto cleanup; } if (errno == ERANGE || value == INFINITY || value == -INFINITY) { - return SF_STATUS_ERROR_OUT_OF_RANGE; + SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_OUT_OF_RANGE, + "Value out of range for float32", "", sfstmt->sfqid); + status = SF_STATUS_ERROR_OUT_OF_RANGE; + goto cleanup; } // Everything checks out, set value and return success + status = SF_STATUS_SUCCESS; + +cleanup: *value_ptr = value; - return SF_STATUS_SUCCESS; + return status; } -SF_STATUS STDCALL snowflake_column_as_float64(SF_STMT *stmt, int idx, float64 *value_ptr) { +SF_STATUS STDCALL snowflake_column_as_float64(SF_STMT *sfstmt, int idx, float64 *value_ptr) { SF_STATUS status; cJSON *column = NULL; + if ((status = _snowflake_column_null_checks(sfstmt, (void *) value_ptr)) != SF_STATUS_SUCCESS) { + return status; + } + // Get column - if ((status = _snowflake_get_column(stmt, idx, &column)) != SF_STATUS_SUCCESS) { + if ((status = _snowflake_get_cJSON_column(sfstmt, idx, &column)) != SF_STATUS_SUCCESS) { return status; } float64 value = 0.0; if (snowflake_cJSON_IsNull(column)) { - *value_ptr = value; - return SF_STATUS_SUCCESS; + status = SF_STATUS_SUCCESS; + goto cleanup; } char *endptr; @@ -2110,22 +2217,35 @@ SF_STATUS STDCALL snowflake_column_as_float64(SF_STMT *stmt, int idx, float64 *v value = strtod(column->valuestring, &endptr); // Check for errors if (endptr == column->valuestring) { - return SF_STATUS_ERROR_INVALID_CONVERSION; + SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_INVALID_CONVERSION, + "Cannot convert value into float64", "", sfstmt->sfqid); + status = SF_STATUS_ERROR_INVALID_CONVERSION; + goto cleanup; } if (errno == ERANGE || value == INFINITY || value == -INFINITY) { - return SF_STATUS_ERROR_OUT_OF_RANGE; + SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_OUT_OF_RANGE, + "Value out of range for float64", "", sfstmt->sfqid); + status = SF_STATUS_ERROR_OUT_OF_RANGE; + goto cleanup; } // Everything checks out, set value and return success + status = SF_STATUS_SUCCESS; + +cleanup: *value_ptr = value; - return SF_STATUS_SUCCESS; + return status; } -SF_STATUS STDCALL snowflake_column_as_const_str(SF_STMT *stmt, int idx, const char **value_ptr) { +SF_STATUS STDCALL snowflake_column_as_const_str(SF_STMT *sfstmt, int idx, const char **value_ptr) { SF_STATUS status; cJSON *column = NULL; + if ((status = _snowflake_column_null_checks(sfstmt, (void *) value_ptr)) != SF_STATUS_SUCCESS) { + return status; + } + // Get column - if ((status = _snowflake_get_column(stmt, idx, &column)) != SF_STATUS_SUCCESS) { + if ((status = _snowflake_get_cJSON_column(sfstmt, idx, &column)) != SF_STATUS_SUCCESS) { return status; } @@ -2134,12 +2254,16 @@ SF_STATUS STDCALL snowflake_column_as_const_str(SF_STMT *stmt, int idx, const ch return SF_STATUS_SUCCESS; } -SF_STATUS STDCALL snowflake_column_as_str(SF_STMT *stmt, int idx, char **value_ptr, size_t *value_len_ptr) { +SF_STATUS STDCALL snowflake_column_as_str(SF_STMT *sfstmt, int idx, char **value_ptr, size_t *value_len_ptr) { SF_STATUS status; cJSON *column = NULL; + if ((status = _snowflake_column_null_checks(sfstmt, (void *) value_ptr)) != SF_STATUS_SUCCESS) { + return status; + } + // Get column - if ((status = _snowflake_get_column(stmt, idx, &column)) != SF_STATUS_SUCCESS) { + if ((status = _snowflake_get_cJSON_column(sfstmt, idx, &column)) != SF_STATUS_SUCCESS) { return status; } @@ -2147,6 +2271,7 @@ SF_STATUS STDCALL snowflake_column_as_str(SF_STMT *stmt, int idx, char **value_p size_t value_len = 0; if (snowflake_cJSON_IsNull(column)) { + status = SF_STATUS_SUCCESS; goto cleanup; } @@ -2155,7 +2280,7 @@ SF_STATUS STDCALL snowflake_column_as_str(SF_STMT *stmt, int idx, char **value_p struct tm *tm_ptr; memset(&tm_obj, 0, sizeof(tm_obj)); - switch (stmt->desc[idx - 1].type) { + switch (sfstmt->desc[idx - 1].type) { case SF_DB_TYPE_BOOLEAN: if (strcmp(column->valuestring, "0") == 0) { /* False */ @@ -2177,10 +2302,11 @@ SF_STATUS STDCALL snowflake_column_as_str(SF_STMT *stmt, int idx, char **value_p tm_ptr = sf_gmtime(&sec, &tm_obj); _mutex_unlock(&gmlocaltime_lock); if (tm_ptr == NULL) { - SET_SNOWFLAKE_ERROR(&stmt->error, + SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_CONVERSION_FAILURE, "Failed to convert a date value to a string.", - SF_SQLSTATE_GENERAL_ERROR); + SF_SQLSTATE_GENERAL_ERROR, + sfstmt->sfqid); value = NULL; value_len = 0; goto cleanup; @@ -2195,14 +2321,15 @@ SF_STATUS STDCALL snowflake_column_as_str(SF_STMT *stmt, int idx, char **value_p if (!_extract_timestamp_dynamic( &value, &value_len, - stmt->desc[idx - 1].type, + sfstmt->desc[idx - 1].type, column->valuestring, - stmt->connection->timezone, - stmt->desc[idx - 1].scale)) { - SET_SNOWFLAKE_ERROR(&stmt->error, + sfstmt->connection->timezone, + sfstmt->desc[idx - 1].scale)) { + SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_CONVERSION_FAILURE, "Failed to convert a timestamp value to a string.", - SF_SQLSTATE_GENERAL_ERROR); + SF_SQLSTATE_GENERAL_ERROR, + sfstmt->sfqid); value = NULL; value_len = 0; goto cleanup; @@ -2215,38 +2342,49 @@ SF_STATUS STDCALL snowflake_column_as_str(SF_STMT *stmt, int idx, char **value_p break; } + // Everything went okay + status = SF_STATUS_SUCCESS; + cleanup: *value_ptr = value; if (value_len_ptr) { *value_len_ptr = value_len; } - return SF_STATUS_SUCCESS; + return status; } -SF_STATUS STDCALL snowflake_column_strlen(SF_STMT *stmt, int idx, size_t *value_ptr) { +SF_STATUS STDCALL snowflake_column_strlen(SF_STMT *sfstmt, int idx, size_t *value_ptr) { SF_STATUS status; cJSON *column = NULL; + if ((status = _snowflake_column_null_checks(sfstmt, (void *) value_ptr)) != SF_STATUS_SUCCESS) { + return status; + } + // Get column - if ((status = _snowflake_get_column(stmt, idx, &column)) != SF_STATUS_SUCCESS) { + if ((status = _snowflake_get_cJSON_column(sfstmt, idx, &column)) != SF_STATUS_SUCCESS) { return status; } if (snowflake_cJSON_IsNull(column)) { *value_ptr = 0; } else { - *value_ptr = strlen(snowflake_cJSON_GetArrayItem(stmt->cur_row, idx - 1)->valuestring); + *value_ptr = strlen(snowflake_cJSON_GetArrayItem(sfstmt->cur_row, idx - 1)->valuestring); } return SF_STATUS_SUCCESS; } -SF_STATUS STDCALL snowflake_column_is_null(SF_STMT *stmt, int idx, sf_bool *value_ptr) { +SF_STATUS STDCALL snowflake_column_is_null(SF_STMT *sfstmt, int idx, sf_bool *value_ptr) { SF_STATUS status; cJSON *column = NULL; + if ((status = _snowflake_column_null_checks(sfstmt, (void *) value_ptr)) != SF_STATUS_SUCCESS) { + return status; + } + // Get column - if ((status = _snowflake_get_column(stmt, idx, &column)) != SF_STATUS_SUCCESS) { + if ((status = _snowflake_get_cJSON_column(sfstmt, idx, &column)) != SF_STATUS_SUCCESS) { return status; } diff --git a/tests/test_column_fetch.c b/tests/test_column_fetch.c index 30f1c16c6b..a9ef832a8d 100644 --- a/tests/test_column_fetch.c +++ b/tests/test_column_fetch.c @@ -421,7 +421,7 @@ void test_column_as_uint32(void **unused) { } assert_int_equal(status, SF_STATUS_ERROR_INVALID_CONVERSION); - // Trying to convert a number that is out of range for uint64 + // Trying to convert a number that is out of range for uint32 if (!(status = snowflake_column_as_uint32(sfstmt, 8, &out))) { dump_error(&(sfstmt->error)); } From 03f7e0341b99386626b9d1e2578e827e4a853c44 Mon Sep 17 00:00:00 2001 From: notkriswagner Date: Mon, 17 Sep 2018 16:05:42 -0700 Subject: [PATCH 12/16] Fixed erroneous comments in column fetch test. Fixed issue with duplicate error codes (SF_STATUS_ERROR_INVALID_CONVERSION and SF_STATUS_ERROR_CONVERSION_FAILURE) to use just SF_STATUS_ERROR_CONVERSION_FAILURE. --- include/snowflake/client.h | 5 ++-- lib/client.c | 36 +++++++++++++-------------- tests/test_column_fetch.c | 50 +++++++++++++++++++------------------- 3 files changed, 45 insertions(+), 46 deletions(-) diff --git a/include/snowflake/client.h b/include/snowflake/client.h index a47816a021..55083b8745 100644 --- a/include/snowflake/client.h +++ b/include/snowflake/client.h @@ -125,9 +125,8 @@ typedef enum SF_STATUS { SF_STATUS_ERROR_CONVERSION_FAILURE = 240018, SF_STATUS_ERROR_OUT_OF_BOUNDS = 240019, SF_STATUS_ERROR_MISSING_COLUMN_IN_ROW = 240020, - SF_STATUS_ERROR_INVALID_CONVERSION = 240021, - SF_STATUS_ERROR_OUT_OF_RANGE = 240022, - SF_STATUS_ERROR_NULL_POINTER = 240023, + SF_STATUS_ERROR_OUT_OF_RANGE = 240021, + SF_STATUS_ERROR_NULL_POINTER = 240022, } SF_STATUS; /** diff --git a/lib/client.c b/lib/client.c index d381489e7e..e4dfcce24d 100644 --- a/lib/client.c +++ b/lib/client.c @@ -1893,9 +1893,9 @@ SF_STATUS STDCALL snowflake_column_as_boolean(SF_STMT *sfstmt, int idx, sf_bool float64 float_val = strtod(column->valuestring, &endptr); // Check for errors if (endptr == column->valuestring) { - SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_INVALID_CONVERSION, + SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_CONVERSION_FAILURE, "Cannot convert value into boolean from float64", "", sfstmt->sfqid); - status = SF_STATUS_ERROR_INVALID_CONVERSION; + status = SF_STATUS_ERROR_CONVERSION_FAILURE; goto cleanup; } if (errno == ERANGE || float_val == INFINITY || float_val == -INFINITY) { @@ -1911,9 +1911,9 @@ SF_STATUS STDCALL snowflake_column_as_boolean(SF_STMT *sfstmt, int idx, sf_bool int64 int_val = strtoll(column->valuestring, &endptr, 10); // Check for errors if (endptr == column->valuestring) { - SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_INVALID_CONVERSION, + SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_CONVERSION_FAILURE, "Cannot convert value into boolean from int64", "", sfstmt->sfqid); - status = SF_STATUS_ERROR_INVALID_CONVERSION; + status = SF_STATUS_ERROR_CONVERSION_FAILURE; goto cleanup; } if ((int_val == SF_INT64_MAX || int_val == SF_INT64_MIN) && errno == ERANGE) { @@ -1933,9 +1933,9 @@ SF_STATUS STDCALL snowflake_column_as_boolean(SF_STMT *sfstmt, int idx, sf_bool } break; default: - SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_INVALID_CONVERSION, + SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_CONVERSION_FAILURE, "No valid conversion to boolean from data type", "", sfstmt->sfqid); - status = SF_STATUS_ERROR_INVALID_CONVERSION; + status = SF_STATUS_ERROR_CONVERSION_FAILURE; goto cleanup; } @@ -1985,9 +1985,9 @@ SF_STATUS STDCALL snowflake_column_as_uint32(SF_STMT *sfstmt, int idx, uint32 *v value = strtoul(column->valuestring, &endptr, 10); // Check for errors if (endptr == column->valuestring) { - SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_INVALID_CONVERSION, + SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_CONVERSION_FAILURE, "Cannot convert value into uint32", "", sfstmt->sfqid); - status = SF_STATUS_ERROR_INVALID_CONVERSION; + status = SF_STATUS_ERROR_CONVERSION_FAILURE; goto cleanup; } if (((value == ULONG_MAX || value == 0) && errno == ERANGE) || value > SF_UINT32_MAX) { @@ -2028,9 +2028,9 @@ SF_STATUS STDCALL snowflake_column_as_uint64(SF_STMT *sfstmt, int idx, uint64 *v value = strtoull(column->valuestring, &endptr, 10); // Check for errors if (endptr == column->valuestring) { - SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_INVALID_CONVERSION, + SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_CONVERSION_FAILURE, "Cannot convert value into uint64", "", sfstmt->sfqid); - status = SF_STATUS_ERROR_INVALID_CONVERSION; + status = SF_STATUS_ERROR_CONVERSION_FAILURE; goto cleanup; } if ((value == SF_UINT64_MAX || value == 0) && errno == ERANGE) { @@ -2088,9 +2088,9 @@ SF_STATUS STDCALL snowflake_column_as_int32(SF_STMT *sfstmt, int idx, int32 *val value = strtol(column->valuestring, &endptr, 10); // Check for errors if (endptr == column->valuestring) { - SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_INVALID_CONVERSION, + SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_CONVERSION_FAILURE, "Cannot convert value into int32", "", sfstmt->sfqid); - status = SF_STATUS_ERROR_INVALID_CONVERSION; + status = SF_STATUS_ERROR_CONVERSION_FAILURE; goto cleanup; } if (((value == LONG_MAX || value == LONG_MIN) && errno == ERANGE) || (value > SF_INT32_MAX || value < SF_INT32_MIN)) { @@ -2131,9 +2131,9 @@ SF_STATUS STDCALL snowflake_column_as_int64(SF_STMT *sfstmt, int idx, int64 *val value = strtoll(column->valuestring, &endptr, 10); // Check for errors if (endptr == column->valuestring) { - SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_INVALID_CONVERSION, + SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_CONVERSION_FAILURE, "Cannot convert value into int64", "", sfstmt->sfqid); - status = SF_STATUS_ERROR_INVALID_CONVERSION; + status = SF_STATUS_ERROR_CONVERSION_FAILURE; goto cleanup; } if ((value == SF_INT64_MAX || value == SF_INT64_MIN) && errno == ERANGE) { @@ -2174,9 +2174,9 @@ SF_STATUS STDCALL snowflake_column_as_float32(SF_STMT *sfstmt, int idx, float32 value = strtof(column->valuestring, &endptr); // Check for errors if (endptr == column->valuestring) { - SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_INVALID_CONVERSION, + SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_CONVERSION_FAILURE, "Cannot convert value into float32", "", sfstmt->sfqid); - status = SF_STATUS_ERROR_INVALID_CONVERSION; + status = SF_STATUS_ERROR_CONVERSION_FAILURE; goto cleanup; } if (errno == ERANGE || value == INFINITY || value == -INFINITY) { @@ -2217,9 +2217,9 @@ SF_STATUS STDCALL snowflake_column_as_float64(SF_STMT *sfstmt, int idx, float64 value = strtod(column->valuestring, &endptr); // Check for errors if (endptr == column->valuestring) { - SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_INVALID_CONVERSION, + SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_CONVERSION_FAILURE, "Cannot convert value into float64", "", sfstmt->sfqid); - status = SF_STATUS_ERROR_INVALID_CONVERSION; + status = SF_STATUS_ERROR_CONVERSION_FAILURE; goto cleanup; } if (errno == ERANGE || value == INFINITY || value == -INFINITY) { diff --git a/tests/test_column_fetch.c b/tests/test_column_fetch.c index a9ef832a8d..865fd5a022 100644 --- a/tests/test_column_fetch.c +++ b/tests/test_column_fetch.c @@ -160,14 +160,14 @@ void test_column_as_int32(void **unused) { } assert_int_equal(out, 0); - // Case where a valid conversion leads to the max value for an int64 + // Case where a valid conversion leads to the max value for an int32 if ((status = snowflake_column_as_int32(sfstmt, 3, &out))) { dump_error(&(sfstmt->error)); } assert_int_equal(status, SF_STATUS_SUCCESS); assert(out == SF_INT32_MAX); - // Case where a valid conversion leads to the min value for an int64 + // Case where a valid conversion leads to the min value for an int32 if ((status = snowflake_column_as_int32(sfstmt, 4, &out))) { dump_error(&(sfstmt->error)); } @@ -194,15 +194,15 @@ void test_column_as_int32(void **unused) { if (!(status = snowflake_column_as_int32(sfstmt, 7, &out))) { dump_error(&(sfstmt->error)); } - assert_int_equal(status, SF_STATUS_ERROR_INVALID_CONVERSION); + assert_int_equal(status, SF_STATUS_ERROR_CONVERSION_FAILURE); - // Trying to convert a number that is out of range for int64 + // Trying to convert a number that is out of range for int32 if (!(status = snowflake_column_as_int32(sfstmt, 8, &out))) { dump_error(&(sfstmt->error)); } assert_int_equal(status, SF_STATUS_ERROR_OUT_OF_RANGE); - // Trying to convert a number that is out of range for int64 + // Trying to convert a number that is out of range for int32 if (!(status = snowflake_column_as_int32(sfstmt, 9, &out))) { dump_error(&(sfstmt->error)); } @@ -284,7 +284,7 @@ void test_column_as_int64(void **unused) { if (!(status = snowflake_column_as_int64(sfstmt, 7, &out))) { dump_error(&(sfstmt->error)); } - assert_int_equal(status, SF_STATUS_ERROR_INVALID_CONVERSION); + assert_int_equal(status, SF_STATUS_ERROR_CONVERSION_FAILURE); // Trying to convert a number that is out of range for int64 if (!(status = snowflake_column_as_int64(sfstmt, 8, &out))) { @@ -385,27 +385,27 @@ void test_column_as_uint32(void **unused) { } assert(out == 0); - // Case where a valid conversion leads to the max value for an int64 + // Case where a valid conversion leads to the max value for an uint32 if ((status = snowflake_column_as_uint32(sfstmt, 3, &out))) { dump_error(&(sfstmt->error)); } assert_int_equal(status, SF_STATUS_SUCCESS); assert(out == SF_UINT32_MAX); - // Trying to convert a negative number to an uint64 + // Trying to convert a negative number to an uint32 if ((status = snowflake_column_as_uint32(sfstmt, 4, &out))) { dump_error(&(sfstmt->error)); } assert_int_equal(status, SF_STATUS_SUCCESS); assert(out == SF_UINT32_MAX); - // Trying to convert a float to an uint64 + // Trying to convert a float to an uint32 if (snowflake_column_as_uint32(sfstmt, 5, &out)) { dump_error(&(sfstmt->error)); } assert_int_equal(10, out); - // Trying to convert a NULL to an uint64 + // Trying to convert a NULL to an uint32 if (snowflake_column_as_uint32(sfstmt, 6, &out)) { dump_error(&(sfstmt->error)); } @@ -415,11 +415,11 @@ void test_column_as_uint32(void **unused) { // Start of invalid conversions // ************************************************** - // Trying to convert non-numerical string to an uint64 + // Trying to convert non-numerical string to an uint32 if (!(status = snowflake_column_as_uint32(sfstmt, 7, &out))) { dump_error(&(sfstmt->error)); } - assert_int_equal(status, SF_STATUS_ERROR_INVALID_CONVERSION); + assert_int_equal(status, SF_STATUS_ERROR_CONVERSION_FAILURE); // Trying to convert a number that is out of range for uint32 if (!(status = snowflake_column_as_uint32(sfstmt, 8, &out))) { @@ -468,7 +468,7 @@ void test_column_as_uint64(void **unused) { } assert(out == 0); - // Case where a valid conversion leads to the max value for an int64 + // Case where a valid conversion leads to the max value for an uint64 if ((status = snowflake_column_as_uint64(sfstmt, 3, &out))) { dump_error(&(sfstmt->error)); } @@ -502,7 +502,7 @@ void test_column_as_uint64(void **unused) { if (!(status = snowflake_column_as_uint64(sfstmt, 7, &out))) { dump_error(&(sfstmt->error)); } - assert_int_equal(status, SF_STATUS_ERROR_INVALID_CONVERSION); + assert_int_equal(status, SF_STATUS_ERROR_CONVERSION_FAILURE); // Trying to convert a number that is out of range for uint64 if (!(status = snowflake_column_as_uint64(sfstmt, 8, &out))) { @@ -558,19 +558,19 @@ void test_column_as_float32(void **unused) { } assert(out == (float32) 1.1); - // Case where a valid conversion leads to the max value for a float64 + // Case where a valid conversion leads to the max value for a float32 if ((status = snowflake_column_as_float32(sfstmt, 4, &out))) { dump_error(&(sfstmt->error)); } assert_int_equal(status, SF_STATUS_SUCCESS); - // Case where a valid conversion leads to the min positive value for a float64 + // Case where a valid conversion leads to the min positive value for a float32 if ((status = snowflake_column_as_float32(sfstmt, 5, &out))) { dump_error(&(sfstmt->error)); } assert_int_equal(status, SF_STATUS_SUCCESS); - // Case where a valid conversion leads to the min positive value for a float64 + // Case where a valid conversion leads to the min positive value for a float32 if ((status = snowflake_column_as_float32(sfstmt, 6, &out))) { dump_error(&(sfstmt->error)); } @@ -580,11 +580,11 @@ void test_column_as_float32(void **unused) { // Start of invalid conversions // ************************************************** - // Trying to convert non-numerical string to an uint64 + // Trying to convert non-numerical string to an float32 if (!(status = snowflake_column_as_float32(sfstmt, 7, &out))) { dump_error(&(sfstmt->error)); } - assert_int_equal(status, SF_STATUS_ERROR_INVALID_CONVERSION); + assert_int_equal(status, SF_STATUS_ERROR_CONVERSION_FAILURE); // Trying to convert a number that is out of range for float32 if (!(status = snowflake_column_as_float32(sfstmt, 8, &out))) { @@ -662,11 +662,11 @@ void test_column_as_float64(void **unused) { // Start of invalid conversions // ************************************************** - // Trying to convert non-numerical string to an uint64 + // Trying to convert non-numerical string to an float64 if (!(status = snowflake_column_as_float64(sfstmt, 7, &out))) { dump_error(&(sfstmt->error)); } - assert_int_equal(status, SF_STATUS_ERROR_INVALID_CONVERSION); + assert_int_equal(status, SF_STATUS_ERROR_CONVERSION_FAILURE); // Trying to convert a number that is out of range for float64 if (!(status = snowflake_column_as_float64(sfstmt, 8, &out))) { @@ -716,19 +716,19 @@ void test_column_as_const_str(void **unused) { } assert_string_equal("", out); - // Case where a valid conversion leads to the max value for an int64 + // Case with a DOUBLE DB type if (snowflake_column_as_const_str(sfstmt, 3, &out)) { dump_error(&(sfstmt->error)); } assert_string_equal("1.1", out); - // Trying to convert non-numerical string to an uint64 + // Case with a INTEGER DB type if (snowflake_column_as_const_str(sfstmt, 4, &out)) { dump_error(&(sfstmt->error)); } assert_string_equal("10", out); - // Trying to convert a float to an uint64 + // Case with a BOOLEAN DB type if (snowflake_column_as_const_str(sfstmt, 5, &out)) { dump_error(&(sfstmt->error)); } @@ -780,7 +780,7 @@ void test_column_is_null(void **unused) { } assert_true(out); - // Case where a valid conversion leads to the max value for an int64 + // Case where column is boolean false if (snowflake_column_is_null(sfstmt, 3, &out)) { dump_error(&(sfstmt->error)); } From 3414e7806f5cb4bc14d33c64984f3c0a1b4d5626 Mon Sep 17 00:00:00 2001 From: notkriswagner Date: Mon, 17 Sep 2018 16:45:51 -0700 Subject: [PATCH 13/16] Added fix to uint32 test to ensure that conversion from negative values works well. --- lib/client.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/client.c b/lib/client.c index e4dfcce24d..7bb04e0fa1 100644 --- a/lib/client.c +++ b/lib/client.c @@ -1982,7 +1982,7 @@ SF_STATUS STDCALL snowflake_column_as_uint32(SF_STMT *sfstmt, int idx, uint32 *v char *endptr; errno = 0; - value = strtoul(column->valuestring, &endptr, 10); + value = strtoull(column->valuestring, &endptr, 10); // Check for errors if (endptr == column->valuestring) { SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_CONVERSION_FAILURE, @@ -1990,12 +1990,20 @@ SF_STATUS STDCALL snowflake_column_as_uint32(SF_STMT *sfstmt, int idx, uint32 *v status = SF_STATUS_ERROR_CONVERSION_FAILURE; goto cleanup; } - if (((value == ULONG_MAX || value == 0) && errno == ERANGE) || value > SF_UINT32_MAX) { + sf_bool neg = (strchr(column->valuestring, '-') != NULL) ? SF_BOOLEAN_TRUE: SF_BOOLEAN_FALSE; + // Check for out of range + if (((value == ULONG_MAX || value == 0) && errno == ERANGE) || + (!neg && value > SF_UINT32_MAX) || + (neg && value < (SF_UINT64_MAX - SF_UINT32_MAX))) { SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_OUT_OF_RANGE, "Value out of range for uint32", "", sfstmt->sfqid); status = SF_STATUS_ERROR_OUT_OF_RANGE; goto cleanup; } + // If the input was negative, we have to do a little trickery to get it into uint32 form + if (neg && value > SF_UINT32_MAX) { + value = value - (SF_UINT64_MAX - SF_UINT32_MAX); + } // Everything checks out, set value and return success status = SF_STATUS_SUCCESS; @@ -2085,7 +2093,7 @@ SF_STATUS STDCALL snowflake_column_as_int32(SF_STMT *sfstmt, int idx, int32 *val char *endptr; errno = 0; - value = strtol(column->valuestring, &endptr, 10); + value = strtoll(column->valuestring, &endptr, 10); // Check for errors if (endptr == column->valuestring) { SET_SNOWFLAKE_STMT_ERROR(&sfstmt->error, SF_STATUS_ERROR_CONVERSION_FAILURE, From 8d0bf3831e741ba90405606a8305d17cf570fd16 Mon Sep 17 00:00:00 2001 From: notkriswagner Date: Tue, 18 Sep 2018 12:12:06 -0700 Subject: [PATCH 14/16] Added memory hooks to use in snowflake_column_as_str function and to eventually use throughout the entire library. --- include/snowflake/client.h | 20 ++++++++------------ lib/memory.h | 9 +++++++++ tests/utils/test_setup.c | 2 +- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/include/snowflake/client.h b/include/snowflake/client.h index 55083b8745..257c1e1d15 100644 --- a/include/snowflake/client.h +++ b/include/snowflake/client.h @@ -356,18 +356,14 @@ typedef struct { } SF_BIND_INPUT; /** - * Bind output parameter context + * */ - // TODO Remove -//typedef struct { -// size_t idx; /* One based index of the columns */ -// SF_C_TYPE c_type; /* expected data type in C */ -// size_t max_length; /* maximum buffer size provided by application */ -// void *value; /* input and output: buffer to stoare a value */ -// size_t len; /* output: actual value length */ -// sf_bool is_null; /* output: SF_BOOLEAN_TRUE if is null else SF_BOOLEAN_FALSE */ -//} SF_BIND_OUTPUT; - +typedef struct SF_USER_MEM_HOOKS { + void *(*alloc_fn)(size_t size); + void (*dealloc_fn)(void *ptr); + void *(*realloc_fn)(void *ptr, size_t size); + void *(*calloc_fn)(size_t nitems, size_t size); +} SF_USER_MEM_HOOKS; /** * Global Snowflake initialization. @@ -375,7 +371,7 @@ typedef struct { * @return 0 if successful, errno otherwise */ SF_STATUS STDCALL -snowflake_global_init(const char *log_path, SF_LOG_LEVEL log_level); +snowflake_global_init(const char *log_path, SF_LOG_LEVEL log_level, SF_USER_MEM_HOOKS *hooks); /** * Global Snowflake cleanup. diff --git a/lib/memory.h b/lib/memory.h index 8558d01bee..a0389ee131 100644 --- a/lib/memory.h +++ b/lib/memory.h @@ -18,6 +18,15 @@ extern "C" { #define SF_REALLOC(p, s) sf_realloc(p, s, __FILE__, __LINE__) #define SF_FREE(p) ((void) (sf_free(p, __FILE__, __LINE__), (p) = NULL)) +typedef struct SF_INTERNAL_MEM_HOOKS { + void *(*alloc)(size_t size); + void (*dealloc)(void *ptr); + void *(*realloc)(void *ptr, size_t size); + void *(*calloc)(size_t nitems, size_t size); +} SF_INTERNAL_MEM_HOOKS; + +static SF_INTERNAL_MEM_HOOKS global_hooks = {malloc, free, realloc}; + void sf_memory_init(); void sf_memory_term(); void *sf_malloc(size_t size, const char *file, int line); diff --git a/tests/utils/test_setup.c b/tests/utils/test_setup.c index 504e67acde..64aebce2d7 100644 --- a/tests/utils/test_setup.c +++ b/tests/utils/test_setup.c @@ -11,7 +11,7 @@ char PERFORMANCE_TEST_RESULTS_PATH[4096]; void initialize_test(sf_bool debug) { // default location and the maximum logging - snowflake_global_init(NULL, SF_LOG_TRACE); + snowflake_global_init(NULL, SF_LOG_TRACE, NULL); snowflake_global_set_attribute(SF_GLOBAL_CA_BUNDLE_FILE, getenv("SNOWFLAKE_TEST_CA_BUNDLE_FILE")); snowflake_global_set_attribute(SF_GLOBAL_DEBUG, &debug); From e9de0e6e57f9eea33cd834d0e20a7dc4a082323f Mon Sep 17 00:00:00 2001 From: notkriswagner Date: Tue, 18 Sep 2018 12:24:25 -0700 Subject: [PATCH 15/16] Forgot to commit client.c in last commit. Memory hooks should work now. --- lib/client.c | 47 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/lib/client.c b/lib/client.c index 7bb04e0fa1..3fdc05eb99 100644 --- a/lib/client.c +++ b/lib/client.c @@ -367,6 +367,42 @@ static SF_STATUS STDCALL _reset_connection_parameters( return ret; } +void STDCALL _snowflake_memory_hooks_setup(SF_USER_MEM_HOOKS *hooks) { + if (hooks == NULL) + { + /* Reset hooks */ + global_hooks.alloc = malloc; + global_hooks.dealloc = free; + global_hooks.realloc = realloc; + global_hooks.calloc = calloc; + return; + } + + global_hooks.alloc = malloc; + if (hooks->alloc_fn != NULL) + { + global_hooks.alloc = hooks->alloc_fn; + } + + global_hooks.dealloc = free; + if (hooks->dealloc_fn != NULL) + { + global_hooks.dealloc = hooks->dealloc_fn; + } + + global_hooks.realloc = realloc; + if (hooks->realloc_fn != NULL) + { + global_hooks.realloc = hooks->realloc_fn; + } + + global_hooks.calloc = calloc; + if (hooks->calloc_fn != NULL) + { + global_hooks.calloc = hooks->calloc_fn; + } +} + /* * Initializes logging file */ @@ -560,7 +596,7 @@ _snowflake_check_connection_parameters(SF_CONNECT *sf) { SF_STATUS STDCALL snowflake_global_init( - const char *log_path, SF_LOG_LEVEL log_level) { + const char *log_path, SF_LOG_LEVEL log_level, SF_USER_MEM_HOOKS *hooks) { SF_STATUS ret = SF_STATUS_ERROR_GENERAL; // Initialize constants @@ -569,6 +605,7 @@ SF_STATUS STDCALL snowflake_global_init( SSL_VERSION = CURL_SSLVERSION_TLSv1_2; DEBUG = SF_BOOLEAN_FALSE; + _snowflake_memory_hooks_setup(hooks); sf_memory_init(); sf_error_init(); if (!log_init(log_path, log_level)) { @@ -2293,12 +2330,12 @@ SF_STATUS STDCALL snowflake_column_as_str(SF_STMT *sfstmt, int idx, char **value if (strcmp(column->valuestring, "0") == 0) { /* False */ value_len = strlen(SF_BOOLEAN_FALSE_STR); - value = calloc(1, value_len + 1); + value = global_hooks.calloc(1, value_len + 1); strncpy(value, SF_BOOLEAN_FALSE_STR, value_len + 1); } else { /* True */ value_len = strlen(SF_BOOLEAN_TRUE_STR); - value = calloc(1, value_len + 1); + value = global_hooks.calloc(1, value_len + 1); strncpy(value, SF_BOOLEAN_TRUE_STR, value_len + 1); } break; @@ -2319,7 +2356,7 @@ SF_STATUS STDCALL snowflake_column_as_str(SF_STMT *sfstmt, int idx, char **value value_len = 0; goto cleanup; } - value = calloc(1, 13); + value = global_hooks.calloc(1, 13); value_len = strftime(value, 13, "%Y-%m-%d", &tm_obj); break; case SF_DB_TYPE_TIME: @@ -2345,7 +2382,7 @@ SF_STATUS STDCALL snowflake_column_as_str(SF_STMT *sfstmt, int idx, char **value break; default: value_len = strlen(column->valuestring); - value = calloc(1, value_len + 1); + value = global_hooks.calloc(1, value_len + 1); strncpy(value, column->valuestring, value_len + 1); break; } From f5ab8b49b7ad272bc020003da763c40ea2a4573e Mon Sep 17 00:00:00 2001 From: notkriswagner Date: Tue, 18 Sep 2018 15:15:33 -0700 Subject: [PATCH 16/16] Added calloc to global_hooks initialization --- lib/memory.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/memory.h b/lib/memory.h index a0389ee131..2bfa3962ea 100644 --- a/lib/memory.h +++ b/lib/memory.h @@ -25,7 +25,7 @@ typedef struct SF_INTERNAL_MEM_HOOKS { void *(*calloc)(size_t nitems, size_t size); } SF_INTERNAL_MEM_HOOKS; -static SF_INTERNAL_MEM_HOOKS global_hooks = {malloc, free, realloc}; +static SF_INTERNAL_MEM_HOOKS global_hooks = {malloc, free, realloc, calloc}; void sf_memory_init(); void sf_memory_term();