Skip to content

Commit 37ef62b

Browse files
authored
Merge pull request #156 from duckdb/jray/create-get-and-bind-varint
create, get, and bind varint
2 parents 16ae003 + df5cc16 commit 37ef62b

File tree

6 files changed

+123
-36
lines changed

6 files changed

+123
-36
lines changed

api/src/DuckDBPreparedStatement.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
DuckDBType,
1313
TIMESTAMPTZ,
1414
TIMETZ,
15+
VARINT,
1516
} from './DuckDBType';
1617
import { DuckDBTypeId } from './DuckDBTypeId';
1718
import { StatementType } from './enums';
@@ -95,6 +96,9 @@ export class DuckDBPreparedStatement {
9596
public bindUHugeInt(parameterIndex: number, value: bigint) {
9697
duckdb.bind_uhugeint(this.prepared_statement, parameterIndex, value);
9798
}
99+
public bindVarInt(parameterIndex: number, value: bigint) {
100+
this.bindValue(parameterIndex, value, VARINT);
101+
}
98102
public bindDecimal(parameterIndex: number, value: DuckDBDecimalValue) {
99103
duckdb.bind_decimal(this.prepared_statement, parameterIndex, value);
100104
}

api/src/createValue.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,10 @@ export function createValue(type: DuckDBType, input: DuckDBValue): Value {
186186
`Cannot create values of type ANY. Specify a specific type.`
187187
);
188188
case DuckDBTypeId.VARINT:
189-
throw new Error(`not yet implemented for VARINT`); // TODO: implement when available in 1.2.0
189+
if (typeof input === 'bigint') {
190+
return duckdb.create_varint(input);
191+
}
192+
throw new Error(`input is not a bigint`);
190193
case DuckDBTypeId.SQLNULL:
191194
throw new Error(`not yet implemented for SQLNUll`); // TODO: implement when available in 1.2.0
192195
default:

api/test/api.test.ts

Lines changed: 46 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -400,29 +400,40 @@ describe('api', () => {
400400
test('should support running prepared statements', async () => {
401401
await withConnection(async (connection) => {
402402
const prepared = await connection.prepare(
403-
'select $num as a, $str as b, $bool as c, $timetz as d, $list as e, $struct as f, $array as g, $null as h'
403+
'select \
404+
$num as num, \
405+
$str as str, \
406+
$bool as bool, \
407+
$timetz as timetz, \
408+
$varint as varint, \
409+
$list as list, \
410+
$struct as struct, \
411+
$array as array, \
412+
$null as null_value'
404413
);
405-
assert.strictEqual(prepared.parameterCount, 8);
414+
assert.strictEqual(prepared.parameterCount, 9);
406415
assert.strictEqual(prepared.parameterName(1), 'num');
407416
assert.strictEqual(prepared.parameterName(2), 'str');
408417
assert.strictEqual(prepared.parameterName(3), 'bool');
409418
assert.strictEqual(prepared.parameterName(4), 'timetz');
410-
assert.strictEqual(prepared.parameterName(5), 'list');
411-
assert.strictEqual(prepared.parameterName(6), 'struct');
412-
assert.strictEqual(prepared.parameterName(7), 'array');
413-
assert.strictEqual(prepared.parameterName(8), 'null');
419+
assert.strictEqual(prepared.parameterName(5), 'varint');
420+
assert.strictEqual(prepared.parameterName(6), 'list');
421+
assert.strictEqual(prepared.parameterName(7), 'struct');
422+
assert.strictEqual(prepared.parameterName(8), 'array');
423+
assert.strictEqual(prepared.parameterName(9), 'null');
414424
prepared.bindInteger(1, 10);
415425
prepared.bindVarchar(2, 'abc');
416426
prepared.bindBoolean(3, true);
417427
prepared.bindTimeTZ(4, TIMETZ.max);
418-
prepared.bindList(5, listValue([100, 200, 300]), LIST(INTEGER));
428+
prepared.bindVarInt(5, VARINT.max);
429+
prepared.bindList(6, listValue([100, 200, 300]), LIST(INTEGER));
419430
prepared.bindStruct(
420-
6,
431+
7,
421432
structValue({ 'a': 42, 'b': 'duck' }),
422433
STRUCT({ 'a': INTEGER, 'b': VARCHAR })
423434
);
424-
prepared.bindArray(7, arrayValue([100, 200, 300]), ARRAY(INTEGER, 3));
425-
prepared.bindNull(8);
435+
prepared.bindArray(8, arrayValue([100, 200, 300]), ARRAY(INTEGER, 3));
436+
prepared.bindNull(9);
426437
assert.equal(prepared.parameterTypeId(1), DuckDBTypeId.INTEGER);
427438
assert.deepEqual(prepared.parameterType(1), INTEGER);
428439
// See https://github.com/duckdb/duckdb/issues/16137
@@ -432,29 +443,32 @@ describe('api', () => {
432443
assert.deepEqual(prepared.parameterType(3), BOOLEAN);
433444
assert.equal(prepared.parameterTypeId(4), DuckDBTypeId.TIME_TZ);
434445
assert.deepEqual(prepared.parameterType(4), TIMETZ);
435-
assert.equal(prepared.parameterTypeId(5), DuckDBTypeId.LIST);
436-
assert.deepEqual(prepared.parameterType(5), LIST(INTEGER));
437-
assert.equal(prepared.parameterTypeId(6), DuckDBTypeId.STRUCT);
438-
assert.deepEqual(prepared.parameterType(6), STRUCT({ 'a': INTEGER, 'b': VARCHAR }));
439-
assert.equal(prepared.parameterTypeId(7), DuckDBTypeId.ARRAY);
440-
assert.deepEqual(prepared.parameterType(7), ARRAY(INTEGER, 3));
441-
assert.equal(prepared.parameterTypeId(8), DuckDBTypeId.SQLNULL);
442-
assert.deepEqual(prepared.parameterType(8), SQLNULL);
446+
assert.equal(prepared.parameterTypeId(5), DuckDBTypeId.VARINT);
447+
assert.deepEqual(prepared.parameterType(5), VARINT);
448+
assert.equal(prepared.parameterTypeId(6), DuckDBTypeId.LIST);
449+
assert.deepEqual(prepared.parameterType(6), LIST(INTEGER));
450+
assert.equal(prepared.parameterTypeId(7), DuckDBTypeId.STRUCT);
451+
assert.deepEqual(prepared.parameterType(7), STRUCT({ 'a': INTEGER, 'b': VARCHAR }));
452+
assert.equal(prepared.parameterTypeId(8), DuckDBTypeId.ARRAY);
453+
assert.deepEqual(prepared.parameterType(8), ARRAY(INTEGER, 3));
454+
assert.equal(prepared.parameterTypeId(9), DuckDBTypeId.SQLNULL);
455+
assert.deepEqual(prepared.parameterType(9), SQLNULL);
443456
const result = await prepared.run();
444457
assertColumns(result, [
445-
{ name: 'a', type: INTEGER },
446-
{ name: 'b', type: VARCHAR },
447-
{ name: 'c', type: BOOLEAN },
448-
{ name: 'd', type: TIMETZ },
449-
{ name: 'e', type: LIST(INTEGER) },
450-
{ name: 'f', type: STRUCT({ 'a': INTEGER, 'b': VARCHAR }) },
451-
{ name: 'g', type: ARRAY(INTEGER, 3) },
452-
{ name: 'h', type: INTEGER },
458+
{ name: 'num', type: INTEGER },
459+
{ name: 'str', type: VARCHAR },
460+
{ name: 'bool', type: BOOLEAN },
461+
{ name: 'timetz', type: TIMETZ },
462+
{ name: 'varint', type: VARINT },
463+
{ name: 'list', type: LIST(INTEGER) },
464+
{ name: 'struct', type: STRUCT({ 'a': INTEGER, 'b': VARCHAR }) },
465+
{ name: 'array', type: ARRAY(INTEGER, 3) },
466+
{ name: 'null_value', type: INTEGER },
453467
]);
454468
const chunk = await result.fetchChunk();
455469
assert.isDefined(chunk);
456470
if (chunk) {
457-
assert.strictEqual(chunk.columnCount, 8);
471+
assert.strictEqual(chunk.columnCount, 9);
458472
assert.strictEqual(chunk.rowCount, 1);
459473
assertValues<number, DuckDBIntegerVector>(
460474
chunk,
@@ -475,16 +489,17 @@ describe('api', () => {
475489
[true]
476490
);
477491
assertValues(chunk, 3, DuckDBTimeTZVector, [TIMETZ.max]);
478-
assertValues(chunk, 4, DuckDBListVector, [listValue([100, 200, 300])]);
479-
assertValues(chunk, 5, DuckDBStructVector, [
492+
assertValues(chunk, 4, DuckDBVarIntVector, [VARINT.max]);
493+
assertValues(chunk, 5, DuckDBListVector, [listValue([100, 200, 300])]);
494+
assertValues(chunk, 6, DuckDBStructVector, [
480495
structValue({ 'a': 42, 'b': 'duck' }),
481496
]);
482-
assertValues(chunk, 6, DuckDBArrayVector, [
497+
assertValues(chunk, 7, DuckDBArrayVector, [
483498
arrayValue([100, 200, 300]),
484499
]);
485500
assertValues<number, DuckDBIntegerVector>(
486501
chunk,
487-
7,
502+
8,
488503
DuckDBIntegerVector,
489504
[null]
490505
);

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,8 @@ export function create_hugeint(input: bigint): Value;
637637
export function create_uhugeint(input: bigint): Value;
638638

639639
// DUCKDB_API duckdb_value duckdb_create_varint(duckdb_varint input);
640+
export function create_varint(input: bigint): Value;
641+
640642
// DUCKDB_API duckdb_value duckdb_create_decimal(duckdb_decimal input);
641643

642644
// DUCKDB_API duckdb_value duckdb_create_float(float input);
@@ -705,6 +707,8 @@ export function get_hugeint(value: Value): bigint;
705707
export function get_uhugeint(value: Value): bigint;
706708

707709
// DUCKDB_API duckdb_varint duckdb_get_varint(duckdb_value val);
710+
export function get_varint(value: Value): bigint;
711+
708712
// DUCKDB_API duckdb_decimal duckdb_get_decimal(duckdb_value val);
709713

710714
// DUCKDB_API float duckdb_get_float(duckdb_value val);

bindings/src/duckdb_node_bindings.cpp

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,38 @@ Napi::BigInt MakeBigIntFromUHugeInt(Napi::Env env, duckdb_uhugeint uhugeint) {
213213
return Napi::BigInt::New(env, sign_bit, word_count, words);
214214
}
215215

216+
duckdb_varint GetVarIntFromBigInt(Napi::Env env, Napi::BigInt bigint) {
217+
int sign_bit;
218+
size_t word_count = bigint.WordCount();
219+
size_t byte_count = word_count * 8;
220+
uint64_t *words = static_cast<uint64_t*>(duckdb_malloc(byte_count));
221+
bigint.ToWords(&sign_bit, &word_count, words);
222+
uint8_t *data = reinterpret_cast<uint8_t*>(words);
223+
idx_t size = byte_count;
224+
bool is_negative = bool(sign_bit);
225+
// convert little-endian to big-endian
226+
for (size_t i = 0; i < size/2; i++) {
227+
auto tmp = data[i];
228+
data[i] = data[size - 1 - i];
229+
data[size - 1 - i] = tmp;
230+
}
231+
return { data, size, is_negative };
232+
}
233+
234+
Napi::BigInt MakeBigIntFromVarInt(Napi::Env env, duckdb_varint varint) {
235+
int sign_bit = varint.is_negative ? 1 : 0;
236+
size_t word_count = varint.size / 8;
237+
uint8_t *data = static_cast<uint8_t*>(duckdb_malloc(varint.size));
238+
// convert big-endian to little-endian
239+
for (size_t i = 0; i < varint.size; i++) {
240+
data[i] = varint.data[varint.size - 1 - i];
241+
}
242+
uint64_t *words = reinterpret_cast<uint64_t*>(data);
243+
auto bigint = Napi::BigInt::New(env, sign_bit, word_count, words);
244+
duckdb_free(data);
245+
return bigint;
246+
}
247+
216248
Napi::Object MakeDecimalObject(Napi::Env env, duckdb_decimal decimal) {
217249
auto decimal_obj = Napi::Object::New(env);
218250
decimal_obj.Set("width", Napi::Number::New(env, decimal.width));
@@ -1110,6 +1142,7 @@ class DuckDBNodeAddon : public Napi::Addon<DuckDBNodeAddon> {
11101142
InstanceMethod("create_int64", &DuckDBNodeAddon::create_int64),
11111143
InstanceMethod("create_hugeint", &DuckDBNodeAddon::create_hugeint),
11121144
InstanceMethod("create_uhugeint", &DuckDBNodeAddon::create_uhugeint),
1145+
InstanceMethod("create_varint", &DuckDBNodeAddon::create_varint),
11131146
InstanceMethod("create_float", &DuckDBNodeAddon::create_float),
11141147
InstanceMethod("create_double", &DuckDBNodeAddon::create_double),
11151148
InstanceMethod("create_date", &DuckDBNodeAddon::create_date),
@@ -1129,6 +1162,7 @@ class DuckDBNodeAddon : public Napi::Addon<DuckDBNodeAddon> {
11291162
InstanceMethod("get_uint64", &DuckDBNodeAddon::get_uint64),
11301163
InstanceMethod("get_hugeint", &DuckDBNodeAddon::get_hugeint),
11311164
InstanceMethod("get_uhugeint", &DuckDBNodeAddon::get_uhugeint),
1165+
InstanceMethod("get_varint", &DuckDBNodeAddon::get_varint),
11321166
InstanceMethod("get_float", &DuckDBNodeAddon::get_float),
11331167
InstanceMethod("get_double", &DuckDBNodeAddon::get_double),
11341168
InstanceMethod("get_date", &DuckDBNodeAddon::get_date),
@@ -2416,6 +2450,16 @@ class DuckDBNodeAddon : public Napi::Addon<DuckDBNodeAddon> {
24162450
}
24172451

24182452
// DUCKDB_API duckdb_value duckdb_create_varint(duckdb_varint input);
2453+
// function create_varint(input: bigint): Value
2454+
Napi::Value create_varint(const Napi::CallbackInfo& info) {
2455+
auto env = info.Env();
2456+
auto input_bigint = info[0].As<Napi::BigInt>();
2457+
auto varint = GetVarIntFromBigInt(env, input_bigint);
2458+
auto value = duckdb_create_varint(varint);
2459+
duckdb_free(varint.data);
2460+
return CreateExternalForValue(env, value);
2461+
}
2462+
24192463
// DUCKDB_API duckdb_value duckdb_create_decimal(duckdb_decimal input);
24202464

24212465
// DUCKDB_API duckdb_value duckdb_create_float(float input);
@@ -2600,6 +2644,16 @@ class DuckDBNodeAddon : public Napi::Addon<DuckDBNodeAddon> {
26002644
}
26012645

26022646
// DUCKDB_API duckdb_varint duckdb_get_varint(duckdb_value val);
2647+
// function get_varint(value: Value): bigint
2648+
Napi::Value get_varint(const Napi::CallbackInfo& info) {
2649+
auto env = info.Env();
2650+
auto value = GetValueFromExternal(env, info[0]);
2651+
auto varint = duckdb_get_varint(value);
2652+
auto bigint = MakeBigIntFromVarInt(env, varint);
2653+
duckdb_free(varint.data);
2654+
return bigint;
2655+
}
2656+
26032657
// DUCKDB_API duckdb_decimal duckdb_get_decimal(duckdb_value val);
26042658

26052659
// DUCKDB_API float duckdb_get_float(duckdb_value val);
@@ -3908,11 +3962,11 @@ NODE_API_ADDON(DuckDBNodeAddon)
39083962
---
39093963
411 total functions
39103964
3911-
214 instance methods
3965+
216 instance methods
39123966
3 unimplemented instance cache functions
39133967
1 unimplemented logical type function
3914-
10 unimplemented value creation functions
3915-
13 unimplemented value inspection functions
3968+
9 unimplemented value creation functions
3969+
12 unimplemented value inspection functions
39163970
13 unimplemented scalar function functions
39173971
4 unimplemented scalar function set functions
39183972
12 unimplemented aggregate function functions

bindings/test/values.test.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ import {
2525
UINTEGER,
2626
USMALLINT,
2727
UTINYINT,
28-
VARCHAR
28+
VARCHAR,
29+
VARINT
2930
} from './utils/expectedLogicalTypes';
3031

3132
suite('values', () => {
@@ -95,6 +96,12 @@ suite('values', () => {
9596
expectLogicalType(duckdb.get_value_type(uhugeint_value), UHUGEINT);
9697
expect(duckdb.get_uhugeint(uhugeint_value)).toBe(input);
9798
});
99+
test('varint', () => {
100+
const input = -((((2n ** 10n + 11n) * (2n ** 64n) + (2n ** 9n + 7n)) * (2n ** 64n)) + (2n ** 8n + 5n));
101+
const varint_value = duckdb.create_varint(input);
102+
expectLogicalType(duckdb.get_value_type(varint_value), VARINT);
103+
expect(duckdb.get_varint(varint_value)).toBe(input);
104+
});
98105
test('float', () => {
99106
const input = 3.4028234663852886e38;
100107
const float_value = duckdb.create_float(input);

0 commit comments

Comments
 (0)