quiverdb 0.9.5 → 0.9.7

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/src/read.ts CHANGED
@@ -1,4 +1,3 @@
1
- import { ptr } from "bun:ffi";
2
1
  import { Database } from "./database.ts";
3
2
  import { check } from "./errors.ts";
4
3
  import {
@@ -33,8 +32,8 @@ Database.prototype.readScalarIntegers = function (
33
32
  this._handle,
34
33
  collBuf.buf,
35
34
  attrBuf.buf,
36
- outValues.ptr,
37
- outCount.ptr,
35
+ outValues.buf,
36
+ outCount.buf,
38
37
  ),
39
38
  );
40
39
  const count = readUint64Out(outCount);
@@ -60,8 +59,8 @@ Database.prototype.readScalarFloats = function (
60
59
  this._handle,
61
60
  collBuf.buf,
62
61
  attrBuf.buf,
63
- outValues.ptr,
64
- outCount.ptr,
62
+ outValues.buf,
63
+ outCount.buf,
65
64
  ),
66
65
  );
67
66
  const count = readUint64Out(outCount);
@@ -87,8 +86,8 @@ Database.prototype.readScalarStrings = function (
87
86
  this._handle,
88
87
  collBuf.buf,
89
88
  attrBuf.buf,
90
- outValues.ptr,
91
- outCount.ptr,
89
+ outValues.buf,
90
+ outCount.buf,
92
91
  ),
93
92
  );
94
93
  const count = readUint64Out(outCount);
@@ -111,17 +110,15 @@ Database.prototype.readScalarIntegerById = function (
111
110
  const collBuf = toCString(collection);
112
111
  const attrBuf = toCString(attribute);
113
112
  const outValBuf = new Uint8Array(8);
114
- const outValPtr = ptr(outValBuf);
115
113
  const outHasBuf = new Uint8Array(4);
116
- const outHasPtr = ptr(outHasBuf);
117
114
  check(
118
115
  lib.quiver_database_read_scalar_integer_by_id(
119
116
  this._handle,
120
117
  collBuf.buf,
121
118
  attrBuf.buf,
122
119
  BigInt(id),
123
- outValPtr,
124
- outHasPtr,
120
+ outValBuf,
121
+ outHasBuf,
125
122
  ),
126
123
  );
127
124
  if (new DataView(outHasBuf.buffer).getInt32(0, true) === 0) return null;
@@ -138,17 +135,15 @@ Database.prototype.readScalarFloatById = function (
138
135
  const collBuf = toCString(collection);
139
136
  const attrBuf = toCString(attribute);
140
137
  const outValBuf = new Uint8Array(8);
141
- const outValPtr = ptr(outValBuf);
142
138
  const outHasBuf = new Uint8Array(4);
143
- const outHasPtr = ptr(outHasBuf);
144
139
  check(
145
140
  lib.quiver_database_read_scalar_float_by_id(
146
141
  this._handle,
147
142
  collBuf.buf,
148
143
  attrBuf.buf,
149
144
  BigInt(id),
150
- outValPtr,
151
- outHasPtr,
145
+ outValBuf,
146
+ outHasBuf,
152
147
  ),
153
148
  );
154
149
  if (new DataView(outHasBuf.buffer).getInt32(0, true) === 0) return null;
@@ -166,15 +161,14 @@ Database.prototype.readScalarStringById = function (
166
161
  const attrBuf = toCString(attribute);
167
162
  const outValue = allocPtrOut();
168
163
  const outHasBuf = new Uint8Array(4);
169
- const outHasPtr = ptr(outHasBuf);
170
164
  check(
171
165
  lib.quiver_database_read_scalar_string_by_id(
172
166
  this._handle,
173
167
  collBuf.buf,
174
168
  attrBuf.buf,
175
169
  BigInt(id),
176
- outValue.ptr,
177
- outHasPtr,
170
+ outValue.buf,
171
+ outHasBuf,
178
172
  ),
179
173
  );
180
174
  if (new DataView(outHasBuf.buffer).getInt32(0, true) === 0) return null;
@@ -190,7 +184,7 @@ Database.prototype.readElementIds = function (this: Database, collection: string
190
184
  const collBuf = toCString(collection);
191
185
  const outIds = allocPtrOut();
192
186
  const outCount = allocUint64Out();
193
- check(lib.quiver_database_read_element_ids(this._handle, collBuf.buf, outIds.ptr, outCount.ptr));
187
+ check(lib.quiver_database_read_element_ids(this._handle, collBuf.buf, outIds.buf, outCount.buf));
194
188
  const count = readUint64Out(outCount);
195
189
  if (count === 0) return [];
196
190
  const arrPtr = readPtrOut(outIds);
@@ -218,9 +212,9 @@ function readBulkIntegers(
218
212
  handle,
219
213
  collBuf.buf,
220
214
  attrBuf.buf,
221
- outVectors.ptr,
222
- outSizes.ptr,
223
- outCount.ptr,
215
+ outVectors.buf,
216
+ outSizes.buf,
217
+ outCount.buf,
224
218
  ),
225
219
  );
226
220
  const count = readUint64Out(outCount);
@@ -254,9 +248,9 @@ function readBulkFloats(
254
248
  handle,
255
249
  collBuf.buf,
256
250
  attrBuf.buf,
257
- outVectors.ptr,
258
- outSizes.ptr,
259
- outCount.ptr,
251
+ outVectors.buf,
252
+ outSizes.buf,
253
+ outCount.buf,
260
254
  ),
261
255
  );
262
256
  const count = readUint64Out(outCount);
@@ -290,9 +284,9 @@ function readBulkStrings(
290
284
  handle,
291
285
  collBuf.buf,
292
286
  attrBuf.buf,
293
- outVectors.ptr,
294
- outSizes.ptr,
295
- outCount.ptr,
287
+ outVectors.buf,
288
+ outSizes.buf,
289
+ outCount.buf,
296
290
  ),
297
291
  );
298
292
  const count = readUint64Out(outCount);
@@ -408,8 +402,8 @@ function readByIdIntegers(
408
402
  collBuf.buf,
409
403
  attrBuf.buf,
410
404
  BigInt(id),
411
- outValues.ptr,
412
- outCount.ptr,
405
+ outValues.buf,
406
+ outCount.buf,
413
407
  ),
414
408
  );
