diff --git a/bindings/src/duckdb_node_bindings.cpp b/bindings/src/duckdb_node_bindings.cpp index c198b025..717c357c 100644 --- a/bindings/src/duckdb_node_bindings.cpp +++ b/bindings/src/duckdb_node_bindings.cpp @@ -206,6 +206,18 @@ T* GetDataFromExternal(Napi::Env env, const napi_type_tag &type_tag, Napi::Value // The following type tags are generated using: uuidgen | sed -r -e 's/-//g' -e 's/(.{16})(.*)/0x\1, 0x\2/' +static const napi_type_tag AppenderTypeTag = { + 0x32E0AB3B83F74A89, 0xB785905D92D54996 +}; + +Napi::External<_duckdb_appender> CreateExternalForAppender(Napi::Env env, duckdb_appender appender) { + return CreateExternal<_duckdb_appender>(env, AppenderTypeTag, appender); +} + +duckdb_appender GetAppenderFromExternal(Napi::Env env, Napi::Value value) { + return GetDataFromExternal<_duckdb_appender>(env, AppenderTypeTag, value, "Invalid appender argument"); +} + static const napi_type_tag ConfigTypeTag = { 0x5963FBB9648B4D2A, 0xB41ADE86056218D1 }; @@ -1886,7 +1898,7 @@ class DuckDBNodeAddon : public Napi::Addon { auto prepared_statement = GetPreparedStatementFromExternal(env, info[0]); duckdb_pending_result pending_result; if (duckdb_pending_prepared(prepared_statement, &pending_result)) { - auto error = duckdb_pending_error(pending_result); + std::string error = duckdb_pending_error(pending_result); duckdb_destroy_pending(&pending_result); throw Napi::Error::New(env, error); } @@ -2616,45 +2628,71 @@ class DuckDBNodeAddon : public Napi::Addon { // function appender_create(connection: Connection, schema: string, table: string): Appender Napi::Value appender_create(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto connection = GetConnectionFromExternal(env, info[0]); + std::string schema = info[1].As(); + std::string table = info[2].As(); + duckdb_appender appender; + if (duckdb_appender_create(connection, schema.c_str(), table.c_str(), &appender)) { + std::string error = duckdb_appender_error(appender); + duckdb_appender_destroy(&appender); + throw Napi::Error::New(env, error); + } + return CreateExternalForAppender(env, appender); } // DUCKDB_API idx_t duckdb_appender_column_count(duckdb_appender appender); // function appender_column_count(appender: Appender): number Napi::Value appender_column_count(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto appender = GetAppenderFromExternal(env, info[0]); + auto column_count = duckdb_appender_column_count(appender); + return Napi::Number::New(env, column_count); } // DUCKDB_API duckdb_logical_type duckdb_appender_column_type(duckdb_appender appender, idx_t col_idx); // function appender_column_type(appender: Appender, column_index: number): LogicalType Napi::Value appender_column_type(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto appender = GetAppenderFromExternal(env, info[0]); + auto column_index = info[1].As().Uint32Value(); + auto logical_type = duckdb_appender_column_type(appender, column_index); + return CreateExternalForLogicalType(env, logical_type); } // DUCKDB_API const char *duckdb_appender_error(duckdb_appender appender); -// not exposed: other appender functions throw + // not exposed: other appender functions throw // DUCKDB_API duckdb_state duckdb_appender_flush(duckdb_appender appender); // function appender_flush(appender: Appender): void Napi::Value appender_flush(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto appender = GetAppenderFromExternal(env, info[0]); + if (duckdb_appender_flush(appender)) { + throw Napi::Error::New(env, duckdb_appender_error(appender)); + } + return env.Undefined(); } // DUCKDB_API duckdb_state duckdb_appender_close(duckdb_appender appender); // function appender_close(appender: Appender): void Napi::Value appender_close(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto appender = GetAppenderFromExternal(env, info[0]); + if (duckdb_appender_close(appender)) { + throw Napi::Error::New(env, duckdb_appender_error(appender)); + } + return env.Undefined(); } // DUCKDB_API duckdb_state duckdb_appender_destroy(duckdb_appender *appender); // function appender_destroy(appender: Appender): void Napi::Value appender_destroy(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto appender = GetAppenderFromExternal(env, info[0]); + if (duckdb_appender_destroy(&appender)) { + throw Napi::Error::New(env, "Failed to destroy appender"); + } + return env.Undefined(); } // DUCKDB_API duckdb_state duckdb_appender_begin_row(duckdb_appender appender); @@ -2664,133 +2702,237 @@ class DuckDBNodeAddon : public Napi::Addon { // function appender_end_row(appender: Appender): void Napi::Value appender_end_row(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto appender = GetAppenderFromExternal(env, info[0]); + if (duckdb_appender_end_row(appender)) { + throw Napi::Error::New(env, duckdb_appender_error(appender)); + } + return env.Undefined(); } // DUCKDB_API duckdb_state duckdb_append_bool(duckdb_appender appender, bool value); // function append_bool(appender: Appender, bool: boolean): void Napi::Value append_bool(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto appender = GetAppenderFromExternal(env, info[0]); + auto bool_value = info[1].As(); + if (duckdb_append_bool(appender, bool_value)) { + throw Napi::Error::New(env, duckdb_appender_error(appender)); + } + return env.Undefined(); } // DUCKDB_API duckdb_state duckdb_append_int8(duckdb_appender appender, int8_t value); // function append_int8(appender: Appender, int8: number): void Napi::Value append_int8(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto appender = GetAppenderFromExternal(env, info[0]); + auto int8_value = info[1].As().Int32Value(); + if (duckdb_append_int8(appender, int8_value)) { + throw Napi::Error::New(env, duckdb_appender_error(appender)); + } + return env.Undefined(); } // DUCKDB_API duckdb_state duckdb_append_int16(duckdb_appender appender, int16_t value); // function append_int16(appender: Appender, int16: number): void Napi::Value append_int16(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto appender = GetAppenderFromExternal(env, info[0]); + auto int16_value = info[1].As().Int32Value(); + if (duckdb_append_int16(appender, int16_value)) { + throw Napi::Error::New(env, duckdb_appender_error(appender)); + } + return env.Undefined(); } // DUCKDB_API duckdb_state duckdb_append_int32(duckdb_appender appender, int32_t value); // function append_int32(appender: Appender, int32: number): void Napi::Value append_int32(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto appender = GetAppenderFromExternal(env, info[0]); + auto int32_value = info[1].As().Int32Value(); + if (duckdb_append_int32(appender, int32_value)) { + throw Napi::Error::New(env, duckdb_appender_error(appender)); + } + return env.Undefined(); } // DUCKDB_API duckdb_state duckdb_append_int64(duckdb_appender appender, int64_t value); // function append_int64(appender: Appender, int64: bigint): void Napi::Value append_int64(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto appender = GetAppenderFromExternal(env, info[0]); + bool lossless; + auto int64_value = info[1].As().Int64Value(&lossless); + if (!lossless) { + throw Napi::Error::New(env, "bigint out of int64 range"); + } + if (duckdb_append_int64(appender, int64_value)) { + throw Napi::Error::New(env, duckdb_appender_error(appender)); + } + return env.Undefined(); } // DUCKDB_API duckdb_state duckdb_append_hugeint(duckdb_appender appender, duckdb_hugeint value); // function append_hugeint(appender: Appender, hugeint: bigint): void Napi::Value append_hugeint(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto appender = GetAppenderFromExternal(env, info[0]); + auto bigint = info[1].As(); + auto hugeint_value = GetHugeIntFromBigInt(env, bigint); + if (duckdb_append_hugeint(appender, hugeint_value)) { + throw Napi::Error::New(env, duckdb_appender_error(appender)); + } + return env.Undefined(); } // DUCKDB_API duckdb_state duckdb_append_uint8(duckdb_appender appender, uint8_t value); // function append_uint8(appender: Appender, uint8: number): void Napi::Value append_uint8(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto appender = GetAppenderFromExternal(env, info[0]); + auto uint8_value = info[1].As().Uint32Value(); + if (duckdb_append_uint8(appender, uint8_value)) { + throw Napi::Error::New(env, duckdb_appender_error(appender)); + } + return env.Undefined(); } // DUCKDB_API duckdb_state duckdb_append_uint16(duckdb_appender appender, uint16_t value); // function append_uint16(appender: Appender, uint16: number): void Napi::Value append_uint16(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto appender = GetAppenderFromExternal(env, info[0]); + auto uint16_value = info[1].As().Uint32Value(); + if (duckdb_append_uint16(appender, uint16_value)) { + throw Napi::Error::New(env, duckdb_appender_error(appender)); + } + return env.Undefined(); } // DUCKDB_API duckdb_state duckdb_append_uint32(duckdb_appender appender, uint32_t value); // function append_uint32(appender: Appender, uint32: number): void Napi::Value append_uint32(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto appender = GetAppenderFromExternal(env, info[0]); + auto uint32_value = info[1].As().Uint32Value(); + if (duckdb_append_uint32(appender, uint32_value)) { + throw Napi::Error::New(env, duckdb_appender_error(appender)); + } + return env.Undefined(); } // DUCKDB_API duckdb_state duckdb_append_uint64(duckdb_appender appender, uint64_t value); // function append_uint64(appender: Appender, uint64: bigint): void Napi::Value append_uint64(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto appender = GetAppenderFromExternal(env, info[0]); + bool lossless; + auto uint64_value = info[1].As().Uint64Value(&lossless); + if (!lossless) { + throw Napi::Error::New(env, "bigint out of uint64 range"); + } + if (duckdb_append_uint64(appender, uint64_value)) { + throw Napi::Error::New(env, duckdb_appender_error(appender)); + } + return env.Undefined(); } // DUCKDB_API duckdb_state duckdb_append_uhugeint(duckdb_appender appender, duckdb_uhugeint value); // function append_uhugeint(appender: Appender, uhugeint: bigint): void Napi::Value append_uhugeint(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto appender = GetAppenderFromExternal(env, info[0]); + auto bigint = info[1].As(); + auto uhugeint_value = GetUHugeIntFromBigInt(env, bigint); + if (duckdb_append_uhugeint(appender, uhugeint_value)) { + throw Napi::Error::New(env, duckdb_appender_error(appender)); + } + return env.Undefined(); } // DUCKDB_API duckdb_state duckdb_append_float(duckdb_appender appender, float value); // function append_float(appender: Appender, float: number): void Napi::Value append_float(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto appender = GetAppenderFromExternal(env, info[0]); + auto float_value = info[1].As().FloatValue(); + if (duckdb_append_float(appender, float_value)) { + throw Napi::Error::New(env, duckdb_appender_error(appender)); + } + return env.Undefined(); } // DUCKDB_API duckdb_state duckdb_append_double(duckdb_appender appender, double value); // function append_double(appender: Appender, double: number): void Napi::Value append_double(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto appender = GetAppenderFromExternal(env, info[0]); + auto double_value = info[1].As().DoubleValue(); + if (duckdb_append_double(appender, double_value)) { + throw Napi::Error::New(env, duckdb_appender_error(appender)); + } + return env.Undefined(); } // DUCKDB_API duckdb_state duckdb_append_date(duckdb_appender appender, duckdb_date value); // function append_date(appender: Appender, date: Date_): void Napi::Value append_date(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto appender = GetAppenderFromExternal(env, info[0]); + auto date_value = GetDateFromObject(info[1].As()); + if (duckdb_append_date(appender, date_value)) { + throw Napi::Error::New(env, duckdb_appender_error(appender)); + } + return env.Undefined(); } // DUCKDB_API duckdb_state duckdb_append_time(duckdb_appender appender, duckdb_time value); // function append_time(appender: Appender, time: Time): void Napi::Value append_time(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto appender = GetAppenderFromExternal(env, info[0]); + auto time_value = GetTimeFromObject(info[1].As()); + if (duckdb_append_time(appender, time_value)) { + throw Napi::Error::New(env, duckdb_appender_error(appender)); + } + return env.Undefined(); } // DUCKDB_API duckdb_state duckdb_append_timestamp(duckdb_appender appender, duckdb_timestamp value); // function append_timestamp(appender: Appender, timestamp: Timestamp): void Napi::Value append_timestamp(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto appender = GetAppenderFromExternal(env, info[0]); + auto timestamp_value = GetTimestampFromObject(env, info[1].As()); + if (duckdb_append_timestamp(appender, timestamp_value)) { + throw Napi::Error::New(env, duckdb_appender_error(appender)); + } + return env.Undefined(); } // DUCKDB_API duckdb_state duckdb_append_interval(duckdb_appender appender, duckdb_interval value); // function append_interval(appender: Appender, interval: Interval): void Napi::Value append_interval(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto appender = GetAppenderFromExternal(env, info[0]); + auto interval_value = GetIntervalFromObject(env, info[1].As()); + if (duckdb_append_interval(appender, interval_value)) { + throw Napi::Error::New(env, duckdb_appender_error(appender)); + } + return env.Undefined(); } // DUCKDB_API duckdb_state duckdb_append_varchar(duckdb_appender appender, const char *val); // function append_varchar(appender: Appender, varchar: string): void Napi::Value append_varchar(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto appender = GetAppenderFromExternal(env, info[0]); + std::string str = info[1].As(); + if (duckdb_append_varchar(appender, str.c_str())) { + throw Napi::Error::New(env, duckdb_appender_error(appender)); + } + return env.Undefined(); } // DUCKDB_API duckdb_state duckdb_append_varchar_length(duckdb_appender appender, const char *val, idx_t length); @@ -2800,21 +2942,37 @@ class DuckDBNodeAddon : public Napi::Addon { // function append_blob(appender: Appender, data: Uint8Array): void Napi::Value append_blob(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto appender = GetAppenderFromExternal(env, info[0]); + auto array = info[1].As(); + auto data = reinterpret_cast(array.Data()); + auto length = array.ByteLength(); + if (duckdb_append_blob(appender, data, length)) { + throw Napi::Error::New(env, duckdb_appender_error(appender)); + } + return env.Undefined(); } // DUCKDB_API duckdb_state duckdb_append_null(duckdb_appender appender); // function append_null(appender: Appender): void Napi::Value append_null(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto appender = GetAppenderFromExternal(env, info[0]); + if (duckdb_append_null(appender)) { + throw Napi::Error::New(env, duckdb_appender_error(appender)); + } + return env.Undefined(); } // DUCKDB_API duckdb_state duckdb_append_data_chunk(duckdb_appender appender, duckdb_data_chunk chunk); // function append_data_chunk(appender: Appender, chunk: DataChunk): void Napi::Value append_data_chunk(const Napi::CallbackInfo& info) { auto env = info.Env(); - throw Napi::Error::New(env, "Not implemented yet"); + auto appender = GetAppenderFromExternal(env, info[0]); + auto chunk = GetDataChunkFromExternal(env, info[1]); + if (duckdb_append_data_chunk(appender, chunk)) { + throw Napi::Error::New(env, duckdb_appender_error(appender)); + } + return env.Undefined(); } // #ifndef DUCKDB_API_NO_DEPRECATED diff --git a/bindings/test/appender.test.ts b/bindings/test/appender.test.ts new file mode 100644 index 00000000..c1087476 --- /dev/null +++ b/bindings/test/appender.test.ts @@ -0,0 +1,290 @@ +import duckdb from '@duckdb/node-bindings'; +import { expect, suite, test } from 'vitest'; +import { withConnection } from './utils/withConnection'; +import { expectResult } from './utils/expectResult'; +import { expectLogicalType } from './utils/expectLogicalType'; +import { BIGINT, BLOB, BOOLEAN, DATE, DOUBLE, FLOAT, HUGEINT, INTEGER, INTERVAL, SMALLINT, TIME, TIMESTAMP, TINYINT, UBIGINT, UHUGEINT, UINTEGER, USMALLINT, UTINYINT, VARCHAR } from './utils/expectedLogicalTypes'; +import { data } from './utils/expectedVectors'; + +suite('appender', () => { + test('error: no table', async () => { + await withConnection(async (connection) => { + expect(() => duckdb.appender_create(connection, 'main', 'bogus_table')) + .toThrowError(`Table "main.bogus_table" could not be found`); + }); + }); + test('one column', async () => { + await withConnection(async (connection) => { + const createResult = await duckdb.query(connection, 'create table appender_target(i integer)'); + try { + await expectResult(createResult, { + statementType: duckdb.StatementType.CREATE, + resultType: duckdb.ResultType.NOTHING, + columns: [ + { name: 'Count', logicalType: BIGINT }, + ], + chunks: [ + { columnCount: 0, rowCount: 0, vectors: [] }, + ], + }); + } finally { + duckdb.destroy_result(createResult); + } + + const appender = duckdb.appender_create(connection, 'main', 'appender_target'); + try { + expect(duckdb.appender_column_count(appender)).toBe(1); + const column_type = duckdb.appender_column_type(appender, 0); + try { + expectLogicalType(column_type, INTEGER); + } finally { + duckdb.destroy_logical_type(column_type); + } + duckdb.append_int32(appender, 11); + duckdb.appender_end_row(appender); + duckdb.append_int32(appender, 22); + duckdb.appender_end_row(appender); + duckdb.append_int32(appender, 33); + duckdb.appender_end_row(appender); + } finally { + duckdb.appender_destroy(appender); + } + + const result = await duckdb.query(connection, 'from appender_target'); + try { + await expectResult(result, { + columns: [ + { name: 'i', logicalType: INTEGER }, + ], + chunks: [ + { rowCount: 3, vectors: [data(4, [true, true, true], [11, 22, 33])] }, + ], + }); + } finally { + duckdb.destroy_result(result); + } + }); + }); + test('multiple columns', async () => { + await withConnection(async (connection) => { + const createResult = await duckdb.query(connection, + 'create table appender_target(\ + bool boolean, \ + int8 tinyint, \ + int16 smallint, \ + int32 integer, \ + int64 bigint, \ + hugeint hugeint, \ + uint8 utinyint, \ + uint16 usmallint, \ + uint32 uinteger, \ + uint64 ubigint, \ + uhugeint uhugeint, \ + float float, \ + double double, \ + date date, \ + time time, \ + timestamp timestamp, \ + interval interval, \ + varchar varchar, \ + blob blob, \ + null_column integer, \ + )'); + try { + await expectResult(createResult, { + statementType: duckdb.StatementType.CREATE, + resultType: duckdb.ResultType.NOTHING, + columns: [ + { name: 'Count', logicalType: BIGINT }, + ], + chunks: [ + { columnCount: 0, rowCount: 0, vectors: [] }, + ], + }); + } finally { + duckdb.destroy_result(createResult); + } + + const appender = duckdb.appender_create(connection, 'main', 'appender_target'); + try { + expect(duckdb.appender_column_count(appender)).toBe(20); + + const expectedLogicalTypes = [ + BOOLEAN, + TINYINT, + SMALLINT, + INTEGER, + BIGINT, + HUGEINT, + UTINYINT, + USMALLINT, + UINTEGER, + UBIGINT, + UHUGEINT, + FLOAT, + DOUBLE, + DATE, + TIME, + TIMESTAMP, + INTERVAL, + VARCHAR, + BLOB, + INTEGER, + ]; + for (let i = 0; i < expectLogicalType.length; i++) { + const column_type = duckdb.appender_column_type(appender, i); + try { + expectLogicalType(column_type, expectedLogicalTypes[i]); + } finally { + duckdb.destroy_logical_type(column_type); + } + } + + duckdb.append_bool(appender, true); + duckdb.append_int8(appender, 127); + duckdb.append_int16(appender, 32767); + duckdb.append_int32(appender, 2147483647); + duckdb.append_int64(appender, 9223372036854775807n); + duckdb.append_hugeint(appender, 170141183460469231731687303715884105727n); + duckdb.append_uint8(appender, 255); + duckdb.append_uint16(appender, 65535); + duckdb.append_uint32(appender, 4294967295); + duckdb.append_uint64(appender, 18446744073709551615n); + duckdb.append_uhugeint(appender, 340282366920938463463374607431768211455n); + duckdb.append_float(appender, 3.4028234663852886e+38); + duckdb.append_double(appender, 1.7976931348623157e+308); + duckdb.append_date(appender, { days: 2147483646 }); + duckdb.append_time(appender, { micros: 86400000000 }); + duckdb.append_timestamp(appender, { micros: 9223372036854775806n }); + duckdb.append_interval(appender, { months: 999, days: 999, micros: 999999999n }); + duckdb.append_varchar(appender, '🦆🦆🦆🦆🦆🦆'); + duckdb.append_blob(appender, Buffer.from('thisisalongblob\x00withnullbytes')); + duckdb.append_null(appender); + + duckdb.appender_end_row(appender); + // explicitly calling flush and close is unnecessary because destroy does both, but this exercises them. + duckdb.appender_flush(appender); + duckdb.appender_close(appender); + } finally { + duckdb.appender_destroy(appender); + } + + const result = await duckdb.query(connection, 'from appender_target'); + try { + await expectResult(result, { + columns: [ + { name: 'bool', logicalType: BOOLEAN }, + { name: 'int8', logicalType: TINYINT }, + { name: 'int16', logicalType: SMALLINT }, + { name: 'int32', logicalType: INTEGER }, + { name: 'int64', logicalType: BIGINT }, + { name: 'hugeint', logicalType: HUGEINT }, + { name: 'uint8', logicalType: UTINYINT }, + { name: 'uint16', logicalType: USMALLINT }, + { name: 'uint32', logicalType: UINTEGER }, + { name: 'uint64', logicalType: UBIGINT }, + { name: 'uhugeint', logicalType: UHUGEINT }, + { name: 'float', logicalType: FLOAT }, + { name: 'double', logicalType: DOUBLE }, + { name: 'date', logicalType: DATE }, + { name: 'time', logicalType: TIME }, + { name: 'timestamp', logicalType: TIMESTAMP }, + { name: 'interval', logicalType: INTERVAL }, + { name: 'varchar', logicalType: VARCHAR }, + { name: 'blob', logicalType: BLOB }, + { name: 'null_column', logicalType: INTEGER }, + ], + chunks: [ + { + rowCount: 1, + vectors: [ + data(1, [true], [true]), + data(1, [true], [127]), + data(2, [true], [32767]), + data(4, [true], [2147483647]), + data(8, [true], [9223372036854775807n]), + data(16, [true], [170141183460469231731687303715884105727n]), + data(1, [true], [255]), + data(2, [true], [65535]), + data(4, [true], [4294967295]), + data(8, [true], [18446744073709551615n]), + data(16, [true], [340282366920938463463374607431768211455n]), + data(4, [true], [3.4028234663852886e+38]), + data(8, [true], [1.7976931348623157e+308]), + data(4, [true], [2147483646]), + data(8, [true], [86400000000n]), + data(8, [true], [9223372036854775806n]), + data(16, [true], [{ months: 999, days: 999, micros: 999999999n }]), + data(16, [true], ['🦆🦆🦆🦆🦆🦆']), + data(16, [true], [Buffer.from('thisisalongblob\x00withnullbytes')]), + data(4, [false], [null]), + ], + }, + ], + }); + } finally { + duckdb.destroy_result(result); + } + }); + }); + test('data chunk', async () => { + await withConnection(async (connection) => { + const createResult = await duckdb.query(connection, 'create table appender_target(i integer, v varchar)'); + try { + await expectResult(createResult, { + statementType: duckdb.StatementType.CREATE, + resultType: duckdb.ResultType.NOTHING, + columns: [ + { name: 'Count', logicalType: BIGINT }, + ], + chunks: [ + { columnCount: 0, rowCount: 0, vectors: [] }, + ], + }); + } finally { + duckdb.destroy_result(createResult); + } + + const appender = duckdb.appender_create(connection, 'main', 'appender_target'); + try { + expect(duckdb.appender_column_count(appender)).toBe(2); + + const source_result = await duckdb.query(connection, 'select int, varchar from test_all_types()'); + try { + const source_chunk = await duckdb.fetch_chunk(source_result); + try { + duckdb.append_data_chunk(appender, source_chunk); + } finally { + duckdb.destroy_data_chunk(source_chunk); + } + } finally { + duckdb.destroy_result(source_result); + } + + } finally { + duckdb.appender_destroy(appender); + } + + const result = await duckdb.query(connection, 'from appender_target'); + try { + await expectResult(result, { + columns: [ + { name: 'i', logicalType: INTEGER }, + { name: 'v', logicalType: VARCHAR }, + ], + chunks: [ + { + rowCount: 3, + vectors: [ + data(4, [true, true, false], [-2147483648, 2147483647, null]), + data(16, [true, true, false], ['🦆🦆🦆🦆🦆🦆', 'goo\0se', null]), + ], + }, + ], + }); + } finally { + duckdb.destroy_result(result); + } + }); + }); +}); diff --git a/bindings/test/data_chunk.test.ts b/bindings/test/data_chunk.test.ts index 6e6db7b7..f1b63466 100644 --- a/bindings/test/data_chunk.test.ts +++ b/bindings/test/data_chunk.test.ts @@ -48,7 +48,6 @@ suite('data chunk', () => { 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); diff --git a/bindings/test/query.test.ts b/bindings/test/query.test.ts index f95be769..708982c7 100644 --- a/bindings/test/query.test.ts +++ b/bindings/test/query.test.ts @@ -249,7 +249,7 @@ suite('query', () => { statementType: duckdb.StatementType.CREATE, resultType: duckdb.ResultType.NOTHING, columns: [ - { name: 'Count', logicalType: { typeId: duckdb.Type.BIGINT } }, + { name: 'Count', logicalType: BIGINT }, ], chunks: [ { columnCount: 0, rowCount: 0, vectors: [] }, @@ -265,7 +265,7 @@ suite('query', () => { resultType: duckdb.ResultType.CHANGED_ROWS, rowsChanged: 17, columns: [ - { name: 'Count', logicalType: { typeId: duckdb.Type.BIGINT } }, + { name: 'Count', logicalType: BIGINT }, ], chunks: [ { rowCount: 1, vectors: [data(8, [true], [17n])] },