|
| 1 | +#include "chdb.h" |
| 2 | +#include "chdb_node.h" |
| 3 | +#include <stdio.h> |
| 4 | +#include <stdlib.h> |
| 5 | +#include <string.h> |
| 6 | +#include <iostream> |
| 7 | +#include <napi.h> |
| 8 | + |
| 9 | +#define MAX_FORMAT_LENGTH 64 |
| 10 | +#define MAX_PATH_LENGTH 4096 |
| 11 | +#define MAX_ARG_COUNT 6 |
| 12 | + |
| 13 | +// Utility function to construct argument string |
| 14 | +void construct_arg(char *dest, const char *prefix, const char *value, |
| 15 | + size_t dest_size) { |
| 16 | + snprintf(dest, dest_size, "%s%s", prefix, value); |
| 17 | +} |
| 18 | + |
| 19 | +// Generalized query function |
| 20 | +char *general_query(int argc, char *args[]) { |
| 21 | + struct local_result *result = query_stable(argc, args); |
| 22 | + |
| 23 | + if (result == NULL) { |
| 24 | + return NULL; |
| 25 | + } else { |
| 26 | + return result->buf; |
| 27 | + } |
| 28 | +} |
| 29 | + |
| 30 | +// Query function without session |
| 31 | +char *Query(const char *query, const char *format) { |
| 32 | + char dataFormat[MAX_FORMAT_LENGTH]; |
| 33 | + char *dataQuery; |
| 34 | + char *args[MAX_ARG_COUNT] = {"clickhouse", "--multiquery", NULL, NULL}; |
| 35 | + int argc = 4; |
| 36 | + |
| 37 | + construct_arg(dataFormat, "--output-format=", format, MAX_FORMAT_LENGTH); |
| 38 | + args[2] = dataFormat; |
| 39 | + |
| 40 | + dataQuery = (char *)malloc(strlen(query) + strlen("--query=") + 1); |
| 41 | + if (dataQuery == NULL) { |
| 42 | + return NULL; |
| 43 | + } |
| 44 | + construct_arg(dataQuery, "--query=", query, |
| 45 | + strlen(query) + strlen("--query=") + 1); |
| 46 | + args[3] = dataQuery; |
| 47 | + |
| 48 | + char *result = general_query(argc, args); |
| 49 | + free(dataQuery); |
| 50 | + return result; |
| 51 | +} |
| 52 | + |
| 53 | +// QuerySession function will save the session to the path |
| 54 | +// queries with same path will use the same session |
| 55 | +char *QuerySession(const char *query, const char *format, const char *path) { |
| 56 | + char dataFormat[MAX_FORMAT_LENGTH]; |
| 57 | + char dataPath[MAX_PATH_LENGTH]; |
| 58 | + char *dataQuery; |
| 59 | + char *args[MAX_ARG_COUNT] = {"clickhouse", "--multiquery", NULL, NULL, NULL}; |
| 60 | + int argc = 5; |
| 61 | + |
| 62 | + construct_arg(dataFormat, "--output-format=", format, MAX_FORMAT_LENGTH); |
| 63 | + args[2] = dataFormat; |
| 64 | + |
| 65 | + dataQuery = (char *)malloc(strlen(query) + strlen("--query=") + 1); |
| 66 | + if (dataQuery == NULL) { |
| 67 | + return NULL; |
| 68 | + } |
| 69 | + construct_arg(dataQuery, "--query=", query, |
| 70 | + strlen(query) + strlen("--query=") + 1); |
| 71 | + args[3] = dataQuery; |
| 72 | + |
| 73 | + construct_arg(dataPath, "--path=", path, MAX_PATH_LENGTH); |
| 74 | + args[4] = dataPath; |
| 75 | + |
| 76 | + char *result = general_query(argc, args); |
| 77 | + free(dataQuery); |
| 78 | + return result; |
| 79 | +} |
| 80 | + |
| 81 | +Napi::String QueryWrapper(const Napi::CallbackInfo &info) { |
| 82 | + Napi::Env env = info.Env(); |
| 83 | + |
| 84 | + // Check argument types and count |
| 85 | + if (info.Length() < 2 || !info[0].IsString() || !info[1].IsString()) { |
| 86 | + Napi::TypeError::New(env, "String expected").ThrowAsJavaScriptException(); |
| 87 | + return Napi::String::New(env, ""); |
| 88 | + } |
| 89 | + |
| 90 | + // Get the arguments |
| 91 | + std::string query = info[0].As<Napi::String>().Utf8Value(); |
| 92 | + std::string format = info[1].As<Napi::String>().Utf8Value(); |
| 93 | + |
| 94 | + // Call the native function |
| 95 | + char *result = Query(query.c_str(), format.c_str()); |
| 96 | + |
| 97 | + // Return the result |
| 98 | + return Napi::String::New(env, result); |
| 99 | +} |
| 100 | + |
| 101 | +Napi::String QuerySessionWrapper(const Napi::CallbackInfo &info) { |
| 102 | + Napi::Env env = info.Env(); |
| 103 | + |
| 104 | + // Check argument types and count |
| 105 | + if (info.Length() < 3 || !info[0].IsString() || !info[1].IsString() || |
| 106 | + !info[2].IsString()) { |
| 107 | + Napi::TypeError::New(env, "String expected").ThrowAsJavaScriptException(); |
| 108 | + return Napi::String::New(env, ""); |
| 109 | + } |
| 110 | + |
| 111 | + // Get the arguments |
| 112 | + std::string query = info[0].As<Napi::String>().Utf8Value(); |
| 113 | + std::string format = info[1].As<Napi::String>().Utf8Value(); |
| 114 | + std::string path = info[2].As<Napi::String>().Utf8Value(); |
| 115 | + |
| 116 | + // std::cerr << query << std::endl; |
| 117 | + // std::cerr << format << std::endl; |
| 118 | + // std::cerr << path << std::endl; |
| 119 | + // Call the native function |
| 120 | + char *result = QuerySession(query.c_str(), format.c_str(), path.c_str()); |
| 121 | + if (result == NULL) { |
| 122 | + // std::cerr << "result is null" << std::endl; |
| 123 | + return Napi::String::New(env, ""); |
| 124 | + } |
| 125 | + |
| 126 | + // Return the result |
| 127 | + return Napi::String::New(env, result); |
| 128 | +} |
| 129 | + |
| 130 | +Napi::Object Init(Napi::Env env, Napi::Object exports) { |
| 131 | + // Export the functions |
| 132 | + exports.Set("Query", Napi::Function::New(env, QueryWrapper)); |
| 133 | + exports.Set("QuerySession", Napi::Function::New(env, QuerySessionWrapper)); |
| 134 | + return exports; |
| 135 | +} |
| 136 | + |
| 137 | +NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init) |
0 commit comments