415
409
  const count = readUint64Out(outCount);
@@ -438,8 +432,8 @@ function readByIdFloats(
438
432
  collBuf.buf,
439
433
  attrBuf.buf,
440
434
  BigInt(id),
441
- outValues.ptr,
442
- outCount.ptr,
435
+ outValues.buf,
436
+ outCount.buf,
443
437
  ),
444
438
  );
445
439
  const count = readUint64Out(outCount);
@@ -468,8 +462,8 @@ function readByIdStrings(
468
462
  collBuf.buf,
469
463
  attrBuf.buf,
470
464
  BigInt(id),
471
- outValues.ptr,
472
- outCount.ptr,
465
+ outValues.buf,
466
+ outCount.buf,
473
467
  ),
474
468
  );
475
469
  const count = readUint64Out(outCount);
@@ -1,6 +1,6 @@
1
1
  import { CString, type Pointer, ptr, read, toArrayBuffer } from "bun:ffi";
2
2
  import { Database } from "./database.ts";
3
- import { check } from "./errors.ts";
3
+ import { check, QuiverError } from "./errors.ts";
4
4
  import {
5
5
  allocNativeFloat64,
6
6
  allocNativeInt64,
@@ -18,15 +18,16 @@ import {
18
18
  toCString,
19
19
  } from "./ffi-helpers.ts";
20
20
  import { getSymbols } from "./loader.ts";
21
- import type { Allocation } from "./types.ts";
21
+ import {
22
+ type Allocation,
23
+ DATA_TYPE_DATE_TIME,
24
+ DATA_TYPE_FLOAT,
25
+ DATA_TYPE_INTEGER,
26
+ DATA_TYPE_STRING,
27
+ } from "./types.ts";
22
28
 
23
29
  export type TimeSeriesData = Record<string, (number | string)[]>;
24
30
 
25
- const DATA_TYPE_INTEGER = 0;
26
- const DATA_TYPE_FLOAT = 1;
27
- const DATA_TYPE_STRING = 2;
28
- const DATA_TYPE_DATE_TIME = 3;
29
-
30
31
  Database.prototype.readTimeSeriesGroup = function (
31
32
  this: Database,
32
33
  collection: string,
@@ -48,11 +49,11 @@ Database.prototype.readTimeSeriesGroup = function (
48
49
  collBuf.buf,
49
50
  grpBuf.buf,
50
51
  BigInt(id),
51
- outNames.ptr,
52
- outTypes.ptr,
53
- outData.ptr,
54
- outColCount.ptr,
55
- outRowCount.ptr,
52
+ outNames.buf,
53
+ outTypes.buf,
54
+ outData.buf,
55
+ outColCount.buf,
56
+ outRowCount.buf,
56
57
  ),
57
58
  );
58
59
 
@@ -96,6 +97,64 @@ Database.prototype.readTimeSeriesGroup = function (
96
97
  return result;
97
98
  };
98
99
 
100
+ Database.prototype.readTimeSeriesRow = function (
101
+ this: Database,
102
+ collection: string,
103
+ group: string,
104
+ attribute: string,
105
+ dateTime: string,
106
+ ): (number | string | null)[] {
107
+ const lib = getSymbols();
108
+ const collBuf = toCString(collection);
109
+ const grpBuf = toCString(group);
110
+ const attrBuf = toCString(attribute);
111
+ const dtBuf = toCString(dateTime);
112
+ const outDataType = new Uint8Array(4);
113
+ const outValues = allocPtrOut();
114
+ const outCount = allocUint64Out();
115
+
116
+ check(
117
+ lib.quiver_database_read_time_series_row(
118
+ this._handle,
119
+ collBuf.buf,
120
+ grpBuf.buf,
121
+ attrBuf.buf,
122
+ dtBuf.buf,
123
+ outDataType,
124
+ outValues.buf,
125
+ outCount.buf,
126
+ ),
127
+ );
128
+
129
+ const count = readUint64Out(outCount);
130
+ const valuesPtr = readPtrOut(outValues);
131
+ if (count === 0 || !valuesPtr) return [];
132
+
133
+ const dataType = new DataView(outDataType.buffer).getInt32(0, true);
134
+ switch (dataType) {
135
+ case DATA_TYPE_INTEGER: {
136
+ const result = decodeInt64Array(valuesPtr, count);
137
+ lib.quiver_database_free_integer_array(valuesPtr);
138
+ return result;
139
+ }
140
+ case DATA_TYPE_FLOAT: {
141
+ const result = decodeFloat64Array(valuesPtr, count);
142
+ lib.quiver_database_free_float_array(valuesPtr);
143
+ return result;
144
+ }
145
+ default: {
146
+ // STRING or DATE_TIME; NULL entries mark elements with no data
147
+ const result: (string | null)[] = new Array(count);
148
+ for (let i = 0; i < count; i++) {
149
+ const strPtr = read.ptr(valuesPtr as Pointer, i * 8);
150
+ result[i] = strPtr === 0 ? null : new CString(strPtr as Pointer).toString();
151
+ }
152
+ lib.quiver_database_free_string_array(valuesPtr, BigInt(count));
153
+ return result;
154
+ }
155
+ }
156
+ };
157
+
99
158
  Database.prototype.updateTimeSeriesGroup = function (
100
159
  this: Database,
101
160
  collection: string,
@@ -127,6 +186,26 @@ Database.prototype.updateTimeSeriesGroup = function (
127
186
 
128
187
  const columnCount = entries.length;
129
188
  const rowCount = entries[0][1].length;
189
+
190
+ // Validate before marshalling: a jagged column would desync the parallel
191
+ // arrays the C API reads against row_count, and a zero-length column (with
192
+ // columns present) would otherwise marshal a null data pointer that the C API
193
+ // dereferences. Named-but-empty columns are a caller mistake -- pass {} to
194
+ // clear the group instead.
195
+ for (const [name, values] of entries) {
196
+ if (values.length !== rowCount) {
197
+ throw new QuiverError(
198
+ `Cannot updateTimeSeriesGroup: column '${name}' has length ${values.length} but expected ${rowCount}`,
199
+ );
200
+ }
201
+ }
202
+ if (rowCount === 0) {
203
+ const names = entries.map(([name]) => name).join(", ");
204
+ throw new QuiverError(
205
+ `Cannot updateTimeSeriesGroup: columns [${names}] contain no rows; pass {} to clear the group`,
206
+ );
207
+ }
208
+
130
209
  const keepalive: Allocation[] = [];
131
210
 
132
211
  // Build column names as native string array
@@ -142,10 +221,7 @@ Database.prototype.updateTimeSeriesGroup = function (
142
221
  for (let c = 0; c < columnCount; c++) {
143
222
  const values = entries[c][1];
144
223
 
145
- if (values.length === 0) {
146
- typesDv.setInt32(c * 4, DATA_TYPE_STRING, true);
147
- dataPtrs.push(null);
148
- } else if (typeof values[0] === "string") {
224
+ if (typeof values[0] === "string") {
149
225
  typesDv.setInt32(c * 4, DATA_TYPE_STRING, true);
150
226
  const { table, keepalive: strPtrs } = allocNativeStringArray(values as string[]);
151
227
  keepalive.push(table, ...strPtrs);
@@ -177,15 +253,76 @@ Database.prototype.updateTimeSeriesGroup = function (
177
253
  collBuf.buf,
178
254
  grpBuf.buf,
179
255
  BigInt(id),
180
- namesTable.ptr,
181
- typesAlloc.ptr,
182
- dataTable.ptr,
256
+ namesTable.buf,
257
+ typesAlloc.buf,
258
+ dataTable.buf,
183
259
  BigInt(columnCount),
184
260
  BigInt(rowCount),
185
261
  ),
186
262
  );
187
263
  };
188
264
 
265
+ Database.prototype.addTimeSeriesRow = function (
266
+ this: Database,
267
+ collection: string,
268
+ group: string,
269
+ id: number,
270
+ row: Record<string, number | bigint | string>,
271
+ ): void {
272
+ const lib = getSymbols();
273
+ const collBuf = toCString(collection);
274
+ const grpBuf = toCString(group);
275
+ const entries = Object.entries(row);
276
+ const columnCount = entries.length;
277
+ const keepalive: Allocation[] = [];
278
+
279
+ const colNames = entries.map(([name]) => name);
280
+ const { table: namesTable, keepalive: namesPtrs } = allocNativeStringArray(colNames);
281
+ keepalive.push(namesTable, ...namesPtrs);
282
+
283
+ const typesBuf = new Uint8Array(columnCount * 4);
284
+ const typesDv = new DataView(typesBuf.buffer);
285
+ const dataPtrs: (Pointer | null)[] = [];
286
+
287
+ for (let c = 0; c < columnCount; c++) {
288
+ const value = entries[c][1];
289
+ if (typeof value === "string") {
290
+ typesDv.setInt32(c * 4, DATA_TYPE_STRING, true);
291
+ const { table, keepalive: strPtrs } = allocNativeStringArray([value]);
292
+ keepalive.push(table, ...strPtrs);
293
+ dataPtrs.push(table.ptr);
294
+ } else if (typeof value === "bigint" || Number.isInteger(value)) {
295
+ typesDv.setInt32(c * 4, DATA_TYPE_INTEGER, true);
296
+ const p = allocNativeInt64([value]);
297
+ keepalive.push(p);
298
+ dataPtrs.push(p.ptr);
299
+ } else {
300
+ typesDv.setInt32(c * 4, DATA_TYPE_FLOAT, true);
301
+ const p = allocNativeFloat64([value as number]);
302
+ keepalive.push(p);
303
+ dataPtrs.push(p.ptr);
304
+ }
305
+ }
306
+
307
+ const typesAlloc: Allocation = { ptr: ptr(typesBuf), buf: typesBuf };
308
+ keepalive.push(typesAlloc);
309
+ const dataTable = allocNativePtrTable(dataPtrs);
310
+ keepalive.push(dataTable);
311
+
312
+ check(
313
+ lib.quiver_database_add_time_series_row(
314
+ this._handle,
315
+ collBuf.buf,
316
+ grpBuf.buf,
317
+ BigInt(id),
318
+ namesTable.buf,
319
+ typesAlloc.buf,
320
+ dataTable.buf,
321
+ BigInt(columnCount),
322
+ ),
323
+ );
324
+ };
325
+
189
326
  Database.prototype.hasTimeSeriesFiles = function (this: Database, collection: string): boolean {
190
327
  const lib = getSymbols();
191
328
  const collBuf = toCString(collection);
@@ -206,8 +343,8 @@ Database.prototype.listTimeSeriesFilesColumns = function (
206
343
  lib.quiver_database_list_time_series_files_columns(
207
344
  this._handle,
208
345
  collBuf.buf,
209
- outColumns.ptr,
210
- outCount.ptr,
346
+ outColumns.buf,
347
+ outCount.buf,
211
348
  ),
212
349
  );
213
350
  const count = readUint64Out(outCount);
@@ -231,9 +368,9 @@ Database.prototype.readTimeSeriesFiles = function (
231
368
  lib.quiver_database_read_time_series_files(
232
369
  this._handle,
233
370
  collBuf.buf,
234
- outColumns.ptr,
235
- outPaths.ptr,
236
- outCount.ptr,
371
+ outColumns.buf,
372
+ outPaths.buf,
373
+ outCount.buf,
237
374
  ),
238
375
  );
239
376
  const count = readUint64Out(outCount);
@@ -283,8 +420,8 @@ Database.prototype.updateTimeSeriesFiles = function (
283
420
  lib.quiver_database_update_time_series_files(
284
421
  this._handle,
285
422
  collBuf.buf,
286
- colsTable.ptr,
287
- pathsTable.ptr,
423
+ colsTable.buf,
424
+ pathsTable.buf,
288
425
  BigInt(entries.length),
289
426
  ),
290
427
  );
@@ -1,4 +1,3 @@
1
- import { ptr } from "bun:ffi";
2
1
  import { Database } from "./database.ts";
3
2
  import { check } from "./errors.ts";
4
3
  import { getSymbols } from "./loader.ts";
@@ -21,7 +20,6 @@ Database.prototype.rollback = function (this: Database): void {
21
20
  Database.prototype.inTransaction = function (this: Database): boolean {
22
21
  const lib = getSymbols();
23
22
  const outBuf = new Uint8Array(4);
24
- const outPtr = ptr(outBuf);
25
- check(lib.quiver_database_in_transaction(this._handle, outPtr));
23
+ check(lib.quiver_database_in_transaction(this._handle, outBuf));
26
24
  return new DataView(outBuf.buffer).getInt32(0, true) !== 0;
27
25
  };
package/src/types.ts CHANGED
@@ -1,5 +1,26 @@
1
1
  import type { Pointer } from "bun:ffi";
2
2
 
3
+ // Mirrors quiver_data_type_t in the C API
4
+ export const DATA_TYPE_INTEGER = 0;
5
+ export const DATA_TYPE_FLOAT = 1;
6
+ export const DATA_TYPE_STRING = 2;
7
+ export const DATA_TYPE_DATE_TIME = 3;
8
+ export const DATA_TYPE_NULL = 4;
9
+
10
+ // Mirrors quiver_log_level_t in the C API
11
+ export const LOG_LEVEL_DEBUG = 0;
12
+ export const LOG_LEVEL_INFO = 1;
13
+ export const LOG_LEVEL_WARN = 2;
14
+ export const LOG_LEVEL_ERROR = 3;
15
+ export const LOG_LEVEL_OFF = 4;
16
+
17
+ /** Options for opening or creating a database. */
18
+ export type DatabaseOptions = {
19
+ readOnly?: boolean;
20
+ /** A LOG_LEVEL_* constant; defaults to LOG_LEVEL_INFO. */
21
+ consoleLevel?: number;
22
+ };
23
+
3
24
  export type ScalarValue = number | bigint | string | null;
4
25
  export type ArrayValue = number[] | bigint[] | string[];
5
26
  export type Value = ScalarValue | ArrayValue;