Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SNOW-910807: refactoring to reuse query context cache implementation in ODBC #647

Merged
2 commits merged into from
Jan 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ set(SOURCE_FILES_CPP_WRAPPER
include/snowflake/Param.hpp
include/snowflake/Exceptions.hpp
include/snowflake/jwtWrapper.h
include/snowflake/QueryContextCache.hpp
cpp/lib/Exceptions.cpp
cpp/lib/Connection.cpp
cpp/lib/Statement.cpp
Expand All @@ -219,7 +220,8 @@ set(SOURCE_FILES_CPP_WRAPPER
cpp/lib/DataConversion.cpp
cpp/lib/DataConversion.hpp
cpp/lib/QueryContextCache.cpp
cpp/lib/QueryContextCache.hpp
cpp/lib/ClientQueryContextCache.cpp
cpp/lib/ClientQueryContextCache.hpp
cpp/lib/result_set.cpp
cpp/lib/result_set_arrow.cpp
cpp/lib/result_set_json.cpp
Expand Down
324 changes: 324 additions & 0 deletions cpp/lib/ClientQueryContextCache.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,324 @@
/*
* Copyright (c) 2024 Snowflake Computing, Inc. All rights reserved.
*/

#include "ClientQueryContextCache.hpp"
#include "../logger/SFLogger.hpp"
#include "query_context_cache.h"

// wrapper functions for C
extern "C" {

sf_bool is_qcc_enabled(SF_CONNECT * conn)
{
if (!conn || conn->qcc_disable)
{
return SF_BOOLEAN_FALSE;
}
return SF_BOOLEAN_TRUE;
}

void qcc_initialize(SF_CONNECT * conn)
{
if (!is_qcc_enabled(conn) || conn->qcc)
{
return;
}

conn->qcc = (void *) new Snowflake::Client::ClientQueryContextCache(conn->qcc_capacity);
}

void qcc_set_capacity(SF_CONNECT * conn, uint64 capacity)
{
if (!is_qcc_enabled(conn))
{
return;
}
if (!conn->qcc)
{
qcc_initialize(conn);
}
static_cast<Snowflake::Client::ClientQueryContextCache *>(conn->qcc)->setCapacity(capacity);
}

cJSON* qcc_serialize(SF_CONNECT * conn)
{
if (!is_qcc_enabled(conn) || !conn->qcc)
{
return NULL;
}

return static_cast<Snowflake::Client::ClientQueryContextCache *>(conn->qcc)->serializeQueryContext();
}

void qcc_deserialize(SF_CONNECT * conn, cJSON* query_context)
{
if (!is_qcc_enabled(conn))
{
return;
}
if (!conn->qcc)
{
qcc_initialize(conn);
}

static_cast<Snowflake::Client::ClientQueryContextCache *>(conn->qcc)->deserializeQueryContext(query_context);
}

void qcc_terminate(SF_CONNECT * conn)
{
if (!conn || !conn->qcc)
sfc-gh-dprzybysz marked this conversation as resolved.
Show resolved Hide resolved
{
return;
}

delete static_cast<Snowflake::Client::ClientQueryContextCache *>(conn->qcc);
}

} // extern "C"

namespace Snowflake
{
namespace Client
{

// Public ======================================================================
void ClientQueryContextCache::deserializeQueryContext(cJSON * data)
{
if ((!data) || (data->type != cJSON_Object))
{
// Clear the cache
clearCache();
return;
}

// Deserialize the entries. The first entry with priority is the main entry.
// Save all entries into one list to simplify the logic. An example JSON is:
// {
// "entries": [
// {
// "id": 0,
// "read_timestamp": 123456789,
// "priority": 0,
// "context": "base64 encoded context"
// },
// {
// "id": 1,
// "read_timestamp": 123456789,
// "priority": 1,
// "context": "base64 encoded context"
// },
// {
// "id": 2,
// "read_timestamp": 123456789,
// "priority": 2,
// "context": "base64 encoded context"
// }
// ]
std::vector<QueryContextElement> elements;
cJSON * entries = snowflake_cJSON_GetObjectItem(data, SF_QCC_ENTRIES_KEY);
if ((entries) && (entries->type == cJSON_Array))
{
int arraySize = snowflake_cJSON_GetArraySize(entries);
for (int i = 0; i < arraySize; i++)
{
QueryContextElement entry;
if (deserializeQueryContextElement(snowflake_cJSON_GetArrayItem(entries, i), entry))
{
elements.push_back(entry);
}
else
{
CXX_LOG_TRACE("ClientQueryContextCache::deserializeQueryContext: "
"meets mismatch field type. Clear the QueryContextCache.");
clearCache();
return;
}
}
}

UpdateQueryContextCache(elements);
}

void ClientQueryContextCache::deserializeQueryContextReq(cJSON * data)
{
if ((!data) || (data->type != cJSON_Object))
{
// Clear the cache
clearCache();
return;
}

std::vector<QueryContextElement> elements;
cJSON * entries = snowflake_cJSON_GetObjectItem(data, SF_QCC_ENTRIES_KEY);
int arraySize = snowflake_cJSON_GetArraySize(entries);
if ((entries) && (entries->type == cJSON_Array))
{
for (int i = 0; i < arraySize; i++)
{
QueryContextElement entry;
if (deserializeQueryContextElementReq(snowflake_cJSON_GetArrayItem(entries, i), entry))
{
elements.push_back(entry);
}
else
{
CXX_LOG_TRACE("ClientQueryContextCache::deserializeQueryContextReq: "
"meets mismatch field type. Clear the QueryContextCache.");
clearCache();
return;
}
}
}

UpdateQueryContextCache(elements);
}

cJSON * ClientQueryContextCache::serializeQueryContext()
{
std::vector<QueryContextElement> elements;
GetQueryContextEntries(elements);

cJSON * queryContext = snowflake_cJSON_CreateObject();
cJSON * entries = snowflake_cJSON_CreateArray();
for (auto itr = elements.begin(); itr != elements.end(); itr++)
{
cJSON * entry = snowflake_cJSON_CreateObject();
snowflake_cJSON_AddUint64ToObject(entry, SF_QCC_ID_KEY, itr->id);
snowflake_cJSON_AddUint64ToObject(entry, SF_QCC_PRIORITY_KEY, itr->priority);
snowflake_cJSON_AddUint64ToObject(entry, SF_QCC_TIMESTAMP_KEY, itr->readTimestamp);
cJSON * context = snowflake_cJSON_CreateObject();
if (!itr->context.empty())
{
snowflake_cJSON_AddStringToObject(context, SF_QCC_CONTEXT_VALUE_KEY, itr->context.c_str());
}
snowflake_cJSON_AddItemToObject(entry, SF_QCC_CONTEXT_KEY, context);
snowflake_cJSON_AddItemToArray(entries, entry);
}

snowflake_cJSON_AddItemToObject(queryContext, SF_QCC_ENTRIES_KEY, entries);

return queryContext;
}

// Private =====================================================================
bool ClientQueryContextCache::deserializeQueryContextElement(cJSON * entryNode,
QueryContextElement & contextElement)
{
cJSON * id = snowflake_cJSON_GetObjectItem(entryNode, SF_QCC_ID_KEY);
if (id && (id->type == cJSON_Number))
{
contextElement.id = snowflake_cJSON_GetUint64Value(id);
}
else
{
CXX_LOG_WARN("ClientQueryContextCache::deserializeQueryContextElement: `id` field is not integer type");
return false;
}

cJSON * timestamp = snowflake_cJSON_GetObjectItem(entryNode, SF_QCC_TIMESTAMP_KEY);
if (timestamp && (timestamp->type == cJSON_Number))
{
contextElement.readTimestamp = snowflake_cJSON_GetUint64Value(timestamp);
}
else
{
CXX_LOG_WARN("ClientQueryContextCache::deserializeQueryContextElement: `timestamp` field is not integer type");
return false;
}

cJSON * priority = snowflake_cJSON_GetObjectItem(entryNode, SF_QCC_PRIORITY_KEY);
if (priority && (priority->type == cJSON_Number))
{
contextElement.priority = snowflake_cJSON_GetUint64Value(priority);
}
else
{
CXX_LOG_WARN("ClientQueryContextCache::deserializeQueryContextElement: `priority` field is not integer type");
return false;
}

cJSON * context = snowflake_cJSON_GetObjectItem(entryNode, SF_QCC_CONTEXT_KEY);
if (!context || context->type == cJSON_NULL)
{
return true;
}
if (context->type == cJSON_String)
{
contextElement.context = snowflake_cJSON_GetStringValue(context);
}
else
{
CXX_LOG_WARN("ClientQueryContextCache::deserializeQueryContextElement: `context` field is not string type");
return false;
}

return true;
}

bool ClientQueryContextCache::deserializeQueryContextElementReq(cJSON * entryNode,
QueryContextElement & contextElement)
{
cJSON * id = snowflake_cJSON_GetObjectItem(entryNode, SF_QCC_ID_KEY);
if (id && (id->type == cJSON_Number))
{
contextElement.id = snowflake_cJSON_GetUint64Value(id);
}
else
{
CXX_LOG_WARN("ClientQueryContextCache::deserializeQueryContextElement: `id` field is not integer type");
return false;
}

cJSON * timestamp = snowflake_cJSON_GetObjectItem(entryNode, SF_QCC_TIMESTAMP_KEY);
if (timestamp && (timestamp->type == cJSON_Number))
{
contextElement.readTimestamp = snowflake_cJSON_GetUint64Value(timestamp);
}
else
{
CXX_LOG_WARN("ClientQueryContextCache::deserializeQueryContextElement: `timestamp` field is not integer type");
return false;
}

cJSON * priority = snowflake_cJSON_GetObjectItem(entryNode, SF_QCC_PRIORITY_KEY);
if (priority && (priority->type == cJSON_Number))
{
contextElement.priority = snowflake_cJSON_GetUint64Value(priority);
}
else
{
CXX_LOG_WARN("ClientQueryContextCache::deserializeQueryContextElement: `priority` field is not integer type");
return false;
}

cJSON * context = snowflake_cJSON_GetObjectItem(entryNode, SF_QCC_CONTEXT_KEY);
if (!context || context->type == cJSON_NULL)
{
return true;
}
if (context->type != cJSON_Object)
{
return false;
}

cJSON * contextValue = snowflake_cJSON_GetObjectItem(context, SF_QCC_CONTEXT_VALUE_KEY);
if (!contextValue || contextValue->type == cJSON_NULL)
{
return true;
}

if (contextValue->type == cJSON_String)
{
contextElement.context = snowflake_cJSON_GetStringValue(contextValue);
}
else
{
return false;
}

return true;
}

} // namespace Client
} // namespace Snowflake
55 changes: 55 additions & 0 deletions cpp/lib/ClientQueryContextCache.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright (c) 2024 Snowflake Computing, Inc. All rights reserved.
*/

#pragma once
#ifndef CLIENT_QUERY_CONTEXT_CACHE_HPP
#define CLIENT_QUERY_CONTEXT_CACHE_HPP

#include <mutex>
#include <map>
#include <set>
#include <vector>
#include "cJSON.h"
#include "snowflake/client.h"
#include "snowflake/QueryContextCache.hpp"

namespace Snowflake
{
namespace Client
{

class ClientQueryContextCache : public QueryContextCache
{
public:
// constructor
ClientQueryContextCache(size_t capacity) : QueryContextCache(capacity) {}

/**
* @param data: the JSON value of QueryContext Object
*/
void deserializeQueryContext(cJSON * data);

cJSON * serializeQueryContext();

/**
* for test purpose only, deserialize from JSON value serialized from cache
* for sending request
* @param data: the JSON value of QueryContext Object
*/
void deserializeQueryContextReq(cJSON * data);

private:
bool deserializeQueryContextElement(cJSON * entryNode,
QueryContextElement & contextElement);

// for test purpose only, deserializeQueryContextElement from serialized
// JSON value for request
bool deserializeQueryContextElementReq(cJSON * entryNode,
QueryContextElement & contextElement);
};

} // namespace Client
} // namespace Snowflake

#endif // QUERY_CONTEXT_CACHE_HPP
Loading
Loading