diff --git a/api/src/DuckDBPreparedStatement.ts b/api/src/DuckDBPreparedStatement.ts index 2d79a74..1e943ca 100644 --- a/api/src/DuckDBPreparedStatement.ts +++ b/api/src/DuckDBPreparedStatement.ts @@ -47,6 +47,9 @@ export class DuckDBPreparedStatement { constructor(prepared_statement: duckdb.PreparedStatement) { this.prepared_statement = prepared_statement; } + public destroySync() { + return duckdb.destroy_prepare_sync(this.prepared_statement); + } public get statementType(): StatementType { return duckdb.prepared_statement_type(this.prepared_statement); } diff --git a/api/test/api.test.ts b/api/test/api.test.ts index 2eb5225..bacc53c 100644 --- a/api/test/api.test.ts +++ b/api/test/api.test.ts @@ -398,6 +398,7 @@ describe('api', () => { const connection = await instance.connect(); const prepared1 = await connection.prepare('select 1'); assert.isDefined(prepared1); + prepared1.destroySync(); connection.disconnectSync(); try { await connection.prepare('select 2'); diff --git a/bindings/pkgs/@duckdb/node-bindings/duckdb.d.ts b/bindings/pkgs/@duckdb/node-bindings/duckdb.d.ts index c591031..eb3182a 100644 --- a/bindings/pkgs/@duckdb/node-bindings/duckdb.d.ts +++ b/bindings/pkgs/@duckdb/node-bindings/duckdb.d.ts @@ -455,7 +455,7 @@ export function decimal_to_double(decimal: Decimal): number; export function prepare(connection: Connection, query: string): Promise; // DUCKDB_API void duckdb_destroy_prepare(duckdb_prepared_statement *prepared_statement); -// not exposed: destroyed in finalizer +export function destroy_prepare_sync(prepared_statement: PreparedStatement): void; // DUCKDB_API const char *duckdb_prepare_error(duckdb_prepared_statement prepared_statement); // not exposed: prepare rejects promise with error diff --git a/bindings/src/duckdb_node_bindings.cpp b/bindings/src/duckdb_node_bindings.cpp index 0bd39bb..9f8199e 100644 --- a/bindings/src/duckdb_node_bindings.cpp +++ b/bindings/src/duckdb_node_bindings.cpp @@ -544,19 +544,32 @@ static const napi_type_tag PreparedStatementTypeTag = { 0xA8B03DAD16D34416, 0x9735A7E1F2A1240C }; -void FinalizePreparedStatement(Napi::BasicEnv, duckdb_prepared_statement prepared_statement) { - if (prepared_statement) { - duckdb_destroy_prepare(&prepared_statement); - prepared_statement = nullptr; - } +typedef struct { + duckdb_prepared_statement prepared; +} duckdb_prepared_statement_holder; + +duckdb_prepared_statement_holder *CreatePreparedStatementHolder(duckdb_prepared_statement prepared) { + auto prepared_statement_holder_ptr = reinterpret_cast(duckdb_malloc(sizeof(duckdb_prepared_statement_holder))); + prepared_statement_holder_ptr->prepared = prepared; + return prepared_statement_holder_ptr; } -Napi::External<_duckdb_prepared_statement> CreateExternalForPreparedStatement(Napi::Env env, duckdb_prepared_statement prepared_statement) { - return CreateExternal<_duckdb_prepared_statement>(env, PreparedStatementTypeTag, prepared_statement, FinalizePreparedStatement); +void FinalizePreparedStatementHolder(Napi::BasicEnv, duckdb_prepared_statement_holder *prepared_statement_holder_ptr) { + // duckdb_destroy_prepare is a no-op if already destroyed + duckdb_destroy_prepare(&prepared_statement_holder_ptr->prepared); + duckdb_free(prepared_statement_holder_ptr); +} + +Napi::External CreateExternalForPreparedStatement(Napi::Env env, duckdb_prepared_statement prepared_statement) { + return CreateExternal(env, PreparedStatementTypeTag, CreatePreparedStatementHolder(prepared_statement), FinalizePreparedStatementHolder); +} + +duckdb_prepared_statement_holder *GetPreparedStatementHolderFromExternal(Napi::Env env, Napi::Value value) { + return GetDataFromExternal(env, PreparedStatementTypeTag, value, "Invalid prepared statement argument"); } duckdb_prepared_statement GetPreparedStatementFromExternal(Napi::Env env, Napi::Value value) { - return GetDataFromExternal<_duckdb_prepared_statement>(env, PreparedStatementTypeTag, value, "Invalid prepared statement argument"); + return GetPreparedStatementHolderFromExternal(env, value)->prepared; } static const napi_type_tag ResultTypeTag = { @@ -1232,6 +1245,7 @@ class DuckDBNodeAddon : public Napi::Addon { InstanceMethod("decimal_to_double", &DuckDBNodeAddon::decimal_to_double), InstanceMethod("prepare", &DuckDBNodeAddon::prepare), + InstanceMethod("destroy_prepare_sync", &DuckDBNodeAddon::destroy_prepare_sync), InstanceMethod("nparams", &DuckDBNodeAddon::nparams), InstanceMethod("parameter_name", &DuckDBNodeAddon::parameter_name), InstanceMethod("param_type", &DuckDBNodeAddon::param_type), @@ -1996,7 +2010,14 @@ class DuckDBNodeAddon : public Napi::Addon { } // DUCKDB_API void duckdb_destroy_prepare(duckdb_prepared_statement *prepared_statement); - // not exposed: destroyed in finalizer + // function destroy_prepare_sync(prepared_statement: PreparedStatement): void + Napi::Value destroy_prepare_sync(const Napi::CallbackInfo& info) { + auto env = info.Env(); + auto prepared_statement_holder_ptr = GetPreparedStatementHolderFromExternal(env, info[0]); + // duckdb_destroy_prepare is a no-op if already destroyed + duckdb_destroy_prepare(&prepared_statement_holder_ptr->prepared); + return env.Undefined(); + } // DUCKDB_API const char *duckdb_prepare_error(duckdb_prepared_statement prepared_statement); // not exposed: prepare rejects promise with error @@ -4391,7 +4412,7 @@ NODE_API_ADDON(DuckDBNodeAddon) --- 411 total functions - 242 instance methods + 243 instance methods 1 unimplemented logical type function 13 unimplemented scalar function functions 4 unimplemented scalar function set functions @@ -4407,7 +4428,7 @@ NODE_API_ADDON(DuckDBNodeAddon) 6 unimplemented table description functions 8 unimplemented tasks functions 12 unimplemented cast function functions - 24 functions not exposed + 23 functions not exposed + 41 unimplemented deprecated functions (of 47) --- 411 functions accounted for diff --git a/bindings/test/prepared_statements.test.ts b/bindings/test/prepared_statements.test.ts index e1540a3..fb77cf0 100644 --- a/bindings/test/prepared_statements.test.ts +++ b/bindings/test/prepared_statements.test.ts @@ -448,4 +448,10 @@ suite('prepared statements', () => { } }); }); + test('destroy_prepare_sync', async () => { + await withConnection(async (connection) => { + const prepared = await duckdb.prepare(connection, 'select 1'); + duckdb.destroy_prepare_sync(prepared); + }); + }); });