Skip to content

Commit 5a7e705

Browse files
authored
Merge pull request #174 from duckdb/jray/bindings-for-instance-cache
bindings for instance cache
2 parents 4260fa8 + 9217aa6 commit 5a7e705

File tree

3 files changed

+137
-4
lines changed

3 files changed

+137
-4
lines changed

bindings/pkgs/@duckdb/node-bindings/duckdb.d.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,10 @@ export interface ExtractedStatements {
204204
__duckdb_type: 'duckdb_extracted_statements';
205205
}
206206

207+
export interface InstanceCache {
208+
__duckdb_type: 'duckdb_instance_cache';
209+
}
210+
207211
export interface LogicalType {
208212
__duckdb_type: 'duckdb_logical_type';
209213
}
@@ -240,8 +244,13 @@ export interface ExtractedStatementsAndCount {
240244
// Functions
241245

242246
// DUCKDB_API duckdb_instance_cache duckdb_create_instance_cache();
247+
export function create_instance_cache(): InstanceCache;
248+
243249
// DUCKDB_API duckdb_state duckdb_get_or_create_from_cache(duckdb_instance_cache instance_cache, const char *path, duckdb_database *out_database, duckdb_config config, char **out_error);
250+
export function get_or_create_from_cache(cache: InstanceCache, path?: string, config?: Config): Promise<Database>;
251+
244252
// DUCKDB_API void duckdb_destroy_instance_cache(duckdb_instance_cache *instance_cache);
253+
// not exposed: destroyed in finalizer
245254

246255
// DUCKDB_API duckdb_state duckdb_open(const char *path, duckdb_database *out_database);
247256
export function open(path?: string, config?: Config): Promise<Database>;

bindings/src/duckdb_node_bindings.cpp

Lines changed: 101 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,22 @@ duckdb_extracted_statements GetExtractedStatementsFromExternal(Napi::Env env, Na
472472
return GetDataFromExternal<_duckdb_extracted_statements>(env, ExtractedStatementsTypeTag, value, "Invalid extracted statements argument");
473473
}
474474

475+
static const napi_type_tag InstanceCacheTypeTag = {
476+
0x2F3346E30FB5457C, 0xB9201EE5112EEF9F
477+
};
478+
479+
void FinalizeInstanceCache(Napi::BasicEnv, duckdb_instance_cache instance_cache) {
480+
duckdb_destroy_instance_cache(&instance_cache);
481+
}
482+
483+
Napi::External<_duckdb_instance_cache> CreateExternalForInstanceCache(Napi::Env env, duckdb_instance_cache instance_cache) {
484+
return CreateExternal<_duckdb_instance_cache>(env, InstanceCacheTypeTag, instance_cache, FinalizeInstanceCache);
485+
}
486+
487+
duckdb_instance_cache GetInstanceCacheFromExternal(Napi::Env env, Napi::Value value) {
488+
return GetDataFromExternal<_duckdb_instance_cache>(env, InstanceCacheTypeTag, value, "Invalid instance cache argument");
489+
}
490+
475491
static const napi_type_tag LogicalTypeTypeTag = {
476492
0x78AF202191ED4A23, 0x8093715369592A2B
477493
};
@@ -613,6 +629,58 @@ class PromiseWorker : public Napi::AsyncWorker {
613629

614630
};
615631

632+
class GetOrCreateFromCacheWorker : public PromiseWorker {
633+
634+
public:
635+
636+
GetOrCreateFromCacheWorker(Napi::Env env, duckdb_instance_cache instance_cache, std::optional<std::string> path, duckdb_config config)
637+
: PromiseWorker(env), instance_cache_(instance_cache), path_(path), config_(config) {
638+
}
639+
640+
protected:
641+
642+
void Execute() override {
643+
const char *path = nullptr;
644+
if (path_) {
645+
path = path_->c_str();
646+
}
647+
duckdb_config cfg = config_;
648+
// If no config was provided, create one with the default duckdb_api value.
649+
if (cfg == nullptr) {
650+
if (duckdb_create_config(&cfg)) {
651+
duckdb_destroy_config(&cfg);
652+
SetError("Failed to create config");
653+
return;
654+
}
655+
if (duckdb_set_config(cfg, "duckdb_api", DEFAULT_DUCKDB_API)) {
656+
SetError("Failed to set duckdb_api");
657+
return;
658+
}
659+
}
660+
char *error = nullptr;
661+
if (duckdb_get_or_create_from_cache(instance_cache_, path, &database_, cfg, &error)) {
662+
if (error != nullptr) {
663+
SetError(error);
664+
duckdb_free(error);
665+
} else {
666+
SetError("Failed to get or create from cache");
667+
}
668+
}
669+
}
670+
671+
Napi::Value Result() override {
672+
return CreateExternalForDatabase(Env(), database_);
673+
}
674+
675+
private:
676+
677+
duckdb_instance_cache instance_cache_;
678+
std::optional<std::string> path_;
679+
duckdb_config config_;
680+
duckdb_database database_ = nullptr;
681+
682+
};
683+
616684
class OpenWorker : public PromiseWorker {
617685

618686
public:
@@ -1093,6 +1161,9 @@ class DuckDBNodeAddon : public Napi::Addon<DuckDBNodeAddon> {
10931161
InstanceValue("StatementType", CreateStatementTypeEnum(env)),
10941162
InstanceValue("Type", CreateTypeEnum(env)),
10951163

1164+
InstanceMethod("create_instance_cache", &DuckDBNodeAddon::create_instance_cache),
1165+
InstanceMethod("get_or_create_from_cache", &DuckDBNodeAddon::get_or_create_from_cache),
1166+
10961167
InstanceMethod("open", &DuckDBNodeAddon::open),
10971168
InstanceMethod("connect", &DuckDBNodeAddon::connect),
10981169
InstanceMethod("interrupt", &DuckDBNodeAddon::interrupt),
@@ -1354,11 +1425,38 @@ class DuckDBNodeAddon : public Napi::Addon<DuckDBNodeAddon> {
13541425
private:
13551426

13561427
// DUCKDB_API duckdb_instance_cache duckdb_create_instance_cache();
1428+
// function create_instance_cache(): InstanceCache
1429+
Napi::Value create_instance_cache(const Napi::CallbackInfo& info) {
1430+
auto env = info.Env();
1431+
auto instance_cache = duckdb_create_instance_cache();
1432+
return CreateExternalForInstanceCache(env, instance_cache);
1433+
}
1434+
13571435
// DUCKDB_API duckdb_state duckdb_get_or_create_from_cache(duckdb_instance_cache instance_cache, const char *path, duckdb_database *out_database, duckdb_config config, char **out_error);
1436+
// function get_or_create_from_cache(cache: InstanceCache, path?: string, config?: Config): Promise<Database>
1437+
Napi::Value get_or_create_from_cache(const Napi::CallbackInfo& info) {
1438+
auto env = info.Env();
1439+
auto instance_cache = GetInstanceCacheFromExternal(env, info[0]);
1440+
auto pathValue = info[1];
1441+
auto configValue = info[2];
1442+
std::optional<std::string> path = std::nullopt;
1443+
if (!pathValue.IsUndefined()) {
1444+
path = pathValue.As<Napi::String>();
1445+
}
1446+
duckdb_config config = nullptr;
1447+
if (!configValue.IsUndefined()) {
1448+
config = GetConfigFromExternal(env, configValue);
1449+
}
1450+
auto worker = new GetOrCreateFromCacheWorker(env, instance_cache, path, config);
1451+
worker->Queue();
1452+
return worker->Promise();
1453+
}
1454+
13581455
// DUCKDB_API void duckdb_destroy_instance_cache(duckdb_instance_cache *instance_cache);
1456+
// not exposed: destroyed in finalizer
13591457

13601458
// DUCKDB_API duckdb_state duckdb_open(const char *path, duckdb_database *out_database);
1361-
// function open(path: string): Promise<Database>
1459+
// function open(path?: string, config?: Config): Promise<Database>
13621460
Napi::Value open(const Napi::CallbackInfo& info) {
13631461
auto env = info.Env();
13641462
auto pathValue = info[0];
@@ -4268,8 +4366,7 @@ NODE_API_ADDON(DuckDBNodeAddon)
42684366
---
42694367
411 total functions
42704368
4271-
239 instance methods
4272-
3 unimplemented instance cache functions
4369+
241 instance methods
42734370
1 unimplemented logical type function
42744371
13 unimplemented scalar function functions
42754372
4 unimplemented scalar function set functions
@@ -4285,7 +4382,7 @@ NODE_API_ADDON(DuckDBNodeAddon)
42854382
6 unimplemented table description functions
42864383
8 unimplemented tasks functions
42874384
12 unimplemented cast function functions
4288-
24 functions not exposed
4385+
25 functions not exposed
42894386
+ 41 unimplemented deprecated functions (of 47)
42904387
---
42914388
411 functions accounted for

bindings/test/instance_cache.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import duckdb from '@duckdb/node-bindings';
2+
import { expect, suite, test } from 'vitest';
3+
4+
suite('instance_cache', () => {
5+
test('create', async () => {
6+
const cache = duckdb.create_instance_cache();
7+
expect(cache).toBeTruthy();
8+
});
9+
test('empty path', async () => {
10+
const cache = duckdb.create_instance_cache();
11+
const db1 = await duckdb.get_or_create_from_cache(cache, '');
12+
expect(db1).toBeTruthy();
13+
console.log(db1);
14+
const db2 = await duckdb.get_or_create_from_cache(cache, '');
15+
expect(db2).toStrictEqual(db1);
16+
console.log(db2);
17+
});
18+
test('memory path', async () => {
19+
const cache = duckdb.create_instance_cache();
20+
const db1 = await duckdb.get_or_create_from_cache(cache, ':memory:');
21+
expect(db1).toBeTruthy();
22+
console.log(db1);
23+
const db2 = await duckdb.get_or_create_from_cache(cache, ':memory:');
24+
expect(db2).toStrictEqual(db1);
25+
console.log(db2);
26+
});
27+
});

0 commit comments

Comments
 (0)