diff --git a/cpp/StatementPutGet.cpp b/cpp/StatementPutGet.cpp index 989affbf89..883a129a98 100755 --- a/cpp/StatementPutGet.cpp +++ b/cpp/StatementPutGet.cpp @@ -3,11 +3,35 @@ */ #include +#include "connection.h" #include "snowflake/PutGetParseResponse.hpp" #include "StatementPutGet.hpp" +#include "curl_desc_pool.h" using namespace Snowflake::Client; +static size_t file_get_write_callback(char* ptr, size_t size, size_t nmemb, void* userdata) +{ + size_t data_size = size * nmemb; + std::basic_iostream* recvStream = (std::basic_iostream*)(userdata); + if (recvStream) + { + recvStream->write(static_cast(ptr), data_size); + } + + return data_size; +} + +static size_t file_put_read_callback(void* ptr, size_t size, size_t nmemb, void* userdata) +{ + std::basic_iostream* payload = (std::basic_iostream*)(userdata); + size_t data_size = size * nmemb; + + payload->read(static_cast(ptr), data_size); + size_t ret = payload->gcount(); + return payload->gcount(); +} + StatementPutGet::StatementPutGet(SF_STMT *stmt) : m_stmt(stmt), m_useProxy(false) { @@ -104,6 +128,14 @@ bool StatementPutGet::parsePutGetCommand(std::string *sql, }; putGetParseResponse->stageInfo.endPoint = response->stage_info->endPoint; + } + else if (sf_strncasecmp(response->stage_info->location_type, "gcs", 3) == 0) + { + putGetParseResponse->stageInfo.stageType = StageType::GCS; + putGetParseResponse->stageInfo.credentials = { + {"GCS_ACCESS_TOKEN", response->stage_info->stage_cred->gcs_access_token} + }; + } else if (sf_strncasecmp(response->stage_info->location_type, "local_fs", 8) == 0) { @@ -123,3 +155,124 @@ Util::Proxy* StatementPutGet::get_proxy() return &m_proxy; } } + +bool StatementPutGet::http_put(std::string const& url, + std::vector const& headers, + std::basic_iostream& payload, + size_t payloadLen, + std::string& responseHeaders) +{ + if (!m_stmt || !m_stmt->connection) + { + return false; + } + SF_CONNECT* sf = m_stmt->connection; + void* curl_desc = get_curl_desc_from_pool(url.c_str(), sf->proxy, sf->no_proxy); + CURL* curl = get_curl_from_desc(curl_desc); + if (!curl) + { + return false; + } + + char* urlbuf = (char*)SF_CALLOC(1, url.length() + 1); + sf_strcpy(urlbuf, url.length() + 1, url.c_str()); + + SF_HEADER reqHeaders; + reqHeaders.header = NULL; + for (auto itr = headers.begin(); itr != headers.end(); itr++) + { + reqHeaders.header = curl_slist_append(reqHeaders.header, itr->c_str()); + } + + PUT_PAYLOAD putPayload; + putPayload.buffer = &payload; + putPayload.length = payloadLen; + putPayload.read_callback = file_put_read_callback; + + char* respHeaders = NULL; + sf_bool success = SF_BOOLEAN_FALSE; + + success = http_perform(curl, PUT_REQUEST_TYPE, urlbuf, &reqHeaders, NULL, &putPayload, NULL, + NULL, &respHeaders, get_retry_timeout(sf), + SF_BOOLEAN_FALSE, &m_stmt->error, sf->insecure_mode,sf->ocsp_fail_open, + sf->retry_on_curle_couldnt_connect_count, + 0, sf->retry_count, NULL, NULL, NULL, SF_BOOLEAN_FALSE, + sf->proxy, sf->no_proxy, SF_BOOLEAN_FALSE, SF_BOOLEAN_FALSE); + + free_curl_desc(curl_desc); + SF_FREE(urlbuf); + curl_slist_free_all(reqHeaders.header); + if (respHeaders) + { + responseHeaders = std::string(respHeaders); + SF_FREE(respHeaders); + } + + return success; +} + +bool StatementPutGet::http_get(std::string const& url, + std::vector const& headers, + std::basic_iostream* payload, + std::string& responseHeaders, + bool headerOnly) +{ + SF_REQUEST_TYPE reqType = GET_REQUEST_TYPE; + if (headerOnly) + { + reqType = HEAD_REQUEST_TYPE; + } + + if (!m_stmt || !m_stmt->connection) + { + return false; + } + SF_CONNECT* sf = m_stmt->connection; + + void* curl_desc = get_curl_desc_from_pool(url.c_str(), sf->proxy, sf->no_proxy); + CURL* curl = get_curl_from_desc(curl_desc); + if (!curl) + { + return false; + } + + char* urlbuf = (char*)SF_CALLOC(1, url.length() + 1); + sf_strcpy(urlbuf, url.length() + 1, url.c_str()); + + SF_HEADER reqHeaders; + reqHeaders.header = NULL; + for (auto itr = headers.begin(); itr != headers.end(); itr++) + { + reqHeaders.header = curl_slist_append(reqHeaders.header, itr->c_str()); + } + + NON_JSON_RESP resp; + resp.buffer = payload; + resp.write_callback = file_get_write_callback; + + char* respHeaders = NULL; + sf_bool success = SF_BOOLEAN_FALSE; + + success = http_perform(curl, reqType, urlbuf, &reqHeaders, NULL, NULL, NULL, + &resp, &respHeaders, get_retry_timeout(sf), + SF_BOOLEAN_FALSE, &m_stmt->error, sf->insecure_mode, sf->ocsp_fail_open, + sf->retry_on_curle_couldnt_connect_count, + 0, sf->retry_count, NULL, NULL, NULL, SF_BOOLEAN_FALSE, + sf->proxy, sf->no_proxy, SF_BOOLEAN_FALSE, SF_BOOLEAN_FALSE); + + free_curl_desc(curl_desc); + SF_FREE(urlbuf); + curl_slist_free_all(reqHeaders.header); + if (respHeaders) + { + responseHeaders = respHeaders; + SF_FREE(respHeaders); + } + + if (payload) + { + payload->flush(); + } + + return success; +} diff --git a/cpp/StatementPutGet.hpp b/cpp/StatementPutGet.hpp index 94321fac73..19c00df0fd 100644 --- a/cpp/StatementPutGet.hpp +++ b/cpp/StatementPutGet.hpp @@ -28,6 +28,39 @@ class StatementPutGet : public Snowflake::Client::IStatementPutGet virtual Util::Proxy* get_proxy(); + /** + * PUT/GET on GCS use this interface to perform put request. + * Not implemented by default. + * @param url The url of the request. + * @param headers The headers of the request. + * @param payload The upload data. + * @param responseHeaders The headers of the response. + * + * return true if succeed otherwise false + */ + virtual bool http_put(std::string const& url, + std::vector const& headers, + std::basic_iostream& payload, + size_t payloadLen, + std::string& responseHeaders); + + /** + * PUT/GET on GCS use this interface to perform put request. + * Not implemented by default. + * @param url The url of the request. + * @param headers The headers of the request. + * @param payload The upload data. + * @param responseHeaders The headers of the response. + * @param headerOnly True if get response header only without payload body. + * + * return true if succeed otherwise false + */ + virtual bool http_get(std::string const& url, + std::vector const& headers, + std::basic_iostream* payload, + std::string& responseHeaders, + bool headerOnly); + private: SF_STMT *m_stmt; Util::Proxy m_proxy; diff --git a/include/snowflake/client.h b/include/snowflake/client.h index e97ef7e373..756a20a892 100644 --- a/include/snowflake/client.h +++ b/include/snowflake/client.h @@ -18,6 +18,11 @@ extern "C" { /** * API Name */ +/* TODO: Temporarily change to ODBC for now to pass the test before + * features (PUT for GCP, multiple statements etc.) unblocked + * on server side. + * Need to revert to C_API when merging to master. + */ #define SF_API_NAME "ODBC" /** diff --git a/include/snowflake/version.h b/include/snowflake/version.h index 83ddc59f55..437ac8e5fb 100644 --- a/include/snowflake/version.h +++ b/include/snowflake/version.h @@ -5,7 +5,11 @@ #ifndef SNOWFLAKE_CLIENT_VERSION_H #define SNOWFLAKE_CLIENT_VERSION_H -// TODO: temporary change for testing, will remove +/* TODO: Temporarily change to ODBC version for now to pass the test before + * features (PUT for GCP, multiple statements etc.) unblocked + * on server side. + * Need to revert to libsfclient version when merging to master. + */ #define SF_API_VERSION "3.0.1" #endif /* SNOWFLAKE_CLIENT_VERSION_H */ diff --git a/lib/chunk_downloader.c b/lib/chunk_downloader.c index 6f6e923cb2..5657cb4465 100644 --- a/lib/chunk_downloader.c +++ b/lib/chunk_downloader.c @@ -217,8 +217,8 @@ sf_bool STDCALL download_chunk(char *url, SF_HEADER *headers, CURL *curl = get_curl_from_desc(curl_desc); if (!curl || - !http_perform(curl, GET_REQUEST_TYPE, url, headers, NULL, chunk, - non_json_resp, network_timeout, + !http_perform(curl, GET_REQUEST_TYPE, url, headers, NULL, NULL, chunk, + non_json_resp, NULL, network_timeout, SF_BOOLEAN_TRUE, error, insecure_mode, fail_open, 0, 0, retry_max_count, NULL, NULL, NULL, SF_BOOLEAN_FALSE, proxy, no_proxy, SF_BOOLEAN_FALSE, SF_BOOLEAN_FALSE)) { diff --git a/lib/client.c b/lib/client.c index 83607e06db..9880113bab 100644 --- a/lib/client.c +++ b/lib/client.c @@ -2488,6 +2488,9 @@ SF_STATUS STDCALL _snowflake_execute_ex(SF_STMT *sfstmt, json_copy_string( &sfstmt->put_get_response->stage_info->stage_cred->azure_sas_token, stage_cred, "AZURE_SAS_TOKEN"); + json_copy_string( + &sfstmt->put_get_response->stage_info->stage_cred->gcs_access_token, + stage_cred, "GCS_ACCESS_TOKEN"); json_copy_string( &sfstmt->put_get_response->localLocation, data, "localLocation"); diff --git a/lib/client_int.h b/lib/client_int.h index e8b111cd68..632856f031 100644 --- a/lib/client_int.h +++ b/lib/client_int.h @@ -86,6 +86,7 @@ typedef struct SF_STAGE_CRED { char *aws_secret_key; char *aws_token; char *azure_sas_token; + char* gcs_access_token; } SF_STAGE_CRED; typedef struct SF_STAGE_INFO { diff --git a/lib/connection.c b/lib/connection.c index 4ca5a12ca4..cf1916cbdf 100644 --- a/lib/connection.c +++ b/lib/connection.c @@ -239,7 +239,6 @@ cJSON *STDCALL create_query_json_body(const char *sql_text, parameters = snowflake_cJSON_CreateObject(); } snowflake_cJSON_AddStringToObject(parameters, "C_API_QUERY_RESULT_FORMAT", "JSON"); - // temporary code to fake as ODBC to have multiple statements enabled snowflake_cJSON_AddStringToObject(parameters, "ODBC_QUERY_RESULT_FORMAT", "JSON"); #endif @@ -376,7 +375,7 @@ sf_bool STDCALL curl_post_call(SF_CONNECT *sf, } do { - if (!http_perform(curl, POST_REQUEST_TYPE, url, header, body, json, NULL, + if (!http_perform(curl, POST_REQUEST_TYPE, url, header, body, NULL, json, NULL, NULL, retry_timeout, SF_BOOLEAN_FALSE, error, sf->insecure_mode, sf->ocsp_fail_open, sf->retry_on_curle_couldnt_connect_count, @@ -503,7 +502,7 @@ sf_bool STDCALL curl_get_call(SF_CONNECT *sf, memset(query_code, 0, QUERYCODE_LEN); do { - if (!http_perform(curl, GET_REQUEST_TYPE, url, header, NULL, json, NULL, + if (!http_perform(curl, GET_REQUEST_TYPE, url, header, NULL, NULL, json, NULL, NULL, get_retry_timeout(sf), SF_BOOLEAN_FALSE, error, sf->insecure_mode, sf->ocsp_fail_open, sf->retry_on_curle_couldnt_connect_count, @@ -906,16 +905,16 @@ ARRAY_LIST *json_get_object_keys(const cJSON *item) { } size_t -json_resp_cb(char *data, size_t size, size_t nmemb, RAW_JSON_BUFFER *raw_json) { +char_resp_cb(char *data, size_t size, size_t nmemb, RAW_CHAR_BUFFER *raw_buf) { size_t data_size = size * nmemb; log_debug("Curl response size: %zu", data_size); - raw_json->buffer = (char *) SF_REALLOC(raw_json->buffer, - raw_json->size + data_size + 1); + raw_buf->buffer = (char *) SF_REALLOC(raw_buf->buffer, + raw_buf->size + data_size + 1); // Start copying where last null terminator existed - sf_memcpy(&raw_json->buffer[raw_json->size], data_size, data, data_size); - raw_json->size += data_size; - // Set null terminator - raw_json->buffer[raw_json->size] = '\0'; + sf_memcpy(&raw_buf->buffer[raw_buf->size], data_size, data, data_size); + raw_buf->size += data_size; + // Set null raw_buf + raw_buf->buffer[raw_buf->size] = '\0'; return data_size; } diff --git a/lib/connection.h b/lib/connection.h index c536aa9b45..04ef521ee8 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -56,6 +56,9 @@ typedef enum SF_REQUEST_TYPE { /** we are doing a http delete */ DELETE_REQUEST_TYPE, + + /** we are doing a http head */ + HEAD_REQUEST_TYPE, } SF_REQUEST_TYPE; /** @@ -81,12 +84,12 @@ typedef enum SF_JSON_ERROR { /** * Dynamically growing char buffer to hold retrieved in cURL call. */ -typedef struct RAW_JSON_BUFFER { +typedef struct RAW_CHAR_BUFFER { // Char buffer char *buffer; // Number of characters in char buffer size_t size; -} RAW_JSON_BUFFER; +} RAW_CHAR_BUFFER; /** * URL Parameter struct used to construct an encoded URL. @@ -167,6 +170,15 @@ typedef struct non_json_response { void * buffer; } NON_JSON_RESP; +/** +* payload struct for put request +*/ +typedef struct put_payload { + size_t (*read_callback)(void* ptr, size_t size, size_t nmemb, void* userdata); + void * buffer; + size_t length; +} PUT_PAYLOAD; + /** * Macro to get a custom error message to pass to the Snowflake Error object. */ @@ -398,16 +410,16 @@ SF_JSON_ERROR STDCALL json_detach_object_from_array(cJSON **dest, cJSON *data, i ARRAY_LIST *json_get_object_keys(const cJSON *item); /** - * A write callback function to use to write the response text received from the cURL response. The raw JSON buffer + * A write callback function to use to write the response text received from the cURL response. The raw CHAR buffer * will grow in size until * * @param data The data to copy in the buffer. * @param size The size (in bytes) of each data member. * @param nmemb The number of data members. - * @param raw_json The Raw JSON Buffer object that grows in size to copy multiple writes for a single cURL call. + * @param raw_buf The Raw CHAR Buffer object that grows in size to copy multiple writes for a single cURL call. * @return The number of bytes copied into the buffer. */ -size_t json_resp_cb(char *data, size_t size, size_t nmemb, RAW_JSON_BUFFER *raw_json); +size_t char_resp_cb(char *data, size_t size, size_t nmemb, RAW_CHAR_BUFFER *raw_buf); /** * Performs an HTTP request with retry. @@ -416,10 +428,13 @@ size_t json_resp_cb(char *data, size_t size, size_t nmemb, RAW_JSON_BUFFER *raw_ * @param request_type The type of HTTP request. * @param url The fully qualified URL to use for the HTTP request. * @param header The header to use for the HTTP request. - * @param body The body to send over the HTTP request. If running GET request, set this to NULL. + * @param body The body to send over the HTTP request. If running GET/PUT request, set this to NULL. + * @param put_payload The payload to send over the PUT HTTP request. If not running PUT request, set this to NULL. * @param json A reference to a cJSON pointer where we should store a successful request. * @param non_json_resp A reference to a non-json response to retrieve response in non-json format. * Used only when json is set to NULL. + * @param resp_headers A reference to retrieve response headers. Needs to be freed with SF_FREE. + * Set to NULL if it's not needed. * @param network_timeout The network request timeout to use for each request try. * @param chunk_downloader A boolean value determining whether or not we are running this request from the chunk * downloader. Each chunk that we download from AWS is invalid JSON so we need to add an @@ -450,8 +465,9 @@ size_t json_resp_cb(char *data, size_t size, size_t nmemb, RAW_JSON_BUFFER *raw_ * @return Success/failure status of http request call. 1 = Success; 0 = Failure/renew timeout */ sf_bool STDCALL http_perform(CURL *curl, SF_REQUEST_TYPE request_type, char *url, SF_HEADER *header, - char *body, cJSON **json, NON_JSON_RESP* non_json_resp, int64 network_timeout, sf_bool chunk_downloader, - SF_ERROR_STRUCT *error, sf_bool insecure_mode, sf_bool fail_open, + char *body, PUT_PAYLOAD* put_payload, cJSON **json, NON_JSON_RESP* non_json_resp, + char** resp_headers, int64 network_timeout, sf_bool chunk_downloader, + SF_ERROR_STRUCT* error, sf_bool insecure_mode, sf_bool fail_open, int8 retry_on_curle_couldnt_connect_count, int64 renew_timeout, int8 retry_max_count, int64 *elapsed_time, int8 *retried_count, diff --git a/lib/http_perform.c b/lib/http_perform.c index b7291e2034..56d093967d 100644 --- a/lib/http_perform.c +++ b/lib/http_perform.c @@ -143,8 +143,10 @@ sf_bool STDCALL http_perform(CURL *curl, char *url, SF_HEADER *header, char *body, + PUT_PAYLOAD* put_payload, cJSON **json, NON_JSON_RESP *non_json_resp, + char** resp_headers, int64 network_timeout, sf_bool chunk_downloader, SF_ERROR_STRUCT *error, @@ -190,7 +192,8 @@ sf_bool STDCALL http_perform(CURL *curl, sf_get_current_time_millis() // start time }; time_t elapsedRetryTime = time(NULL); - RAW_JSON_BUFFER buffer = {NULL, 0}; + RAW_CHAR_BUFFER buffer = {NULL, 0}; + RAW_CHAR_BUFFER headerBuffer = { NULL, 0 }; struct data config; config.trace_ascii = 1; @@ -204,6 +207,8 @@ sf_bool STDCALL http_perform(CURL *curl, // Reset buffer since this may not be our first rodeo SF_FREE(buffer.buffer); buffer.size = 0; + SF_FREE(headerBuffer.buffer); + headerBuffer.size = 0; // Generate new request guid, if request guid exists in url if (SF_BOOLEAN_TRUE != retry_ctx_update_url(&curl_retry_ctx, url, include_retry_reason)) { @@ -269,6 +274,46 @@ sf_bool STDCALL http_perform(CURL *curl, break; } } + else if (request_type == HEAD_REQUEST_TYPE) + { + /** we want response header only */ + curl_easy_setopt(curl, CURLOPT_NOBODY, 1L); + } + else if (request_type == PUT_REQUEST_TYPE) + { + // we need to upload the data + curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); + + if (!put_payload) + { + log_error("Invalid payload for put request"); + break; + } + /** set read callback function */ + res = curl_easy_setopt(curl, CURLOPT_READFUNCTION, put_payload->read_callback); + if (res != CURLE_OK) { + log_error("Failed to set read function [%s]", curl_easy_strerror(res)); + break; + } + + /** set data object to pass to callback function */ + res = curl_easy_setopt(curl, CURLOPT_READDATA, put_payload->buffer); + if (res != CURLE_OK) { + log_error("Failed to set read data [%s]", curl_easy_strerror(res)); + break; + } + + /** set size of put */ + if (put_payload->length <= SF_INT32_MAX) + { + res = curl_easy_setopt(curl, CURLOPT_INFILESIZE, (long)put_payload->length); + } + else + { + res = curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)put_payload->length); + } + } + if (!json && non_json_resp) { @@ -276,7 +321,7 @@ sf_bool STDCALL http_perform(CURL *curl, } else { - res = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, (void*)&json_resp_cb); + res = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, (void*)&char_resp_cb); } if (res != CURLE_OK) { log_error("Failed to set writer [%s]", curl_easy_strerror(res)); @@ -292,8 +337,20 @@ sf_bool STDCALL http_perform(CURL *curl, res = curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&buffer); } if (res != CURLE_OK) { - log_error("Failed to set write data [%s]", curl_easy_strerror(res)); - break; + log_error("Failed to set write data [%s]", curl_easy_strerror(res)); + break; + } + + res = curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void*)&headerBuffer); + if (res != CURLE_OK) { + log_error("Failed to set header data [%s]", curl_easy_strerror(res)); + break; + } + + res = curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, (void*)&char_resp_cb); + if (res != CURLE_OK) { + log_error("Failed to set header function [%s]", curl_easy_strerror(res)); + break; } if (DISABLE_VERIFY_PEER) { @@ -501,6 +558,15 @@ sf_bool STDCALL http_perform(CURL *curl, SF_FREE(buffer.buffer); + if (resp_headers) + { + *resp_headers = headerBuffer.buffer; + } + else + { + SF_FREE(headerBuffer.buffer); + } + return ret; } @@ -511,8 +577,10 @@ sf_bool STDCALL __wrap_http_perform(CURL *curl, char *url, SF_HEADER *header, char *body, + PUT_PAYLOAD* put_payload, cJSON **json, NON_JSON_RESP *non_json_resp, + char** resp_headers, int64 network_timeout, sf_bool chunk_downloader, SF_ERROR_STRUCT *error, diff --git a/lib/mock_http_perform.h b/lib/mock_http_perform.h index ce6cad20c3..71ea0c8b79 100644 --- a/lib/mock_http_perform.h +++ b/lib/mock_http_perform.h @@ -17,7 +17,7 @@ extern "C" { // The parameters for this are identical to http_perform located in connection.h // This is just the mock interface sf_bool STDCALL __wrap_http_perform(CURL *curl, SF_REQUEST_TYPE request_type, char *url, SF_HEADER *header, - char *body, cJSON **json, NON_JSON_RESP *non_json_resp, int64 network_timeout, sf_bool chunk_downloader, + char *body, PUT_PAYLOAD* put_payload, cJSON **json, NON_JSON_RESP *non_json_resp, char** resp_headers, int64 network_timeout, sf_bool chunk_downloader, SF_ERROR_STRUCT *error, sf_bool insecure_mode, sf_bool fail_open); #endif diff --git a/tests/test_simple_put.cpp b/tests/test_simple_put.cpp index e592371e46..c16a3f98c2 100755 --- a/tests/test_simple_put.cpp +++ b/tests/test_simple_put.cpp @@ -856,6 +856,19 @@ static int gr_setup(void **unused) { initialize_test(SF_BOOLEAN_FALSE); + // TODO SNOW-1526335 + // Sometime we can't get OCSP response from cache server or responder + // Usually happen on GCP and should be ignored by FAIL_OPEN + // Unfortunately libsnowflakeclient doesn't support FAIL_OPEN for now + // so we have to disable OCSP validation to around it. + // Will remove this code when adding support for FAIL_OPEN (which is + // the default behavior for all other drivers) + char *cenv = getenv("CLOUD_PROVIDER"); + if (cenv && !strncmp(cenv, "GCP", 4)) { + sf_bool value = SF_BOOLEAN_FALSE; + snowflake_global_set_attribute(SF_GLOBAL_OCSP_CHECK, &value); + } + if(!setup_random_database()) { std::cout << "Failed to setup random database, fallback to use regular one." << std::endl; } @@ -1231,12 +1244,6 @@ void test_2GBlarge_put(void **unused) return; } } - // put/get for GCP is not supported in libsnowflakeclient - // will test that in odbc. - if (cenv && !strncmp(cenv, "GCP", 4)) { - errno = 0; - return; - } // Jenkins node on Mac has issue with large file. #ifdef __APPLE__ @@ -1270,12 +1277,6 @@ void test_2GBlarge_get(void **unused) return; } } - // put/get for GCP is not supported in libsnowflakeclient - // will test that in odbc. - if (cenv && !strncmp(cenv, "GCP", 4)) { - errno = 0; - return; - } // Jenkins node on Mac has issue with large file. #ifdef __APPLE__ @@ -1637,7 +1638,7 @@ int main(void) { }); if(testAccount.find("GCP") != std::string::npos) { - setenv("CLOUD_PROVIDER", "GCP", 1); + setenv("CLOUD_PROVIDER", "GCP", 1); } else if(testAccount.find("AZURE") != std::string::npos) { @@ -1651,11 +1652,6 @@ int main(void) { char *cp = getenv("CLOUD_PROVIDER"); std::cout << "Cloud provider is " << cp << std::endl; #endif - const char *cloud_provider = std::getenv("CLOUD_PROVIDER"); - if(cloud_provider && ( strcmp(cloud_provider, "GCP") == 0 ) ) { - std::cout << "GCP put/get feature is not available in libsnowflakeclient." << std::endl; - return 0; - } const struct CMUnitTest tests[] = { cmocka_unit_test_teardown(test_simple_put_auto_compress, teardown),