diff --git a/bindings/src/duckdb_node_bindings.cpp b/bindings/src/duckdb_node_bindings.cpp index 768d666..c198b02 100644 --- a/bindings/src/duckdb_node_bindings.cpp +++ b/bindings/src/duckdb_node_bindings.cpp @@ -2357,7 +2357,14 @@ class DuckDBNodeAddon : public Napi::Addon { // function create_data_chunk(logical_types: readonly LogicalType[]): DataChunk Napi::Value create_data_chunk(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto types_array = info[0].As(); + auto types_count = types_array.Length(); + std::vector types(types_count); + for (uint32_t i = 0; i < types_count; i++) { + types[i] = GetLogicalTypeFromExternal(env, types_array.Get(i)); + } + auto data_chunk = duckdb_create_data_chunk(types.data(), types_count); + return CreateExternalForDataChunk(env, data_chunk); } // DUCKDB_API void duckdb_destroy_data_chunk(duckdb_data_chunk *chunk); @@ -2373,7 +2380,9 @@ class DuckDBNodeAddon : public Napi::Addon { // function data_chunk_reset(chunk: DataChunk): void Napi::Value data_chunk_reset(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto chunk = GetDataChunkFromExternal(env, info[0]); + duckdb_data_chunk_reset(chunk); + return env.Undefined(); } // DUCKDB_API idx_t duckdb_data_chunk_get_column_count(duckdb_data_chunk chunk); @@ -2408,7 +2417,10 @@ class DuckDBNodeAddon : public Napi::Addon { // function data_chunk_set_size(chunk: DataChunk, size: number): void Napi::Value data_chunk_set_size(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto chunk = GetDataChunkFromExternal(env, info[0]); + auto size = info[1].As().Uint32Value(); + duckdb_data_chunk_set_size(chunk, size); + return env.Undefined(); } // DUCKDB_API duckdb_logical_type duckdb_vector_get_column_type(duckdb_vector vector); @@ -2444,14 +2456,20 @@ class DuckDBNodeAddon : public Napi::Addon { // function vector_ensure_validity_writable(vector: Vector): void Napi::Value vector_ensure_validity_writable(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto vector = GetVectorFromExternal(env, info[0]); + duckdb_vector_ensure_validity_writable(vector); + return env.Undefined(); } // DUCKDB_API void duckdb_vector_assign_string_element(duckdb_vector vector, idx_t index, const char *str); // function vector_assign_string_element(vector: Vector, index: number, str: string): void Napi::Value vector_assign_string_element(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto vector = GetVectorFromExternal(env, info[0]); + auto index = info[1].As().Uint32Value(); + std::string str = info[2].As(); + duckdb_vector_assign_string_element(vector, index, str.c_str()); + return env.Undefined(); } // DUCKDB_API void duckdb_vector_assign_string_element_len(duckdb_vector vector, idx_t index, const char *str, idx_t str_len); @@ -2479,14 +2497,20 @@ class DuckDBNodeAddon : public Napi::Addon { // function list_vector_set_size(vector: Vector, size: number): void Napi::Value list_vector_set_size(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto vector = GetVectorFromExternal(env, info[0]); + auto size = info[1].As().Uint32Value(); + duckdb_list_vector_set_size(vector, size); + return env.Undefined(); } // DUCKDB_API duckdb_state duckdb_list_vector_reserve(duckdb_vector vector, idx_t required_capacity); // function list_vector_reserve(vector: Vector, required_capacity: number): void Napi::Value list_vector_reserve(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto vector = GetVectorFromExternal(env, info[0]); + auto required_capacity = info[1].As().Uint32Value(); + duckdb_list_vector_reserve(vector, required_capacity); + return env.Undefined(); } // DUCKDB_API duckdb_vector duckdb_struct_vector_get_child(duckdb_vector vector, idx_t index); @@ -2522,21 +2546,31 @@ class DuckDBNodeAddon : public Napi::Addon { // function validity_set_row_validity(validity: Uint8Array, row_index: number, valid: boolean): void Napi::Value validity_set_row_validity(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto validity = reinterpret_cast(info[0].As().Data()); + auto row_index = info[1].As().Uint32Value(); + auto valid = info[2].As(); + duckdb_validity_set_row_validity(validity, row_index, valid); + return env.Undefined(); } // DUCKDB_API void duckdb_validity_set_row_invalid(uint64_t *validity, idx_t row); // function validity_set_row_invalid(validity: Uint8Array, row_index: number): void Napi::Value validity_set_row_invalid(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto validity = reinterpret_cast(info[0].As().Data()); + auto row_index = info[1].As().Uint32Value(); + duckdb_validity_set_row_invalid(validity, row_index); + return env.Undefined(); } // DUCKDB_API void duckdb_validity_set_row_valid(uint64_t *validity, idx_t row); // function validity_set_row_valid(validity: Uint8Array, row_index: number): void Napi::Value validity_set_row_valid(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto validity = reinterpret_cast(info[0].As().Data()); + auto row_index = info[1].As().Uint32Value(); + duckdb_validity_set_row_valid(validity, row_index); + return env.Undefined(); } // #ifndef DUCKDB_NO_EXTENSION_FUNCTIONS diff --git a/bindings/test/data_chunk.test.ts b/bindings/test/data_chunk.test.ts new file mode 100644 index 0000000..6e6db7b --- /dev/null +++ b/bindings/test/data_chunk.test.ts @@ -0,0 +1,114 @@ +import duckdb from '@duckdb/node-bindings'; +import { expect, suite, test } from 'vitest'; +import { expectLogicalType } from './utils/expectLogicalType'; +import { INTEGER, VARCHAR } from './utils/expectedLogicalTypes'; + +suite('data chunk', () => { + test('create', () => { + const int_type = duckdb.create_logical_type(duckdb.Type.INTEGER); + const varchar_type = duckdb.create_logical_type(duckdb.Type.VARCHAR); + try { + const chunk = duckdb.create_data_chunk([int_type, varchar_type]); + try { + expect(duckdb.data_chunk_get_column_count(chunk)).toBe(2); + const vec0 = duckdb.data_chunk_get_vector(chunk, 0); + expectLogicalType(duckdb.vector_get_column_type(vec0), INTEGER); + const vec1 = duckdb.data_chunk_get_vector(chunk, 1); + expectLogicalType(duckdb.vector_get_column_type(vec1), VARCHAR); + } finally { + duckdb.destroy_data_chunk(chunk); + } + } finally { + duckdb.destroy_logical_type(int_type); + duckdb.destroy_logical_type(varchar_type); + } + }); + test('change size', () => { + const int_type = duckdb.create_logical_type(duckdb.Type.INTEGER); + try { + const chunk = duckdb.create_data_chunk([int_type]); + try { + expect(duckdb.data_chunk_get_size(chunk)).toBe(0); + duckdb.data_chunk_set_size(chunk, 42); + expect(duckdb.data_chunk_get_size(chunk)).toBe(42); + duckdb.data_chunk_reset(chunk); + expect(duckdb.data_chunk_get_size(chunk)).toBe(0); + } finally { + duckdb.destroy_data_chunk(chunk); + } + } finally { + duckdb.destroy_logical_type(int_type); + } + }); + test('write vector validity', () => { + const int_type = duckdb.create_logical_type(duckdb.Type.INTEGER); + try { + const chunk = duckdb.create_data_chunk([int_type]); + try { + duckdb.data_chunk_set_size(chunk, 3); + const vector = duckdb.data_chunk_get_vector(chunk, 0); + duckdb.vector_ensure_validity_writable(vector); + // const data = duckdb.vector_get_data(vector, 3 * 4); + const validity = duckdb.vector_get_validity(vector, 8); + expect(validity[0]).toBe(0b11111111); + duckdb.validity_set_row_validity(validity, 1, false); + expect(validity[0]).toBe(0b11111101); + duckdb.validity_set_row_valid(validity, 1); + expect(validity[0]).toBe(0b11111111); + duckdb.validity_set_row_invalid(validity, 2); + expect(validity[0]).toBe(0b11111011); + } finally { + duckdb.destroy_data_chunk(chunk); + } + } finally { + duckdb.destroy_logical_type(int_type); + } + }); + test('write string vector', () => { + const varchar_type = duckdb.create_logical_type(duckdb.Type.VARCHAR); + try { + const chunk = duckdb.create_data_chunk([varchar_type]); + try { + duckdb.data_chunk_set_size(chunk, 3); + const vector = duckdb.data_chunk_get_vector(chunk, 0); + duckdb.vector_assign_string_element(vector, 0, 'ABC'); + duckdb.vector_assign_string_element(vector, 1, 'abcdefghijkl'); + duckdb.vector_assign_string_element(vector, 2, 'longer than twelve characters'); + const data = duckdb.vector_get_data(vector, 3 * 16); + const dv = new DataView(data.buffer); + expect(dv.getUint32(0, true)).toBe(3); + expect([data[4], data[5], data[6]]).toStrictEqual([0x41, 0x42, 0x43]); // A, B, C + expect(dv.getUint32(16, true)).toBe(12); + expect([data[20], data[31]]).toStrictEqual([0x61, 0x6c]); // a, l + expect(dv.getUint32(32, true)).toBe('longer than twelve characters'.length); + expect([data[36], data[37], data[38], data[39]]).toStrictEqual([0x6c, 0x6f, 0x6e, 0x67]); // l, o, n, g + } finally { + duckdb.destroy_data_chunk(chunk); + } + } finally { + duckdb.destroy_logical_type(varchar_type); + } + }); + test('set list vector size', () => { + const int_type = duckdb.create_logical_type(duckdb.Type.INTEGER); + try { + const list_type = duckdb.create_list_type(int_type); + try { + const chunk = duckdb.create_data_chunk([list_type]); + try { + duckdb.data_chunk_set_size(chunk, 3); + const vector = duckdb.data_chunk_get_vector(chunk, 0); + duckdb.list_vector_reserve(vector, 7); // can't easily verify that this worked + duckdb.list_vector_set_size(vector, 5); + expect(duckdb.list_vector_get_size(vector)).toBe(5); + } finally { + duckdb.destroy_data_chunk(chunk); + } + } finally { + duckdb.destroy_logical_type(list_type); + } + } finally { + duckdb.destroy_logical_type(int_type); + } + }); +}); diff --git a/bindings/test/pending.test.ts b/bindings/test/pending.test.ts index 04f46ae..ec575cd 100644 --- a/bindings/test/pending.test.ts +++ b/bindings/test/pending.test.ts @@ -66,7 +66,7 @@ suite('pending', () => { } }); }); - test('interrupt', async () => { + test.skip('interrupt', async () => { // interrupt does not appear to be entirely deterministic await withConnection(async (connection) => { const prepared = await duckdb.prepare(connection, 'select count(*) as count from range(10_000)'); try {