@rip-lang/db 1.3.80 → 1.3.82
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/duckdb-binary.rip +193 -6
- package/lib/duckdb.mjs +86 -7
- package/package.json +4 -3
package/lib/duckdb-binary.rip
CHANGED
|
@@ -32,17 +32,20 @@ export LogicalTypeId =
|
|
|
32
32
|
UBIGINT: 31
|
|
33
33
|
TIMESTAMP_TZ: 32
|
|
34
34
|
TIME_TZ: 34
|
|
35
|
+
TIME_NS: 35
|
|
35
36
|
BIT: 36
|
|
36
37
|
BIGNUM: 39
|
|
37
38
|
UHUGEINT: 49
|
|
38
39
|
HUGEINT: 50
|
|
39
40
|
UUID: 54
|
|
41
|
+
GEOMETRY: 60
|
|
40
42
|
STRUCT: 100
|
|
41
43
|
LIST: 101
|
|
42
44
|
MAP: 102
|
|
43
45
|
ENUM: 104
|
|
44
46
|
UNION: 107
|
|
45
47
|
ARRAY: 108
|
|
48
|
+
VARIANT: 109
|
|
46
49
|
|
|
47
50
|
# Shared TextEncoder instance (avoid allocating per call)
|
|
48
51
|
textEncoder = new TextEncoder()
|
|
@@ -233,7 +236,7 @@ serializeVector = (s, column, values) ->
|
|
|
233
236
|
|
|
234
237
|
switch typeId
|
|
235
238
|
when LogicalTypeId.VARCHAR, LogicalTypeId.CHAR
|
|
236
|
-
s.writeList 102, values, (s, v) -> s.writeString
|
|
239
|
+
s.writeList 102, values, (s, v) -> s.writeString stringify(v)
|
|
237
240
|
|
|
238
241
|
when LogicalTypeId.BOOLEAN
|
|
239
242
|
s.writeFieldId 102
|
|
@@ -341,8 +344,87 @@ serializeVector = (s, column, values) ->
|
|
|
341
344
|
dv.setBigInt64 i * 16 + 8, hi, true
|
|
342
345
|
s.writeData bytes
|
|
343
346
|
|
|
347
|
+
when LogicalTypeId.TIME
|
|
348
|
+
s.writeFieldId 102
|
|
349
|
+
bytes = new Uint8Array values.length * 8
|
|
350
|
+
dv = new DataView bytes.buffer
|
|
351
|
+
for v, i in values
|
|
352
|
+
dv.setBigInt64 i * 8, (if v? then timeToMicros v else 0n), true
|
|
353
|
+
s.writeData bytes
|
|
354
|
+
|
|
355
|
+
when LogicalTypeId.TIME_TZ
|
|
356
|
+
s.writeFieldId 102
|
|
357
|
+
bytes = new Uint8Array values.length * 8
|
|
358
|
+
dv = new DataView bytes.buffer
|
|
359
|
+
for v, i in values
|
|
360
|
+
dv.setBigUint64 i * 8, (if v? then timeToPackedTZ v else 0n), true
|
|
361
|
+
s.writeData bytes
|
|
362
|
+
|
|
363
|
+
when LogicalTypeId.TIME_NS
|
|
364
|
+
s.writeFieldId 102
|
|
365
|
+
bytes = new Uint8Array values.length * 8
|
|
366
|
+
dv = new DataView bytes.buffer
|
|
367
|
+
for v, i in values
|
|
368
|
+
dv.setBigInt64 i * 8, (if v? then timeToNanos v else 0n), true
|
|
369
|
+
s.writeData bytes
|
|
370
|
+
|
|
371
|
+
when LogicalTypeId.TIMESTAMP_SEC
|
|
372
|
+
s.writeFieldId 102
|
|
373
|
+
bytes = new Uint8Array values.length * 8
|
|
374
|
+
dv = new DataView bytes.buffer
|
|
375
|
+
for v, i in values
|
|
376
|
+
dv.setBigInt64 i * 8, (if v? then timestampToSecs v else 0n), true
|
|
377
|
+
s.writeData bytes
|
|
378
|
+
|
|
379
|
+
when LogicalTypeId.TIMESTAMP_MS
|
|
380
|
+
s.writeFieldId 102
|
|
381
|
+
bytes = new Uint8Array values.length * 8
|
|
382
|
+
dv = new DataView bytes.buffer
|
|
383
|
+
for v, i in values
|
|
384
|
+
dv.setBigInt64 i * 8, (if v? then timestampToMillis v else 0n), true
|
|
385
|
+
s.writeData bytes
|
|
386
|
+
|
|
387
|
+
when LogicalTypeId.TIMESTAMP_NS
|
|
388
|
+
s.writeFieldId 102
|
|
389
|
+
bytes = new Uint8Array values.length * 8
|
|
390
|
+
dv = new DataView bytes.buffer
|
|
391
|
+
for v, i in values
|
|
392
|
+
dv.setBigInt64 i * 8, (if v? then timestampToNanos v else 0n), true
|
|
393
|
+
s.writeData bytes
|
|
394
|
+
|
|
395
|
+
when LogicalTypeId.INTERVAL
|
|
396
|
+
s.writeFieldId 102
|
|
397
|
+
bytes = new Uint8Array values.length * 16
|
|
398
|
+
dv = new DataView bytes.buffer
|
|
399
|
+
for v, i in values
|
|
400
|
+
{ months, days, micros } = if v? then parseInterval v else { months: 0, days: 0, micros: 0n }
|
|
401
|
+
dv.setInt32 i * 16, months, true
|
|
402
|
+
dv.setInt32 i * 16 + 4, days, true
|
|
403
|
+
dv.setBigInt64 i * 16 + 8, micros, true
|
|
404
|
+
s.writeData bytes
|
|
405
|
+
|
|
406
|
+
when LogicalTypeId.HUGEINT
|
|
407
|
+
s.writeFieldId 102
|
|
408
|
+
bytes = new Uint8Array values.length * 16
|
|
409
|
+
dv = new DataView bytes.buffer
|
|
410
|
+
for v, i in values
|
|
411
|
+
{ lo, hi } = stringToHugeint v
|
|
412
|
+
dv.setBigUint64 i * 16, lo, true
|
|
413
|
+
dv.setBigInt64 i * 16 + 8, hi, true
|
|
414
|
+
s.writeData bytes
|
|
415
|
+
|
|
416
|
+
when LogicalTypeId.UHUGEINT
|
|
417
|
+
s.writeFieldId 102
|
|
418
|
+
bytes = new Uint8Array values.length * 16
|
|
419
|
+
dv = new DataView bytes.buffer
|
|
420
|
+
for v, i in values
|
|
421
|
+
{ lo, hi } = stringToUhugeint v
|
|
422
|
+
dv.setBigUint64 i * 16, lo, true
|
|
423
|
+
dv.setBigUint64 i * 16 + 8, hi, true
|
|
424
|
+
s.writeData bytes
|
|
425
|
+
|
|
344
426
|
else
|
|
345
|
-
s.writeList 102, values, (s, v) -> s.writeString
|
|
427
|
+
s.writeList 102, values, (s, v) -> s.writeString stringify(v)
|
|
346
428
|
|
|
347
429
|
s.writeEndMarker()
|
|
348
430
|
|
|
@@ -350,6 +432,10 @@ serializeVector = (s, column, values) ->
|
|
|
350
432
|
# Helper functions
|
|
351
433
|
# ==============================================================================
|
|
352
434
|
|
|
435
|
+
stringify = (v) ->
|
|
436
|
+
return '' unless v?
|
|
437
|
+
if typeof v is 'object' then JSON.stringify(v) else String(v)
|
|
438
|
+
|
|
353
439
|
createValidityBitmap = (values) ->
|
|
354
440
|
# Must be uint64-aligned (8-byte chunks) — the UI reads validity with getBigUint64
|
|
355
441
|
byteCount = Math.ceil(values.length / 64) * 8
|
|
@@ -391,6 +477,95 @@ uuidToHugeint = (uuid) ->
|
|
|
391
477
|
lo = full & ((1n << 64n) - 1n)
|
|
392
478
|
{ lo, hi }
|
|
393
479
|
|
|
480
|
+
timeToMicros = (value) ->
|
|
481
|
+
return 0n unless value
|
|
482
|
+
match = String(value).match /^(\d+):(\d+):(\d+)(?:\.(\d+))?/
|
|
483
|
+
return 0n unless match
|
|
484
|
+
h = parseInt match[1], 10
|
|
485
|
+
m = parseInt match[2], 10
|
|
486
|
+
s = parseInt match[3], 10
|
|
487
|
+
frac = match[4] or ''
|
|
488
|
+
fracUs = parseInt frac.padEnd(6, '0').slice(0, 6), 10
|
|
489
|
+
BigInt(h * 3600 + m * 60 + s) * 1000000n + BigInt(fracUs)
|
|
490
|
+
|
|
491
|
+
timeToPackedTZ = (value) ->
|
|
492
|
+
return 0n unless value
|
|
493
|
+
match = String(value).match /^(\d+):(\d+):(\d+)(?:\.(\d+))?([+-])(\d+):(\d+)$/
|
|
494
|
+
return 0n unless match
|
|
495
|
+
h = parseInt match[1], 10
|
|
496
|
+
m = parseInt match[2], 10
|
|
497
|
+
s = parseInt match[3], 10
|
|
498
|
+
frac = match[4] or ''
|
|
499
|
+
fracUs = parseInt frac.padEnd(6, '0').slice(0, 6), 10
|
|
500
|
+
sign = if match[5] is '+' then 1 else -1
|
|
501
|
+
offH = parseInt match[6], 10
|
|
502
|
+
offM = parseInt match[7], 10
|
|
503
|
+
micros = BigInt(h * 3600 + m * 60 + s) * 1000000n + BigInt(fracUs)
|
|
504
|
+
offsetSec = sign * (offH * 3600 + offM * 60)
|
|
505
|
+
(micros << 24n) | BigInt(offsetSec + 86399)
|
|
506
|
+
|
|
507
|
+
timeToNanos = (value) ->
|
|
508
|
+
return 0n unless value
|
|
509
|
+
match = String(value).match /^(\d+):(\d+):(\d+)(?:\.(\d+))?/
|
|
510
|
+
return 0n unless match
|
|
511
|
+
h = parseInt match[1], 10
|
|
512
|
+
m = parseInt match[2], 10
|
|
513
|
+
s = parseInt match[3], 10
|
|
514
|
+
frac = match[4] or ''
|
|
515
|
+
fracNs = parseInt frac.padEnd(9, '0').slice(0, 9), 10
|
|
516
|
+
BigInt(h * 3600 + m * 60 + s) * 1000000000n + BigInt(fracNs)
|
|
517
|
+
|
|
518
|
+
timestampToSecs = (value) ->
|
|
519
|
+
if value instanceof Date then BigInt Math.floor value.getTime() / 1000
|
|
520
|
+
else if typeof value is 'string' then BigInt Math.floor new Date(value).getTime() / 1000
|
|
521
|
+
else if typeof value is 'number' then BigInt value
|
|
522
|
+
else if typeof value is 'bigint' then value
|
|
523
|
+
else 0n
|
|
524
|
+
|
|
525
|
+
timestampToMillis = (value) ->
|
|
526
|
+
if value instanceof Date then BigInt value.getTime()
|
|
527
|
+
else if typeof value is 'string' then BigInt new Date(value).getTime()
|
|
528
|
+
else if typeof value is 'number' then BigInt(value) * 1000n
|
|
529
|
+
else if typeof value is 'bigint' then value
|
|
530
|
+
else 0n
|
|
531
|
+
|
|
532
|
+
timestampToNanos = (value) ->
|
|
533
|
+
if value instanceof Date then BigInt(value.getTime()) * 1000000n
|
|
534
|
+
else if typeof value is 'string' then BigInt(new Date(value).getTime()) * 1000000n
|
|
535
|
+
else if typeof value is 'number' then BigInt(value) * 1000000000n
|
|
536
|
+
else if typeof value is 'bigint' then value
|
|
537
|
+
else 0n
|
|
538
|
+
|
|
539
|
+
parseInterval = (value) ->
|
|
540
|
+
return { months: 0, days: 0, micros: 0n } unless value
|
|
541
|
+
str = String value
|
|
542
|
+
months = 0
|
|
543
|
+
days = 0
|
|
544
|
+
micros = 0n
|
|
545
|
+
if m = str.match /(-?\d+)\s+month/
|
|
546
|
+
months = parseInt m[1], 10
|
|
547
|
+
if m = str.match /(-?\d+)\s+day/
|
|
548
|
+
days = parseInt m[1], 10
|
|
549
|
+
if m = str.match /(-?[\d.]+)\s+second/
|
|
550
|
+
micros = BigInt Math.round parseFloat(m[1]) * 1000000
|
|
551
|
+
{ months, days, micros }
|
|
552
|
+
|
|
553
|
+
stringToHugeint = (value) ->
|
|
554
|
+
return { lo: 0n, hi: 0n } unless value?
|
|
555
|
+
big = BigInt value
|
|
556
|
+
mask64 = (1n << 64n) - 1n
|
|
557
|
+
lo = big & mask64
|
|
558
|
+
hi = big >> 64n
|
|
559
|
+
{ lo, hi }
|
|
560
|
+
|
|
561
|
+
stringToUhugeint = (value) ->
|
|
562
|
+
return { lo: 0n, hi: 0n } unless value?
|
|
563
|
+
big = BigInt value
|
|
564
|
+
mask64 = (1n << 64n) - 1n
|
|
565
|
+
lo = big & mask64
|
|
566
|
+
hi = (big >> 64n) & mask64
|
|
567
|
+
{ lo, hi }
|
|
568
|
+
|
|
394
569
|
mapDuckDBType = (typeName) ->
|
|
395
570
|
return LogicalTypeId.VARCHAR unless typeName
|
|
396
571
|
upper = String(typeName).toUpperCase()
|
|
@@ -412,14 +587,26 @@ mapDuckDBType = (typeName) ->
|
|
|
412
587
|
when 'DATE' then LogicalTypeId.DATE
|
|
413
588
|
when 'TIME' then LogicalTypeId.TIME
|
|
414
589
|
when 'TIMESTAMP', 'DATETIME' then LogicalTypeId.TIMESTAMP
|
|
415
|
-
when '
|
|
590
|
+
when 'TIMESTAMP_S', 'TIMESTAMP_SEC' then LogicalTypeId.TIMESTAMP_SEC
|
|
591
|
+
when 'TIMESTAMP_MS' then LogicalTypeId.TIMESTAMP_MS
|
|
592
|
+
when 'TIMESTAMP_NS' then LogicalTypeId.TIMESTAMP_NS
|
|
593
|
+
when 'TIMESTAMP WITH TIME ZONE', 'TIMESTAMPTZ', 'TIMESTAMP_TZ' then LogicalTypeId.TIMESTAMP_TZ
|
|
594
|
+
when 'TIME WITH TIME ZONE', 'TIMETZ', 'TIME_TZ' then LogicalTypeId.TIME_TZ
|
|
595
|
+
when 'TIME_NS' then LogicalTypeId.TIME_NS
|
|
416
596
|
when 'VARCHAR', 'TEXT', 'STRING', 'CHAR', 'BPCHAR' then LogicalTypeId.VARCHAR
|
|
417
|
-
when 'BLOB', 'BYTEA', 'BINARY', 'VARBINARY' then LogicalTypeId.BLOB
|
|
418
597
|
when 'UUID' then LogicalTypeId.UUID
|
|
419
598
|
when 'INTERVAL' then LogicalTypeId.INTERVAL
|
|
420
|
-
when '
|
|
599
|
+
when 'BLOB', 'BYTEA', 'BINARY', 'VARBINARY' then LogicalTypeId.VARCHAR
|
|
600
|
+
when 'BIT', 'BITSTRING' then LogicalTypeId.VARCHAR
|
|
601
|
+
when 'ENUM' then LogicalTypeId.VARCHAR
|
|
602
|
+
when 'LIST' then LogicalTypeId.VARCHAR
|
|
603
|
+
when 'STRUCT' then LogicalTypeId.VARCHAR
|
|
604
|
+
when 'MAP' then LogicalTypeId.VARCHAR
|
|
605
|
+
when 'UNION' then LogicalTypeId.VARCHAR
|
|
606
|
+
when 'ARRAY' then LogicalTypeId.VARCHAR
|
|
607
|
+
when 'JSON', 'VARIANT' then LogicalTypeId.VARCHAR
|
|
421
608
|
else
|
|
422
|
-
if upper.startsWith 'DECIMAL' then LogicalTypeId.VARCHAR
|
|
609
|
+
if upper.startsWith 'DECIMAL' then LogicalTypeId.VARCHAR
|
|
423
610
|
else if upper.startsWith 'VARCHAR' then LogicalTypeId.VARCHAR
|
|
424
611
|
else if upper.startsWith 'CHAR' then LogicalTypeId.CHAR
|
|
425
612
|
else LogicalTypeId.VARCHAR
|
package/lib/duckdb.mjs
CHANGED
|
@@ -121,7 +121,7 @@ const lib = dlopen(libPath, {
|
|
|
121
121
|
duckdb_vector_get_validity: { args: ['ptr'], returns: 'ptr' },
|
|
122
122
|
duckdb_destroy_data_chunk: { args: ['ptr'], returns: 'void' },
|
|
123
123
|
|
|
124
|
-
// Logical type introspection (for DECIMAL, ENUM, LIST, STRUCT)
|
|
124
|
+
// Logical type introspection (for DECIMAL, ENUM, LIST, STRUCT, ARRAY)
|
|
125
125
|
duckdb_column_logical_type: { args: ['ptr', 'u64'], returns: 'ptr' },
|
|
126
126
|
duckdb_destroy_logical_type: { args: ['ptr'], returns: 'void' },
|
|
127
127
|
duckdb_get_type_id: { args: ['ptr'], returns: 'i32' },
|
|
@@ -132,14 +132,17 @@ const lib = dlopen(libPath, {
|
|
|
132
132
|
duckdb_enum_dictionary_size: { args: ['ptr'], returns: 'u32' },
|
|
133
133
|
duckdb_enum_dictionary_value: { args: ['ptr', 'u64'], returns: 'ptr' },
|
|
134
134
|
|
|
135
|
-
// Nested type vector access (LIST, STRUCT)
|
|
135
|
+
// Nested type vector access (LIST, STRUCT, ARRAY)
|
|
136
136
|
duckdb_list_vector_get_child: { args: ['ptr'], returns: 'ptr' },
|
|
137
|
-
duckdb_list_vector_get_size: { args: ['ptr'], returns: 'u64' },
|
|
137
|
+
duckdb_list_vector_get_size: { args: ['ptr'], returns: 'u64' },
|
|
138
138
|
duckdb_struct_vector_get_child: { args: ['ptr', 'u64'], returns: 'ptr' },
|
|
139
139
|
duckdb_struct_type_child_count: { args: ['ptr'], returns: 'u64' },
|
|
140
140
|
duckdb_struct_type_child_name: { args: ['ptr', 'u64'], returns: 'ptr' },
|
|
141
141
|
duckdb_struct_type_child_type: { args: ['ptr', 'u64'], returns: 'ptr' },
|
|
142
142
|
duckdb_list_type_child_type: { args: ['ptr'], returns: 'ptr' },
|
|
143
|
+
duckdb_array_vector_get_child: { args: ['ptr'], returns: 'ptr' },
|
|
144
|
+
duckdb_array_type_child_type: { args: ['ptr'], returns: 'ptr' },
|
|
145
|
+
duckdb_array_type_array_size: { args: ['ptr'], returns: 'u64' },
|
|
143
146
|
|
|
144
147
|
// Memory
|
|
145
148
|
duckdb_free: { args: ['ptr'], returns: 'void' },
|
|
@@ -183,7 +186,11 @@ const DUCKDB_TYPE = {
|
|
|
183
186
|
UUID: 27,
|
|
184
187
|
UNION: 28,
|
|
185
188
|
BIT: 29,
|
|
186
|
-
|
|
189
|
+
TIME_TZ: 30,
|
|
190
|
+
TIMESTAMP_TZ: 31,
|
|
191
|
+
UHUGEINT: 32,
|
|
192
|
+
ARRAY: 33,
|
|
193
|
+
TIME_NS: 39,
|
|
187
194
|
};
|
|
188
195
|
|
|
189
196
|
export { DUCKDB_TYPE };
|
|
@@ -406,12 +413,13 @@ class Connection {
|
|
|
406
413
|
//
|
|
407
414
|
// Contract:
|
|
408
415
|
// BIGINT/UBIGINT → number (lossy above 2^53, JSON-safe)
|
|
409
|
-
// DECIMAL/HUGEINT → string (preserves precision)
|
|
416
|
+
// DECIMAL/HUGEINT/UHUGEINT → string (preserves precision)
|
|
410
417
|
// All timestamps → Date (UTC)
|
|
411
418
|
// UUID → string (formatted)
|
|
412
419
|
// VARCHAR/BLOB → string
|
|
413
420
|
// ENUM → string (dictionary lookup)
|
|
414
|
-
//
|
|
421
|
+
// TIME/TIME_NS/TIME_TZ → string (formatted)
|
|
422
|
+
// LIST/ARRAY → array, STRUCT → object, MAP → object
|
|
415
423
|
// ---------------------------------------------------------------------------
|
|
416
424
|
|
|
417
425
|
#extractChunks(resultPtr) {
|
|
@@ -431,7 +439,8 @@ class Connection {
|
|
|
431
439
|
|
|
432
440
|
// Get logical type metadata for complex types
|
|
433
441
|
if (type === DUCKDB_TYPE.DECIMAL || type === DUCKDB_TYPE.ENUM ||
|
|
434
|
-
type === DUCKDB_TYPE.LIST || type === DUCKDB_TYPE.STRUCT ||
|
|
442
|
+
type === DUCKDB_TYPE.LIST || type === DUCKDB_TYPE.STRUCT ||
|
|
443
|
+
type === DUCKDB_TYPE.MAP || type === DUCKDB_TYPE.ARRAY) {
|
|
435
444
|
const logType = lib.duckdb_column_logical_type(rp, BigInt(c));
|
|
436
445
|
if (logType) {
|
|
437
446
|
if (type === DUCKDB_TYPE.DECIMAL) {
|
|
@@ -488,6 +497,15 @@ class Connection {
|
|
|
488
497
|
const b = allocPtr(); new DataView(b.buffer).setBigUint64(0, BigInt(keyLogType), true);
|
|
489
498
|
lib.duckdb_destroy_logical_type(ptr(b));
|
|
490
499
|
}
|
|
500
|
+
} else if (type === DUCKDB_TYPE.ARRAY) {
|
|
501
|
+
col.arraySize = Number(lib.duckdb_array_type_array_size(logType));
|
|
502
|
+
const childLogType = lib.duckdb_array_type_child_type(logType);
|
|
503
|
+
if (childLogType) {
|
|
504
|
+
col.childType = lib.duckdb_get_type_id(childLogType);
|
|
505
|
+
const ltBuf2 = allocPtr();
|
|
506
|
+
new DataView(ltBuf2.buffer).setBigUint64(0, BigInt(childLogType), true);
|
|
507
|
+
lib.duckdb_destroy_logical_type(ptr(ltBuf2));
|
|
508
|
+
}
|
|
491
509
|
}
|
|
492
510
|
const ltBuf = allocPtr();
|
|
493
511
|
new DataView(ltBuf.buffer).setBigUint64(0, BigInt(logType), true);
|
|
@@ -592,6 +610,13 @@ class Connection {
|
|
|
592
610
|
return value.toString();
|
|
593
611
|
}
|
|
594
612
|
|
|
613
|
+
case DUCKDB_TYPE.UHUGEINT: {
|
|
614
|
+
const lo = ffiRead.u64(dataPtr, row * 16);
|
|
615
|
+
const hi = ffiRead.u64(dataPtr, row * 16 + 8);
|
|
616
|
+
const value = (BigInt(hi) << 64n) | BigInt(lo);
|
|
617
|
+
return value.toString();
|
|
618
|
+
}
|
|
619
|
+
|
|
595
620
|
case DUCKDB_TYPE.DECIMAL: {
|
|
596
621
|
// Read based on internal type, divide by 10^scale, return as string
|
|
597
622
|
const scale = col?.decimalScale || 0;
|
|
@@ -649,6 +674,39 @@ class Connection {
|
|
|
649
674
|
(frac > 0 ? `.${String(frac).padStart(6,'0').replace(/0+$/, '')}` : '');
|
|
650
675
|
}
|
|
651
676
|
|
|
677
|
+
case DUCKDB_TYPE.TIME_NS: {
|
|
678
|
+
const ns = ffiRead.i64(dataPtr, row * 8);
|
|
679
|
+
const totalUs = Number(ns / 1000n);
|
|
680
|
+
const subUs = Number(ns % 1000n);
|
|
681
|
+
const totalSec = Math.floor(totalUs / 1000000);
|
|
682
|
+
const h = Math.floor(totalSec / 3600);
|
|
683
|
+
const m = Math.floor((totalSec % 3600) / 60);
|
|
684
|
+
const s = totalSec % 60;
|
|
685
|
+
const fracUs = totalUs % 1000000;
|
|
686
|
+
const fracNs = fracUs * 1000 + subUs;
|
|
687
|
+
return `${String(h).padStart(2,'0')}:${String(m).padStart(2,'0')}:${String(s).padStart(2,'0')}` +
|
|
688
|
+
(fracNs > 0 ? `.${String(fracNs).padStart(9,'0').replace(/0+$/, '')}` : '');
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
case DUCKDB_TYPE.TIME_TZ: {
|
|
692
|
+
// Stored as uint64: upper 40 bits = microseconds, lower 24 bits = offset + 86399
|
|
693
|
+
const bits = ffiRead.u64(dataPtr, row * 8);
|
|
694
|
+
const us = Number(bits >> 24n);
|
|
695
|
+
const offsetSec = Number(bits & 0xFFFFFFn) - 86399;
|
|
696
|
+
const totalSec = Math.floor(us / 1000000);
|
|
697
|
+
const h = Math.floor(totalSec / 3600);
|
|
698
|
+
const m = Math.floor((totalSec % 3600) / 60);
|
|
699
|
+
const s = totalSec % 60;
|
|
700
|
+
const frac = us % 1000000;
|
|
701
|
+
const absOff = Math.abs(offsetSec);
|
|
702
|
+
const offH = Math.floor(absOff / 3600);
|
|
703
|
+
const offM = Math.floor((absOff % 3600) / 60);
|
|
704
|
+
const sign = offsetSec >= 0 ? '+' : '-';
|
|
705
|
+
return `${String(h).padStart(2,'0')}:${String(m).padStart(2,'0')}:${String(s).padStart(2,'0')}` +
|
|
706
|
+
(frac > 0 ? `.${String(frac).padStart(6,'0').replace(/0+$/, '')}` : '') +
|
|
707
|
+
`${sign}${String(offH).padStart(2,'0')}:${String(offM).padStart(2,'0')}`;
|
|
708
|
+
}
|
|
709
|
+
|
|
652
710
|
case DUCKDB_TYPE.UUID:
|
|
653
711
|
return readUUID(dataPtr, row);
|
|
654
712
|
|
|
@@ -755,6 +813,27 @@ class Connection {
|
|
|
755
813
|
return obj;
|
|
756
814
|
}
|
|
757
815
|
|
|
816
|
+
case DUCKDB_TYPE.ARRAY: {
|
|
817
|
+
// Fixed-size array: child elements are contiguous, arraySize elements per row
|
|
818
|
+
if (!vec) return null;
|
|
819
|
+
const arraySize = col?.arraySize || 0;
|
|
820
|
+
const childVec = lib.duckdb_array_vector_get_child(vec);
|
|
821
|
+
const childData = lib.duckdb_vector_get_data(childVec);
|
|
822
|
+
const childValidity = lib.duckdb_vector_get_validity(childVec);
|
|
823
|
+
const childType = col?.childType || DUCKDB_TYPE.VARCHAR;
|
|
824
|
+
const baseIdx = row * arraySize;
|
|
825
|
+
const result = [];
|
|
826
|
+
for (let i = 0; i < arraySize; i++) {
|
|
827
|
+
const childRow = baseIdx + i;
|
|
828
|
+
if (!isValid(childValidity, childRow)) {
|
|
829
|
+
result.push(null);
|
|
830
|
+
} else {
|
|
831
|
+
result.push(this.#readValue(childData, childRow, childType, null, childVec));
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
return result;
|
|
835
|
+
}
|
|
836
|
+
|
|
758
837
|
case DUCKDB_TYPE.UNION:
|
|
759
838
|
case DUCKDB_TYPE.BIT:
|
|
760
839
|
return null; // Rarely used types
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rip-lang/db",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.82",
|
|
4
4
|
"description": "DuckDB server with official DuckDB UI - pure Bun FFI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "db.rip",
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
},
|
|
14
14
|
"scripts": {
|
|
15
15
|
"start": "rip db.rip",
|
|
16
|
-
"dev": "rip db.rip :memory:"
|
|
16
|
+
"dev": "rip db.rip :memory:",
|
|
17
|
+
"test": "bun test --preload ../../rip-loader.js test/"
|
|
17
18
|
},
|
|
18
19
|
"keywords": [
|
|
19
20
|
"db",
|
|
@@ -38,7 +39,7 @@
|
|
|
38
39
|
"author": "Steve Shreeve <steve.shreeve@gmail.com>",
|
|
39
40
|
"license": "MIT",
|
|
40
41
|
"dependencies": {
|
|
41
|
-
"rip-lang": ">=3.13.
|
|
42
|
+
"rip-lang": ">=3.13.94",
|
|
42
43
|
"@rip-lang/server": ">=1.3.0"
|
|
43
44
|
},
|
|
44
45
|
"files": [
|