diff --git a/api/src/DuckDBPreparedStatement.ts b/api/src/DuckDBPreparedStatement.ts index 7eeed8f2..4372c92c 100644 --- a/api/src/DuckDBPreparedStatement.ts +++ b/api/src/DuckDBPreparedStatement.ts @@ -10,6 +10,9 @@ import { DuckDBListType, DuckDBStructType, DuckDBType, + TIMESTAMP_MS, + TIMESTAMP_NS, + TIMESTAMP_S, TIMESTAMPTZ, TIMETZ, VARINT, @@ -24,6 +27,9 @@ import { DuckDBIntervalValue, DuckDBListValue, DuckDBStructValue, + DuckDBTimestampMillisecondsValue, + DuckDBTimestampNanosecondsValue, + DuckDBTimestampSecondsValue, DuckDBTimestampTZValue, DuckDBTimestampValue, DuckDBTimeTZValue, @@ -52,10 +58,9 @@ export class DuckDBPreparedStatement { ) as number as DuckDBTypeId; } public parameterType(parameterIndex: number): DuckDBType { - return DuckDBLogicalType.create(duckdb.param_logical_type( - this.prepared_statement, - parameterIndex - )).asType(); + return DuckDBLogicalType.create( + duckdb.param_logical_type(this.prepared_statement, parameterIndex) + ).asType(); } public clearBindings() { duckdb.clear_bindings(this.prepared_statement); @@ -126,7 +131,24 @@ export class DuckDBPreparedStatement { ) { this.bindValue(parameterIndex, value, TIMESTAMPTZ); } - // TODO: bind TIMESTAMPS_S/_MS/_NS + public bindTimestampSeconds( + parameterIndex: number, + value: DuckDBTimestampSecondsValue + ) { + this.bindValue(parameterIndex, value, TIMESTAMP_S); + } + public bindTimestampMilliseconds( + parameterIndex: number, + value: DuckDBTimestampMillisecondsValue + ) { + this.bindValue(parameterIndex, value, TIMESTAMP_MS); + } + public bindTimestampNanoseconds( + parameterIndex: number, + value: DuckDBTimestampNanosecondsValue + ) { + this.bindValue(parameterIndex, value, TIMESTAMP_NS); + } public bindInterval(parameterIndex: number, value: DuckDBIntervalValue) { duckdb.bind_interval(this.prepared_statement, parameterIndex, value); } diff --git a/api/src/createValue.ts b/api/src/createValue.ts index 66b58e66..bd4f8880 100644 --- a/api/src/createValue.ts +++ b/api/src/createValue.ts @@ -9,6 +9,9 @@ import { DuckDBIntervalValue, DuckDBListValue, DuckDBStructValue, + DuckDBTimestampMillisecondsValue, + DuckDBTimestampNanosecondsValue, + DuckDBTimestampSecondsValue, DuckDBTimestampTZValue, DuckDBTimestampValue, DuckDBTimeTZValue, @@ -17,7 +20,8 @@ import { } from './values'; export function createValue(type: DuckDBType, input: DuckDBValue): Value { - switch (type.typeId) { + const { typeId } = type; + switch (typeId) { case DuckDBTypeId.BOOLEAN: if (typeof input === 'boolean') { return duckdb.create_bool(input); @@ -119,11 +123,20 @@ export function createValue(type: DuckDBType, input: DuckDBValue): Value { } throw new Error(`input is not a DuckDBDecimalValue`); case DuckDBTypeId.TIMESTAMP_S: - throw new Error(`not yet implemented for TIMESTAMP_S`); // TODO: implement when available in 1.2.0 + if (input instanceof DuckDBTimestampSecondsValue) { + return duckdb.create_timestamp_s(input); + } + throw new Error(`input is not a DuckDBTimestampSecondsValue`); case DuckDBTypeId.TIMESTAMP_MS: - throw new Error(`not yet implemented for TIMESTAMP_MS`); // TODO: implement when available in 1.2.0 + if (input instanceof DuckDBTimestampMillisecondsValue) { + return duckdb.create_timestamp_ms(input); + } + throw new Error(`input is not a DuckDBTimestampMillisecondsValue`); case DuckDBTypeId.TIMESTAMP_NS: - throw new Error(`not yet implemented for TIMESTAMP_NS`); // TODO: implement when available in 1.2.0 + if (input instanceof DuckDBTimestampNanosecondsValue) { + return duckdb.create_timestamp_ns(input); + } + throw new Error(`input is not a DuckDBTimestampNanosecondsValue`); case DuckDBTypeId.ENUM: throw new Error(`not yet implemented for ENUM`); // TODO: implement when available in 1.2.0 case DuckDBTypeId.LIST: @@ -141,7 +154,11 @@ export function createValue(type: DuckDBType, input: DuckDBValue): Value { throw new Error(`input is not a DuckDBListValue`); case DuckDBTypeId.STRUCT: if (input instanceof DuckDBStructValue) { - if (type.entryTypes.find((type) => type.typeId === DuckDBTypeId.ANY)) { + if ( + type.entryTypes.find( + (entryType) => entryType.typeId === DuckDBTypeId.ANY + ) + ) { throw new Error( 'Cannot create structs with an entry type of ANY. Specify a specific type.' ); @@ -173,7 +190,7 @@ export function createValue(type: DuckDBType, input: DuckDBValue): Value { throw new Error(`not yet implemented for UUID`); // TODO: implement when available in 1.2.0 case DuckDBTypeId.UNION: throw new Error(`not yet implemented for UNION`); // TODO: implement when available, hopefully in 1.2.0 - case DuckDBTypeId.UNION: + case DuckDBTypeId.BIT: throw new Error(`not yet implemented for BIT`); // TODO: implement when available in 1.2.0 case DuckDBTypeId.TIME_TZ: if (input instanceof DuckDBTimeTZValue) { @@ -182,7 +199,7 @@ export function createValue(type: DuckDBType, input: DuckDBValue): Value { throw new Error(`input is not a DuckDBTimeTZValue`); case DuckDBTypeId.TIMESTAMP_TZ: if (input instanceof DuckDBTimestampTZValue) { - return duckdb.create_timestamp(input); // TODO: change to create_timestamp_tz when available in 1.2.0 + return duckdb.create_timestamp_tz(input); } throw new Error(`input is not a DuckDBTimestampTZValue`); case DuckDBTypeId.ANY: @@ -197,6 +214,6 @@ export function createValue(type: DuckDBType, input: DuckDBValue): Value { case DuckDBTypeId.SQLNULL: throw new Error(`not yet implemented for SQLNUll`); // TODO: implement when available in 1.2.0 default: - throw new Error(`unrecognized type id ${type.typeId}`); + throw new Error(`unrecognized type id ${typeId}`); } } diff --git a/api/test/api.test.ts b/api/test/api.test.ts index 2012495e..88c93890 100644 --- a/api/test/api.test.ts +++ b/api/test/api.test.ts @@ -399,120 +399,128 @@ describe('api', () => { }); test('should support running prepared statements', async () => { await withConnection(async (connection) => { - const prepared = await connection.prepare( - 'select \ - $num as num, \ - $str as str, \ - $bool as bool, \ - $timetz as timetz, \ - $varint as varint, \ - $list as list, \ - $list_dec as list_dec, \ - $struct as struct, \ - $array as array, \ - $null as null_value' - ); - assert.strictEqual(prepared.parameterCount, 10); - assert.strictEqual(prepared.parameterName(1), 'num'); - assert.strictEqual(prepared.parameterName(2), 'str'); - assert.strictEqual(prepared.parameterName(3), 'bool'); - assert.strictEqual(prepared.parameterName(4), 'timetz'); - assert.strictEqual(prepared.parameterName(5), 'varint'); - assert.strictEqual(prepared.parameterName(6), 'list'); - assert.strictEqual(prepared.parameterName(7), 'list_dec'); - assert.strictEqual(prepared.parameterName(8), 'struct'); - assert.strictEqual(prepared.parameterName(9), 'array'); - assert.strictEqual(prepared.parameterName(10), 'null'); - prepared.bindInteger(1, 10); - prepared.bindVarchar(2, 'abc'); - prepared.bindBoolean(3, true); - prepared.bindTimeTZ(4, TIMETZ.max); - prepared.bindVarInt(5, VARINT.max); - prepared.bindList(6, listValue([100, 200, 300]), LIST(INTEGER)); + const params: ColumnNameAndType[] = [ + { name: 'num', type: INTEGER }, + { name: 'str', type: VARCHAR }, + { name: 'bool', type: BOOLEAN }, + { name: 'timetz', type: TIMETZ }, + { name: 'timestamptz', type: TIMESTAMPTZ }, + { name: 'timestamp_s', type: TIMESTAMP_S }, + { name: 'timestamp_ms', type: TIMESTAMP_MS }, + { name: 'timestamp_ns', type: TIMESTAMP_NS }, + { name: 'varint', type: VARINT }, + { name: 'list_int', type: LIST(INTEGER) }, + { name: 'list_dec', type: LIST(DECIMAL(4, 1)) }, + { name: 'struct', type: STRUCT({ 'a': INTEGER, 'b': VARCHAR }) }, + { name: 'array', type: ARRAY(INTEGER, 3) }, + { name: 'null_value', type: SQLNULL }, + ]; + + const sql = `select ${params + .map((p) => `$${p.name} as ${p.name}`) + .join(', ')}`; + const prepared = await connection.prepare(sql); + + assert.strictEqual(prepared.parameterCount, params.length); + for (let i = 0; i < params.length; i++) { + assert.strictEqual(prepared.parameterName(i + 1), params[i].name, `param ${i} name mismatch`); + } + + let i = 1; + prepared.bindInteger(i++, 10); + prepared.bindVarchar(i++, 'abc'); + prepared.bindBoolean(i++, true); + prepared.bindTimeTZ(i++, TIMETZ.max); + prepared.bindTimestampTZ(i++, TIMESTAMPTZ.max); + prepared.bindTimestampSeconds(i++, TIMESTAMP_S.max); + prepared.bindTimestampMilliseconds(i++, TIMESTAMP_MS.max); + prepared.bindTimestampNanoseconds(i++, TIMESTAMP_NS.max); + prepared.bindVarInt(i++, VARINT.max); + prepared.bindList(i++, listValue([100, 200, 300]), LIST(INTEGER)); prepared.bindList( - 7, + i++, listValue([decimalValue(9876n, 4, 1), decimalValue(5432n, 4, 1)]), LIST(DECIMAL(4, 1)) ); prepared.bindStruct( - 8, + i++, structValue({ 'a': 42, 'b': 'duck' }), STRUCT({ 'a': INTEGER, 'b': VARCHAR }) ); - prepared.bindArray(9, arrayValue([100, 200, 300]), ARRAY(INTEGER, 3)); - prepared.bindNull(10); - assert.equal(prepared.parameterTypeId(1), DuckDBTypeId.INTEGER); - assert.deepEqual(prepared.parameterType(1), INTEGER); - // See https://github.com/duckdb/duckdb/issues/16137 - // assert.equal(prepared.parameterTypeId(2), DuckDBTypeId.VARCHAR); - // assert.deepEqual(prepared.parameterType(2), VARCHAR); - assert.equal(prepared.parameterTypeId(3), DuckDBTypeId.BOOLEAN); - assert.deepEqual(prepared.parameterType(3), BOOLEAN); - assert.equal(prepared.parameterTypeId(4), DuckDBTypeId.TIME_TZ); - assert.deepEqual(prepared.parameterType(4), TIMETZ); - assert.equal(prepared.parameterTypeId(5), DuckDBTypeId.VARINT); - assert.deepEqual(prepared.parameterType(5), VARINT); - assert.equal(prepared.parameterTypeId(6), DuckDBTypeId.LIST); - assert.deepEqual(prepared.parameterType(6), LIST(INTEGER)); - assert.equal(prepared.parameterTypeId(7), DuckDBTypeId.LIST); - assert.deepEqual(prepared.parameterType(7), LIST(DECIMAL(4, 1))); - assert.equal(prepared.parameterTypeId(8), DuckDBTypeId.STRUCT); - assert.deepEqual(prepared.parameterType(8), STRUCT({ 'a': INTEGER, 'b': VARCHAR })); - assert.equal(prepared.parameterTypeId(9), DuckDBTypeId.ARRAY); - assert.deepEqual(prepared.parameterType(9), ARRAY(INTEGER, 3)); - assert.equal(prepared.parameterTypeId(10), DuckDBTypeId.SQLNULL); - assert.deepEqual(prepared.parameterType(10), SQLNULL); + prepared.bindArray(i++, arrayValue([100, 200, 300]), ARRAY(INTEGER, 3)); + prepared.bindNull(i++); + + for (let i = 0; i < params.length; i++) { + let type = params[i].type; + if (i === 1) { + // VARCHAR type is reported incorrectly; see https://github.com/duckdb/duckdb/issues/16137 + continue; + } + assert.equal(prepared.parameterTypeId(i + 1), type.typeId, `param ${i} type id mismatch`); + assert.deepEqual(prepared.parameterType(i + 1), type, `param ${i} type mismatch`); + } + const result = await prepared.run(); - assertColumns(result, [ - { name: 'num', type: INTEGER }, - { name: 'str', type: VARCHAR }, - { name: 'bool', type: BOOLEAN }, - { name: 'timetz', type: TIMETZ }, - { name: 'varint', type: VARINT }, - { name: 'list', type: LIST(INTEGER) }, - { name: 'list_dec', type: LIST(DECIMAL(4, 1)) }, - { name: 'struct', type: STRUCT({ 'a': INTEGER, 'b': VARCHAR }) }, - { name: 'array', type: ARRAY(INTEGER, 3) }, - { name: 'null_value', type: INTEGER }, - ]); + + // In the result, SQLNULL params get type INTEGER. + const expectedColumns = params.map((p) => + p.type.typeId === DuckDBTypeId.SQLNULL ? { ...p, type: INTEGER } : p + ); + + assertColumns(result, expectedColumns); + const chunk = await result.fetchChunk(); + assert.isDefined(chunk); if (chunk) { - assert.strictEqual(chunk.columnCount, 10); + assert.strictEqual(chunk.columnCount, expectedColumns.length); assert.strictEqual(chunk.rowCount, 1); + let i = 0; assertValues( chunk, - 0, + i++, DuckDBIntegerVector, [10] ); assertValues( chunk, - 1, + i++, DuckDBVarCharVector, ['abc'] ); assertValues( chunk, - 2, + i++, DuckDBBooleanVector, [true] ); - assertValues(chunk, 3, DuckDBTimeTZVector, [TIMETZ.max]); - assertValues(chunk, 4, DuckDBVarIntVector, [VARINT.max]); - assertValues(chunk, 5, DuckDBListVector, [listValue([100, 200, 300])]); - assertValues(chunk, 6, DuckDBListVector, [ + assertValues(chunk, i++, DuckDBTimeTZVector, [TIMETZ.max]); + assertValues(chunk, i++, DuckDBTimestampTZVector, [TIMESTAMPTZ.max]); + assertValues(chunk, i++, DuckDBTimestampSecondsVector, [ + TIMESTAMP_S.max, + ]); + assertValues(chunk, i++, DuckDBTimestampMillisecondsVector, [ + TIMESTAMP_MS.max, + ]); + assertValues(chunk, i++, DuckDBTimestampNanosecondsVector, [ + TIMESTAMP_NS.max, + ]); + assertValues(chunk, i++, DuckDBVarIntVector, [VARINT.max]); + assertValues(chunk, i++, DuckDBListVector, [ + listValue([100, 200, 300]), + ]); + assertValues(chunk, i++, DuckDBListVector, [ listValue([decimalValue(9876n, 4, 1), decimalValue(5432n, 4, 1)]), ]); - assertValues(chunk, 7, DuckDBStructVector, [ + assertValues(chunk, i++, DuckDBStructVector, [ structValue({ 'a': 42, 'b': 'duck' }), ]); - assertValues(chunk, 8, DuckDBArrayVector, [ + assertValues(chunk, i++, DuckDBArrayVector, [ arrayValue([100, 200, 300]), ]); assertValues( chunk, - 9, + i++, DuckDBIntegerVector, [null] ); diff --git a/bindings/pkgs/@duckdb/node-bindings/duckdb.d.ts b/bindings/pkgs/@duckdb/node-bindings/duckdb.d.ts index 498381a9..36489aa8 100644 --- a/bindings/pkgs/@duckdb/node-bindings/duckdb.d.ts +++ b/bindings/pkgs/@duckdb/node-bindings/duckdb.d.ts @@ -661,9 +661,16 @@ export function create_time_tz_value(input: TimeTZ): Value; export function create_timestamp(input: Timestamp): Value; // DUCKDB_API duckdb_value duckdb_create_timestamp_tz(duckdb_timestamp input); +export function create_timestamp_tz(input: Timestamp): Value; + // DUCKDB_API duckdb_value duckdb_create_timestamp_s(duckdb_timestamp_s input); +export function create_timestamp_s(input: TimestampSeconds): Value; + // DUCKDB_API duckdb_value duckdb_create_timestamp_ms(duckdb_timestamp_ms input); +export function create_timestamp_ms(input: TimestampMilliseconds): Value; + // DUCKDB_API duckdb_value duckdb_create_timestamp_ns(duckdb_timestamp_ns input); +export function create_timestamp_ns(input: TimestampNanoseconds): Value; // DUCKDB_API duckdb_value duckdb_create_interval(duckdb_interval input); export function create_interval(input: Interval): Value; @@ -732,9 +739,16 @@ export function get_time_tz(value: Value): TimeTZ; export function get_timestamp(value: Value): Timestamp; // DUCKDB_API duckdb_timestamp duckdb_get_timestamp_tz(duckdb_value val); +export function get_timestamp_tz(value: Value): Timestamp; + // DUCKDB_API duckdb_timestamp_s duckdb_get_timestamp_s(duckdb_value val); +export function get_timestamp_s(value: Value): TimestampSeconds; + // DUCKDB_API duckdb_timestamp_ms duckdb_get_timestamp_ms(duckdb_value val); +export function get_timestamp_ms(value: Value): TimestampMilliseconds; + // DUCKDB_API duckdb_timestamp_ns duckdb_get_timestamp_ns(duckdb_value val); +export function get_timestamp_ns(value: Value): TimestampNanoseconds; // DUCKDB_API duckdb_interval duckdb_get_interval(duckdb_value val); export function get_interval(value: Value): Interval; diff --git a/bindings/src/duckdb_node_bindings.cpp b/bindings/src/duckdb_node_bindings.cpp index a019fd03..67c3be4e 100644 --- a/bindings/src/duckdb_node_bindings.cpp +++ b/bindings/src/duckdb_node_bindings.cpp @@ -110,6 +110,12 @@ duckdb_timestamp GetTimestampFromObject(Napi::Env env, Napi::Object timestamp_ob return { micros }; } +Napi::Object MakeTimestampSecondsObject(Napi::Env env, duckdb_timestamp_s timestamp) { + auto timestamp_s_obj = Napi::Object::New(env); + timestamp_s_obj.Set("seconds", Napi::BigInt::New(env, timestamp.seconds)); + return timestamp_s_obj; +} + duckdb_timestamp_s GetTimestampSecondsFromObject(Napi::Env env, Napi::Object timestamp_s_obj) { bool lossless; auto seconds = timestamp_s_obj.Get("seconds").As().Int64Value(&lossless); @@ -119,6 +125,12 @@ duckdb_timestamp_s GetTimestampSecondsFromObject(Napi::Env env, Napi::Object tim return { seconds }; } +Napi::Object MakeTimestampMillisecondsObject(Napi::Env env, duckdb_timestamp_ms timestamp) { + auto timestamp_ms_obj = Napi::Object::New(env); + timestamp_ms_obj.Set("millis", Napi::BigInt::New(env, timestamp.millis)); + return timestamp_ms_obj; +} + duckdb_timestamp_ms GetTimestampMillisecondsFromObject(Napi::Env env, Napi::Object timestamp_ms_obj) { bool lossless; auto millis = timestamp_ms_obj.Get("millis").As().Int64Value(&lossless); @@ -128,6 +140,12 @@ duckdb_timestamp_ms GetTimestampMillisecondsFromObject(Napi::Env env, Napi::Obje return { millis }; } +Napi::Object MakeTimestampNanosecondsObject(Napi::Env env, duckdb_timestamp_ns timestamp) { + auto timestamp_ns_obj = Napi::Object::New(env); + timestamp_ns_obj.Set("nanos", Napi::BigInt::New(env, timestamp.nanos)); + return timestamp_ns_obj; +} + duckdb_timestamp_ns GetTimestampNanosecondsFromObject(Napi::Env env, Napi::Object timestamp_ns_obj) { bool lossless; auto nanos = timestamp_ns_obj.Get("nanos").As().Int64Value(&lossless); @@ -1150,6 +1168,10 @@ class DuckDBNodeAddon : public Napi::Addon { InstanceMethod("create_time", &DuckDBNodeAddon::create_time), InstanceMethod("create_time_tz_value", &DuckDBNodeAddon::create_time_tz_value), InstanceMethod("create_timestamp", &DuckDBNodeAddon::create_timestamp), + InstanceMethod("create_timestamp_tz", &DuckDBNodeAddon::create_timestamp_tz), + InstanceMethod("create_timestamp_s", &DuckDBNodeAddon::create_timestamp_s), + InstanceMethod("create_timestamp_ms", &DuckDBNodeAddon::create_timestamp_ms), + InstanceMethod("create_timestamp_ns", &DuckDBNodeAddon::create_timestamp_ns), InstanceMethod("create_interval", &DuckDBNodeAddon::create_interval), InstanceMethod("create_blob", &DuckDBNodeAddon::create_blob), InstanceMethod("get_bool", &DuckDBNodeAddon::get_bool), @@ -1171,6 +1193,10 @@ class DuckDBNodeAddon : public Napi::Addon { InstanceMethod("get_time", &DuckDBNodeAddon::get_time), InstanceMethod("get_time_tz", &DuckDBNodeAddon::get_time_tz), InstanceMethod("get_timestamp", &DuckDBNodeAddon::get_timestamp), + InstanceMethod("get_timestamp_tz", &DuckDBNodeAddon::get_timestamp_tz), + InstanceMethod("get_timestamp_s", &DuckDBNodeAddon::get_timestamp_s), + InstanceMethod("get_timestamp_ms", &DuckDBNodeAddon::get_timestamp_ms), + InstanceMethod("get_timestamp_ns", &DuckDBNodeAddon::get_timestamp_ns), InstanceMethod("get_interval", &DuckDBNodeAddon::get_interval), InstanceMethod("get_value_type", &DuckDBNodeAddon::get_value_type), InstanceMethod("get_blob", &DuckDBNodeAddon::get_blob), @@ -2527,9 +2553,40 @@ class DuckDBNodeAddon : public Napi::Addon { } // DUCKDB_API duckdb_value duckdb_create_timestamp_tz(duckdb_timestamp input); + // function create_timestamp_tz(input: Timestamp): Value + Napi::Value create_timestamp_tz(const Napi::CallbackInfo& info) { + auto env = info.Env(); + auto input = GetTimestampFromObject(env, info[0].As()); + auto value = duckdb_create_timestamp_tz(input); + return CreateExternalForValue(env, value); + } + // DUCKDB_API duckdb_value duckdb_create_timestamp_s(duckdb_timestamp_s input); + // function create_timestamp_s(input: TimestampSeconds): Value + Napi::Value create_timestamp_s(const Napi::CallbackInfo& info) { + auto env = info.Env(); + auto input = GetTimestampSecondsFromObject(env, info[0].As()); + auto value = duckdb_create_timestamp_s(input); + return CreateExternalForValue(env, value); + } + // DUCKDB_API duckdb_value duckdb_create_timestamp_ms(duckdb_timestamp_ms input); + // function create_timestamp_ms(input: TimestampMilliseconds): Value + Napi::Value create_timestamp_ms(const Napi::CallbackInfo& info) { + auto env = info.Env(); + auto input = GetTimestampMillisecondsFromObject(env, info[0].As()); + auto value = duckdb_create_timestamp_ms(input); + return CreateExternalForValue(env, value); + } + // DUCKDB_API duckdb_value duckdb_create_timestamp_ns(duckdb_timestamp_ns input); + // function create_timestamp_ns(input: TimestampNanoseconds): Value + Napi::Value create_timestamp_ns(const Napi::CallbackInfo& info) { + auto env = info.Env(); + auto input = GetTimestampNanosecondsFromObject(env, info[0].As()); + auto value = duckdb_create_timestamp_ns(input); + return CreateExternalForValue(env, value); + } // DUCKDB_API duckdb_value duckdb_create_interval(duckdb_interval input); // function create_interval(input: Interval): Value @@ -2728,9 +2785,40 @@ class DuckDBNodeAddon : public Napi::Addon { } // DUCKDB_API duckdb_timestamp duckdb_get_timestamp_tz(duckdb_value val); + // function get_timestamp_tz(value: Value): Timestamp + Napi::Value get_timestamp_tz(const Napi::CallbackInfo& info) { + auto env = info.Env(); + auto value = GetValueFromExternal(env, info[0]); + auto timestamp = duckdb_get_timestamp_tz(value); + return MakeTimestampObject(env, timestamp); + } + // DUCKDB_API duckdb_timestamp_s duckdb_get_timestamp_s(duckdb_value val); + // function get_timestamp_s(value: Value): TimestampSeconds + Napi::Value get_timestamp_s(const Napi::CallbackInfo& info) { + auto env = info.Env(); + auto value = GetValueFromExternal(env, info[0]); + auto timestamp_s = duckdb_get_timestamp_s(value); + return MakeTimestampSecondsObject(env, timestamp_s); + } + // DUCKDB_API duckdb_timestamp_ms duckdb_get_timestamp_ms(duckdb_value val); + // function get_timestamp_ms(value: Value): TimestampMilliseconds + Napi::Value get_timestamp_ms(const Napi::CallbackInfo& info) { + auto env = info.Env(); + auto value = GetValueFromExternal(env, info[0]); + auto timestamp_ms = duckdb_get_timestamp_ms(value); + return MakeTimestampMillisecondsObject(env, timestamp_ms); + } + // DUCKDB_API duckdb_timestamp_ns duckdb_get_timestamp_ns(duckdb_value val); + // function get_timestamp_ns(value: Value): TimestampNanoseconds + Napi::Value get_timestamp_ns(const Napi::CallbackInfo& info) { + auto env = info.Env(); + auto value = GetValueFromExternal(env, info[0]); + auto timestamp_ns = duckdb_get_timestamp_ns(value); + return MakeTimestampNanosecondsObject(env, timestamp_ns); + } // DUCKDB_API duckdb_interval duckdb_get_interval(duckdb_value val); // function get_interval(value: Value): Interval @@ -3979,11 +4067,11 @@ NODE_API_ADDON(DuckDBNodeAddon) --- 411 total functions - 218 instance methods + 226 instance methods 3 unimplemented instance cache functions 1 unimplemented logical type function - 8 unimplemented value creation functions - 11 unimplemented value inspection functions + 4 unimplemented value creation functions + 7 unimplemented value inspection functions 13 unimplemented scalar function functions 4 unimplemented scalar function set functions 12 unimplemented aggregate function functions diff --git a/bindings/test/values.test.ts b/bindings/test/values.test.ts index 9b145728..08ac8040 100644 --- a/bindings/test/values.test.ts +++ b/bindings/test/values.test.ts @@ -4,6 +4,9 @@ import duckdb, { Interval, Time, Timestamp, + TimestampMilliseconds, + TimestampNanoseconds, + TimestampSeconds, TimeTZ, } from '@duckdb/node-bindings'; import { expect, suite, test } from 'vitest'; @@ -27,6 +30,10 @@ import { TIME, TIME_TZ, TIMESTAMP, + TIMESTAMP_MS, + TIMESTAMP_NS, + TIMESTAMP_S, + TIMESTAMP_TZ, TINYINT, UBIGINT, UHUGEINT, @@ -155,6 +162,30 @@ suite('values', () => { expectLogicalType(duckdb.get_value_type(timestamp_value), TIMESTAMP); expect(duckdb.get_timestamp(timestamp_value)).toStrictEqual(input); }); + test('timestamp_tz', () => { + const input: Timestamp = { micros: 9223372036854775806n }; + const timestamp_value = duckdb.create_timestamp_tz(input); + expectLogicalType(duckdb.get_value_type(timestamp_value), TIMESTAMP_TZ); + expect(duckdb.get_timestamp_tz(timestamp_value)).toStrictEqual(input); + }); + test('timestamp_s', () => { + const input: TimestampSeconds = { seconds: 9223372036854n }; + const timestamp_s_value = duckdb.create_timestamp_s(input); + expectLogicalType(duckdb.get_value_type(timestamp_s_value), TIMESTAMP_S); + expect(duckdb.get_timestamp_s(timestamp_s_value)).toStrictEqual(input); + }); + test('timestamp_ms', () => { + const input: TimestampMilliseconds = { millis: 9223372036854775n }; + const timestamp_ms_value = duckdb.create_timestamp_ms(input); + expectLogicalType(duckdb.get_value_type(timestamp_ms_value), TIMESTAMP_MS); + expect(duckdb.get_timestamp_ms(timestamp_ms_value)).toStrictEqual(input); + }); + test('timestamp_ns', () => { + const input: TimestampNanoseconds = { nanos: 9223372036854775806n }; + const timestamp_ns_value = duckdb.create_timestamp_ns(input); + expectLogicalType(duckdb.get_value_type(timestamp_ns_value), TIMESTAMP_NS); + expect(duckdb.get_timestamp_ns(timestamp_ns_value)).toStrictEqual(input); + }); test('interval', () => { const input: Interval = { months: 999, days: 999, micros: 999999999n }; const interval_value = duckdb.create_interval(input);