@rip-lang/db 0.10.0 → 1.0.2

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.
@@ -2,8 +2,6 @@
2
2
  #
3
3
  # Implements the binary serialization format used by DuckDB's official UI.
4
4
  # This allows rip-db to serve responses that the DuckDB UI can understand.
5
- #
6
- # Protocol spec: See PROTOCOL.md in this directory
7
5
 
8
6
  # ==============================================================================
9
7
  # LogicalTypeId - matches DuckDB's internal type IDs
@@ -46,6 +44,9 @@ export LogicalTypeId =
46
44
  UNION: 107
47
45
  ARRAY: 108
48
46
 
47
+ # Shared TextEncoder instance (avoid allocating per call)
48
+ textEncoder = new TextEncoder()
49
+
49
50
  # ==============================================================================
50
51
  # BinarySerializer - writes the DuckDB binary format
51
52
  # ==============================================================================
@@ -106,8 +107,7 @@ export class BinarySerializer
106
107
  break
107
108
 
108
109
  writeString: (str) ->
109
- encoder = new TextEncoder()
110
- bytes = encoder.encode str
110
+ bytes = textEncoder.encode str
111
111
  @writeVarInt bytes.length
112
112
  @buffer.push ...bytes
113
113
 
@@ -213,21 +213,23 @@ serializeType = (s, column) ->
213
213
  serializeDataChunk = (s, columns, rows) ->
214
214
  s.writePropertyVarInt 100, rows.length
215
215
  s.writeList 101, columns, (s, col, colIdx) ->
216
- values = rows.map (row) -> row[colIdx] ? row[col.name]
216
+ values = rows.map (row) -> row[colIdx]
217
217
  serializeVector s, col, values
218
218
  s.writeEndMarker()
219
219
 
220
220
  serializeVector = (s, column, values) ->
221
221
  typeId = mapDuckDBType column.type
222
222
  hasNulls = values.some (v) -> v is null or v is undefined
223
- allValid = if hasNulls then 0 else 1
224
223
 
224
+ # allValid flag: 0 = all valid (no bitmap), non-zero = has validity bitmap
225
+ # The deserializer reads field 101 ONLY when allValid is truthy
225
226
  s.writeFieldId 100
226
- s.writeUint8 allValid
227
-
228
- if not allValid
227
+ if hasNulls
228
+ s.writeUint8 1 # Has validity bitmap
229
229
  s.writeFieldId 101
230
230
  s.writeData createValidityBitmap values
231
+ else
232
+ s.writeUint8 0 # All valid, no bitmap needed
231
233
 
232
234
  switch typeId
233
235
  when LogicalTypeId.VARCHAR, LogicalTypeId.CHAR
@@ -329,6 +331,16 @@ serializeVector = (s, column, values) ->
329
331
  dv.setBigInt64 i * 8, micros, true
330
332
  s.writeData bytes
331
333
 
334
+ when LogicalTypeId.UUID
335
+ s.writeFieldId 102
336
+ bytes = new Uint8Array values.length * 16
337
+ dv = new DataView bytes.buffer
338
+ for v, i in values
339
+ { lo, hi } = uuidToHugeint(v)
340
+ dv.setBigUint64 i * 16, lo, true
341
+ dv.setBigInt64 i * 16 + 8, hi, true
342
+ s.writeData bytes
343
+
332
344
  else
333
345
  s.writeList 102, values, (s, v) -> s.writeString String(v ? '')
334
346
 
@@ -339,10 +351,11 @@ serializeVector = (s, column, values) ->
339
351
  # ==============================================================================
340
352
 
341
353
  createValidityBitmap = (values) ->
342
- byteCount = Math.ceil values.length / 8
354
+ # Must be uint64-aligned (8-byte chunks) — the UI reads validity with getBigUint64
355
+ byteCount = Math.ceil(values.length / 64) * 8
343
356
  bitmap = new Uint8Array byteCount
344
357
  for v, i in values
345
- if v? and v isnt null
358
+ if v?
346
359
  byteIdx = Math.floor i / 8
347
360
  bitIdx = i % 8
348
361
  bitmap[byteIdx] |= 1 << bitIdx
@@ -370,6 +383,14 @@ timestampToMicros = (value) ->
370
383
  else
371
384
  0n
372
385
 
386
+ uuidToHugeint = (uuid) ->
387
+ return { lo: 0n, hi: 0n } unless uuid
388
+ hex = String(uuid).replace(/-/g, '')
389
+ full = BigInt "0x#{hex}"
390
+ hi = (full >> 64n) ^ (1n << 63n) # XOR sign bit for DuckDB sorting
391
+ lo = full & ((1n << 64n) - 1n)
392
+ { lo, hi }
393
+
373
394
  mapDuckDBType = (typeName) ->
374
395
  return LogicalTypeId.VARCHAR unless typeName
375
396
  upper = String(typeName).toUpperCase()
@@ -398,7 +419,7 @@ mapDuckDBType = (typeName) ->
398
419
  when 'INTERVAL' then LogicalTypeId.INTERVAL
399
420
  when 'JSON' then LogicalTypeId.VARCHAR
400
421
  else
401
- if upper.startsWith 'DECIMAL' then LogicalTypeId.DOUBLE
422
+ if upper.startsWith 'DECIMAL' then LogicalTypeId.VARCHAR # Serialize as string to preserve exact precision
402
423
  else if upper.startsWith 'VARCHAR' then LogicalTypeId.VARCHAR
403
424
  else if upper.startsWith 'CHAR' then LogicalTypeId.CHAR
404
425
  else LogicalTypeId.VARCHAR
@@ -520,6 +541,6 @@ export inferType = (value) ->
520
541
  else if /^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}/.test value then 'TIMESTAMP'
521
542
  else 'VARCHAR'
522
543
  when 'object'
523
- if value instanceof Date then 'TIMESTAMP'
544
+ if value instanceof Date then 'TIMESTAMPTZ'
524
545
  else 'VARCHAR'
525
546
  else 'VARCHAR'