Skip to content

Commit 0f1b23e

Browse files
authored
Merge pull request #32 from wudidapaopao/upgrade-chdb-version
2 parents 884d7b5 + c809b8f commit 0f1b23e

File tree

6 files changed

+341
-18
lines changed

6 files changed

+341
-18
lines changed

index.js

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ function queryBind(query, args = {}, format = "CSV") {
1919
return chdbNode.QueryBindSession(query, args, format);
2020
}
2121

22-
// Session class with path handling
22+
// Session class with connection-based path handling
2323
class Session {
2424
constructor(path = "") {
2525
if (path === "") {
@@ -30,21 +30,38 @@ class Session {
3030
this.path = path;
3131
this.isTemp = false;
3232
}
33+
34+
// Create a connection for this session
35+
this.connection = chdbNode.CreateConnection(this.path);
36+
if (!this.connection) {
37+
throw new Error("Failed to create connection");
38+
}
3339
}
3440

3541
query(query, format = "CSV") {
3642
if (!query) return "";
37-
return chdbNode.QuerySession(query, format, this.path);
43+
if (!this.connection) {
44+
throw new Error("No active connection available");
45+
}
46+
return chdbNode.QueryWithConnection(this.connection, query, format);
3847
}
3948

4049
queryBind(query, args = {}, format = "CSV") {
41-
if(!query) return "";
42-
return chdbNode.QueryBindSession(query, args, format, this.path)
50+
throw new Error("QueryBind is not supported with connection-based sessions. Please use the standalone queryBind function instead.");
4351
}
4452

45-
// Cleanup method to delete the temporary directory
53+
// Cleanup method to close connection and delete directory if temp
4654
cleanup() {
47-
rmSync(this.path, { recursive: true }); // Replaced rmdirSync with rmSync
55+
// Close the connection if it exists
56+
if (this.connection) {
57+
chdbNode.CloseConnection(this.connection);
58+
this.connection = null;
59+
}
60+
61+
// Only delete directory if it's temporary
62+
if (this.isTemp) {
63+
rmSync(this.path, { recursive: true });
64+
}
4865
}
4966
}
5067

lib/chdb_node.cpp

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@
66
#include <iostream>
77
#include <napi.h>
88

9+
typedef void * ChdbConnection;
10+
ChdbConnection CreateConnection(const char * path);
11+
void CloseConnection(ChdbConnection conn);
12+
char * QueryWithConnection(ChdbConnection conn, const char * query, const char * format, char ** error_message);
13+
914
#define MAX_FORMAT_LENGTH 64
1015
#define MAX_PATH_LENGTH 4096
1116
#define MAX_ARG_COUNT 6
@@ -189,6 +194,56 @@ char *QueryBindSession(const char *query, const char *format, const char *path,
189194
return query_stable_v2(static_cast<int>(argv.size()), argv.data())->buf;
190195
}
191196

197+
ChdbConnection CreateConnection(const char * path) {
198+
char dataPath[MAX_PATH_LENGTH];
199+
char * args[MAX_ARG_COUNT] = {"clickhouse", NULL};
200+
int argc = 1;
201+
202+
if (path && path[0]) {
203+
construct_arg(dataPath, "--path=", path, MAX_PATH_LENGTH);
204+
args[1] = dataPath;
205+
argc = 2;
206+
}
207+
208+
return static_cast<ChdbConnection>(chdb_connect(argc, args));
209+
}
210+
211+
void CloseConnection(ChdbConnection conn) {
212+
if (conn) {
213+
chdb_close_conn(static_cast<chdb_connection *>(conn));
214+
}
215+
}
216+
217+
char * QueryWithConnection(ChdbConnection conn, const char * query, const char * format, char ** error_message) {
218+
if (!conn || !query || !format) {
219+
return nullptr;
220+
}
221+
222+
chdb_connection * inner_conn = static_cast<chdb_connection *>(conn);
223+
chdb_result * result = chdb_query(*inner_conn, query, format);
224+
if (!result) {
225+
return nullptr;
226+
}
227+
228+
const char * error = chdb_result_error(result);
229+
if (error) {
230+
if (error_message) {
231+
*error_message = strdup(error);
232+
}
233+
chdb_destroy_query_result(result);
234+
return nullptr;
235+
}
236+
237+
const char * buffer = chdb_result_buffer(result);
238+
char * output = nullptr;
239+
if (buffer) {
240+
output = strdup(buffer);
241+
}
242+
243+
chdb_destroy_query_result(result);
244+
return output;
245+
}
246+
192247
Napi::String QueryWrapper(const Napi::CallbackInfo &info) {
193248
Napi::Env env = info.Env();
194249

@@ -291,11 +346,81 @@ Napi::String QueryBindSessionWrapper(const Napi::CallbackInfo& info) {
291346
return Napi::String::New(env, out);
292347
}
293348

349+
Napi::Value CreateConnectionWrapper(const Napi::CallbackInfo & info) {
350+
Napi::Env env = info.Env();
351+
352+
if (info.Length() < 1 || !info[0].IsString()) {
353+
Napi::TypeError::New(env, "Path string expected").ThrowAsJavaScriptException();
354+
return env.Null();
355+
}
356+
357+
std::string path = info[0].As<Napi::String>().Utf8Value();
358+
ChdbConnection conn = CreateConnection(path.c_str());
359+
360+
if (!conn) {
361+
Napi::Error::New(env, "Failed to create connection").ThrowAsJavaScriptException();
362+
return env.Null();
363+
}
364+
365+
return Napi::External<void>::New(env, conn);
366+
}
367+
368+
Napi::Value CloseConnectionWrapper(const Napi::CallbackInfo & info) {
369+
Napi::Env env = info.Env();
370+
371+
if (info.Length() < 1 || !info[0].IsExternal()) {
372+
Napi::TypeError::New(env, "Connection handle expected").ThrowAsJavaScriptException();
373+
return env.Undefined();
374+
}
375+
376+
ChdbConnection conn = info[0].As<Napi::External<void>>().Data();
377+
CloseConnection(conn);
378+
379+
return env.Undefined();
380+
}
381+
382+
Napi::String QueryWithConnectionWrapper(const Napi::CallbackInfo & info) {
383+
Napi::Env env = info.Env();
384+
385+
if (info.Length() < 3 || !info[0].IsExternal() || !info[1].IsString() || !info[2].IsString()) {
386+
Napi::TypeError::New(env, "Usage: connection, query, format").ThrowAsJavaScriptException();
387+
return Napi::String::New(env, "");
388+
}
389+
390+
ChdbConnection conn = info[0].As<Napi::External<void>>().Data();
391+
std::string query = info[1].As<Napi::String>().Utf8Value();
392+
std::string format = info[2].As<Napi::String>().Utf8Value();
393+
394+
char * error_message = nullptr;
395+
char * result = QueryWithConnection(conn, query.c_str(), format.c_str(), &error_message);
396+
397+
if (error_message) {
398+
std::string error_msg = std::string("Query failed: ") + error_message;
399+
free(error_message);
400+
Napi::Error::New(env, error_msg).ThrowAsJavaScriptException();
401+
return Napi::String::New(env, "");
402+
}
403+
404+
if (!result) {
405+
return Napi::String::New(env, "");
406+
}
407+
408+
Napi::String output = Napi::String::New(env, result);
409+
free(result);
410+
return output;
411+
}
412+
294413
Napi::Object Init(Napi::Env env, Napi::Object exports) {
295414
// Export the functions
296415
exports.Set("Query", Napi::Function::New(env, QueryWrapper));
297416
exports.Set("QuerySession", Napi::Function::New(env, QuerySessionWrapper));
298417
exports.Set("QueryBindSession", Napi::Function::New(env, QueryBindSessionWrapper));
418+
419+
// Export connection management functions
420+
exports.Set("CreateConnection", Napi::Function::New(env, CreateConnectionWrapper));
421+
exports.Set("CloseConnection", Napi::Function::New(env, CloseConnectionWrapper));
422+
exports.Set("QueryWithConnection", Napi::Function::New(env, QueryWithConnectionWrapper));
423+
299424
return exports;
300425
}
301426

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "chdb",
3-
"version": "1.4.0",
3+
"version": "1.5.0",
44
"description": "chDB bindings for nodejs",
55
"main": "index.js",
66
"repository": {
@@ -9,7 +9,7 @@
99
},
1010
"scripts": {
1111
"install": "npm run libchdb && npm run build",
12-
"test": "mocha --timeout 15000",
12+
"test": "mocha test_basic.js test_connection.js --timeout 15000",
1313
"libchdb": "bash ./update_libchdb.sh",
1414
"fixloaderpath": "bash ./fix_loader_path.sh",
1515
"build": "node-gyp configure build --verbose && npm run fixloaderpath"

test.js renamed to test_basic.js

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,16 @@ describe('chDB Queries', function () {
5656
let session;
5757

5858
before(function () {
59-
// Create a new session instance before running the tests
60-
session = new Session("./chdb-node-tmp");
59+
// Delete existing directory and create a new session instance
60+
const fs = require('fs');
61+
const path = require('path');
62+
const tmpDir = "./chdb-node-tmp";
63+
64+
if (fs.existsSync(tmpDir)) {
65+
fs.rmSync(tmpDir, { recursive: true, force: true });
66+
}
67+
68+
session = new Session(tmpDir);
6169
});
6270

6371
after(function () {
@@ -92,12 +100,10 @@ describe('chDB Queries', function () {
92100
}).to.throw(Error, /Unknown table expression identifier/);
93101
});
94102

95-
it('should return result of the query made using bind parameters', () => {
96-
const ret = session.queryBind("SELECT * from testdb.testtable where id > {id: UInt32}", { id: 2}, "CSV");
97-
console.log("Bind Session result:", ret);
98-
expect(ret).to.not.include('1');
99-
expect(ret).to.not.include('2');
100-
expect(ret).to.include('3');
103+
it('should throw an error when using queryBind with session', () => {
104+
expect(() => {
105+
session.queryBind("SELECT * from testdb.testtable where id > {id: UInt32}", { id: 2}, "CSV");
106+
}).to.throw(Error, /QueryBind is not supported with connection-based sessions. Please use the standalone queryBind function instead./);
101107
})
102108
});
103109

0 commit comments

Comments
 (0)