quiverdb 0.13.0 → 0.13.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.
- package/libs/linux-aarch64/libquiver.so +0 -0
- package/libs/linux-aarch64/libquiver.so.0 +0 -0
- package/libs/linux-aarch64/libquiver_c.so +0 -0
- package/libs/linux-x86_64/libquiver.so +0 -0
- package/libs/linux-x86_64/libquiver.so.0 +0 -0
- package/libs/linux-x86_64/libquiver_c.so +0 -0
- package/libs/macos-aarch64/libquiver.0.dylib +0 -0
- package/libs/macos-aarch64/libquiver_c.dylib +0 -0
- package/libs/windows-x86_64/libquiver.dll +0 -0
- package/libs/windows-x86_64/libquiver_c.dll +0 -0
- package/package.json +4 -2
- package/src/group-columns.ts +8 -6
- package/src/loader.ts +13 -2
- package/src/sandbox-api.ts +56 -15
- package/src/time-series.ts +8 -6
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "quiverdb",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
],
|
|
21
21
|
"scripts": {
|
|
22
22
|
"test": "bun test test",
|
|
23
|
+
"typecheck": "tsc --noEmit",
|
|
23
24
|
"lint": "biome check",
|
|
24
25
|
"format": "biome format --write"
|
|
25
26
|
},
|
|
@@ -29,6 +30,7 @@
|
|
|
29
30
|
},
|
|
30
31
|
"devDependencies": {
|
|
31
32
|
"@biomejs/biome": "^2.4.6",
|
|
32
|
-
"@types/bun": "latest"
|
|
33
|
+
"@types/bun": "latest",
|
|
34
|
+
"typescript": "7.0.2"
|
|
33
35
|
}
|
|
34
36
|
}
|
package/src/group-columns.ts
CHANGED
|
@@ -42,11 +42,11 @@ export type TimeSeriesData = Record<string, (number | string | null)[]>;
|
|
|
42
42
|
* (quiver_database_update_{time_series,vector,set}_group and their _by_label forms). The 4th
|
|
43
43
|
* argument addresses the element: an id for the by-id forms, a NUL-terminated label otherwise.
|
|
44
44
|
*/
|
|
45
|
-
type ColumnUpdateFn = (
|
|
45
|
+
type ColumnUpdateFn<Key extends number | string> = (
|
|
46
46
|
db: NativePointer,
|
|
47
47
|
collection: Uint8Array,
|
|
48
48
|
group: Uint8Array,
|
|
49
|
-
key:
|
|
49
|
+
key: Key extends string ? Uint8Array : bigint,
|
|
50
50
|
names: Uint8Array | null,
|
|
51
51
|
types: Uint8Array | null,
|
|
52
52
|
data: Uint8Array | null,
|
|
@@ -90,18 +90,20 @@ export function numericCells(
|
|
|
90
90
|
*
|
|
91
91
|
* Pass `{}` (no columns) to clear the group.
|
|
92
92
|
*/
|
|
93
|
-
export function updateGroupColumns(
|
|
93
|
+
export function updateGroupColumns<Key extends number | string>(
|
|
94
94
|
handle: NativePointer,
|
|
95
95
|
caller: string,
|
|
96
|
-
update: ColumnUpdateFn
|
|
96
|
+
update: ColumnUpdateFn<Key>,
|
|
97
97
|
collection: string,
|
|
98
98
|
group: string,
|
|
99
|
-
key:
|
|
99
|
+
key: Key,
|
|
100
100
|
data: GroupColumns,
|
|
101
101
|
): void {
|
|
102
102
|
const collBuf = toCString(collection);
|
|
103
103
|
const grpBuf = toCString(group);
|
|
104
|
-
const keyArg = typeof key === "string" ? toCString(key).buf : BigInt(key)
|
|
104
|
+
const keyArg = (typeof key === "string" ? toCString(key).buf : BigInt(key)) as Key extends string
|
|
105
|
+
? Uint8Array
|
|
106
|
+
: bigint;
|
|
105
107
|
const entries = Object.entries(data);
|
|
106
108
|
|
|
107
109
|
if (entries.length === 0) {
|
package/src/loader.ts
CHANGED
|
@@ -288,7 +288,17 @@ function initLibrary(): QuiverLib {
|
|
|
288
288
|
}
|
|
289
289
|
}
|
|
290
290
|
|
|
291
|
-
// Tier 2:
|
|
291
|
+
// Tier 2: Standalone Bun executables ship native siblings beside the executable.
|
|
292
|
+
const executableDir = dirname(process.execPath);
|
|
293
|
+
if (existsSync(join(executableDir, C_API_LIB))) {
|
|
294
|
+
try {
|
|
295
|
+
return openLibrary(executableDir);
|
|
296
|
+
} catch (e) {
|
|
297
|
+
lastError = e;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Tier 3: Dev mode -- walk up directories looking for build/bin/.
|
|
292
302
|
for (const dir of getSearchPaths()) {
|
|
293
303
|
try {
|
|
294
304
|
return openLibrary(dir);
|
|
@@ -297,7 +307,7 @@ function initLibrary(): QuiverLib {
|
|
|
297
307
|
}
|
|
298
308
|
}
|
|
299
309
|
|
|
300
|
-
// Tier
|
|
310
|
+
// Tier 4: System PATH fallback -- the core lib is expected to be discoverable
|
|
301
311
|
// on PATH alongside the C API lib.
|
|
302
312
|
try {
|
|
303
313
|
return dlopen(C_API_LIB, allSymbols);
|
|
@@ -307,6 +317,7 @@ function initLibrary(): QuiverLib {
|
|
|
307
317
|
|
|
308
318
|
const searched = [
|
|
309
319
|
join(__dirname, "..", "libs", platformKey),
|
|
320
|
+
executableDir,
|
|
310
321
|
...getSearchPaths(),
|
|
311
322
|
"system PATH",
|
|
312
323
|
].join(", ");
|
package/src/sandbox-api.ts
CHANGED
|
@@ -103,12 +103,14 @@ midnight.
|
|
|
103
103
|
and \`dofile\`/\`loadfile\` are removed (string-form \`load\` stays for source text; a precompiled
|
|
104
104
|
binary chunk is refused). Integer division is the Lua 5.4 \`//\` operator — a language operator,
|
|
105
105
|
unrelated to \`math\`. No \`io\` does **not** mean
|
|
106
|
-
a data file on disk is out of reach:
|
|
107
|
-
the
|
|
106
|
+
a data file on disk is out of reach: use \`db:read_csv\` / \`db:read_csv_stream\` for CSV or
|
|
107
|
+
\`db:read_xlsx\` / \`db:read_xlsx_stream\` for XLSX (see the file reading sections below).
|
|
108
|
+
Never copy, paste, or re-type a data file's contents into
|
|
108
109
|
the script as literals — read the file.
|
|
109
110
|
- **Filesystem sandbox.** Every file-touching operation (\`db:export_csv\`, \`db:import_csv\`,
|
|
110
111
|
\`db:open_file\`, \`db:bin_to_csv\`, \`db:csv_to_bin\`, \`db:validate_migrations\`, \`db:read_csv\`,
|
|
111
|
-
\`db:read_csv_stream\`, \`db:write_csv\`, \`
|
|
112
|
+
\`db:read_csv_stream\`, \`db:write_csv\`, \`db:read_xlsx\`, \`db:read_xlsx_stream\`,
|
|
113
|
+
\`save\` on a file or an expression) resolves
|
|
112
114
|
relative paths against the directory containing the database file and rejects anything outside it
|
|
113
115
|
(subdirectories are fine; \`..\` escapes and outside absolute paths throw \`Cannot <op>: path '...' escapes the
|
|
114
116
|
database directory ...\`). On an in-memory database these operations throw
|
|
@@ -325,10 +327,11 @@ Notes:
|
|
|
325
327
|
\`{ x = nil }\` is identical to \`{}\`; an update/create table that ends up with no attributes
|
|
326
328
|
**throws** (\`...must have at least one scalar attribute\` on create, \`...at least one attribute
|
|
327
329
|
to update\` on update). To leave a column unchanged, omit the key — you cannot set a scalar to
|
|
328
|
-
NULL via the element table
|
|
329
|
-
|
|
330
|
-
\`
|
|
331
|
-
the
|
|
330
|
+
NULL via the element table — nor via \`update_time_series_files\`, keyed by column name too,
|
|
331
|
+
where an omitted column keeps its current value. (\`nil\` → NULL is accepted by
|
|
332
|
+
\`update_relation\` and in query params (not a trailing one), and as a cell in the group writers;
|
|
333
|
+
\`upsert_time_series_row\` replaces the whole row, so a column you leave out (or set to \`nil\`,
|
|
334
|
+
the same thing) is cleared — see Time series.)
|
|
332
335
|
- **\`update_relation\` points one scalar foreign-key relation at another element**, named by the
|
|
333
336
|
target's label. The column is derived from the naming convention —
|
|
334
337
|
\`lowercase(collection_to) .. "_" .. relation_type\`, so
|
|
@@ -557,15 +560,12 @@ singleton table):
|
|
|
557
560
|
db:has_time_series_files(collection) -- boolean
|
|
558
561
|
db:list_time_series_files_columns(collection) -- { "data_file", "metadata_file", ... }
|
|
559
562
|
db:read_time_series_files(collection) -- { data_file = "path", metadata_file = nil, ... }
|
|
560
|
-
db:update_time_series_files(collection, { data_file = "path/to/data.bin" })
|
|
563
|
+
db:update_time_series_files(collection, { data_file = "path/to/data.bin" })
|
|
561
564
|
\`\`\`
|
|
562
565
|
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
changes nothing and clears nothing. A value that is not a string throws. To change one path, read
|
|
567
|
-
the row with \`db:read_time_series_files(collection)\`, change that one entry, and pass the whole
|
|
568
|
-
table back.
|
|
566
|
+
Only the columns you name are written, so the call above leaves \`metadata_file\` alone. A column
|
|
567
|
+
cannot be cleared from Lua here: \`{ metadata_file = nil }\` is \`{}\`, which names nothing — so
|
|
568
|
+
feeding a \`read_time_series_files\` result straight back in is a no-op.
|
|
569
569
|
|
|
570
570
|
---
|
|
571
571
|
|
|
@@ -703,7 +703,7 @@ own) — it throws \`Cannot import_csv: transaction already active\`. Call it ou
|
|
|
703
703
|
|
|
704
704
|
## CSV file reading
|
|
705
705
|
|
|
706
|
-
Read a CSV file from disk directly into Lua
|
|
706
|
+
Read a CSV file from disk directly into Lua, since
|
|
707
707
|
\`io\` is deliberately absent from the sandbox. \`path\` is sandboxed the same way as every other
|
|
708
708
|
file-touching operation (see Critical rules).
|
|
709
709
|
|
|
@@ -789,6 +789,47 @@ end
|
|
|
789
789
|
|
|
790
790
|
---
|
|
791
791
|
|
|
792
|
+
## XLSX file reading
|
|
793
|
+
|
|
794
|
+
Read one worksheet of an XLSX workbook through the same filesystem sandbox as CSV. XLSX only:
|
|
795
|
+
legacy XLS and encrypted workbooks are unsupported. These methods read cells; they do not write
|
|
796
|
+
the workbook or import rows into the database.
|
|
797
|
+
|
|
798
|
+
\`\`\`lua
|
|
799
|
+
local data = db:read_xlsx("input.xlsx", { sheet = "Data", header_row = 1 })
|
|
800
|
+
-- data.header: array of strings; data.rows: array of arrays of strings
|
|
801
|
+
local count = db:read_xlsx_stream("input.xlsx", function(row, index, header)
|
|
802
|
+
-- header is available even on the first callback; index counts emitted data rows.
|
|
803
|
+
-- Only an explicit false stops. No return, nil, 0 and strings all continue.
|
|
804
|
+
end, { sheet = 2 })
|
|
805
|
+
\`\`\`
|
|
806
|
+
|
|
807
|
+
The optional options table accepts exactly \`sheet\` and \`header_row\`. \`sheet\` is an exact
|
|
808
|
+
worksheet name or a positive 1-based integer, default \`1\`. Chart sheets do not count; hidden
|
|
809
|
+
worksheets do. Missing worksheets throw. \`header_row\` is the physical worksheet row number,
|
|
810
|
+
default \`1\`; rows above it are skipped. A missing or completely blank header throws.
|
|
811
|
+
\`header_row = 0\` reads all data rows with no header (\`data.header\` and the callback header are
|
|
812
|
+
\`nil\`). Omit options, pass \`nil\`, or pass \`{}\` to use defaults; unknown keys and wrong
|
|
813
|
+
types throw \`Cannot <operation>: ...\`.
|
|
814
|
+
|
|
815
|
+
Every cell is a string. Text, including leading zeros, Unicode, whitespace and rich-text content,
|
|
816
|
+
is preserved. Numbers keep their stored locale-independent text, booleans are \`"1"\`/\`"0"\`,
|
|
817
|
+
blank cells are \`""\`, and Excel errors such as \`#DIV/0!\` stay text. Formatting is ignored:
|
|
818
|
+
an Excel date stored as \`45292.5\` remains \`"45292.5"\`, and an explicit date stored as text
|
|
819
|
+
stays text. Formulas return their saved cached value, never the expression or a recalculated value.
|
|
820
|
+
A formula with no cache throws, naming the sheet and cell; recalculate and save it in Excel first.
|
|
821
|
+
|
|
822
|
+
Completely blank data rows are skipped. Rows and headers are padded with \`""\` to the worksheet's
|
|
823
|
+
last column containing a value or formula, so column positions stay aligned; formatting-only
|
|
824
|
+
cells do not expand that width. Merged cells contribute only their stored values (normally the
|
|
825
|
+
top-left cell); values are not copied across the merged area. Header-only sheets return no data
|
|
826
|
+
rows; an empty sheet is readable with \`header_row = 0\`.
|
|
827
|
+
|
|
828
|
+
Both forms use the same reader. The callback form returns the number of rows delivered, including
|
|
829
|
+
the row that returned false; callback errors propagate unchanged and handles close on every exit.
|
|
830
|
+
Unlike CSV streaming, XLSX callbacks avoid the full Lua rows table but still load worksheet XML
|
|
831
|
+
and shared strings into memory. For very large files, use CSV streaming instead.
|
|
832
|
+
|
|
792
833
|
## CSV file writing
|
|
793
834
|
|
|
794
835
|
Write a CSV file to disk — the only way to get data out of a script onto disk, since \`io\` is
|
package/src/time-series.ts
CHANGED
|
@@ -149,11 +149,11 @@ Database.prototype.updateTimeSeriesGroupByLabel = function (
|
|
|
149
149
|
* (quiver_database_upsert_time_series_row and its _by_label form). `key` is an id for the by-id
|
|
150
150
|
* form, a NUL-terminated label otherwise.
|
|
151
151
|
*/
|
|
152
|
-
type UpsertRowFn = (
|
|
152
|
+
type UpsertRowFn<Key extends number | string> = (
|
|
153
153
|
db: NativePointer,
|
|
154
154
|
collection: Uint8Array,
|
|
155
155
|
group: Uint8Array,
|
|
156
|
-
key:
|
|
156
|
+
key: Key extends string ? Uint8Array : bigint,
|
|
157
157
|
names: Uint8Array | null,
|
|
158
158
|
types: Uint8Array | null,
|
|
159
159
|
data: Uint8Array | null,
|
|
@@ -165,18 +165,20 @@ type UpsertRowFn = (
|
|
|
165
165
|
* C functions. Shared by upsertTimeSeriesRow / upsertTimeSeriesRowByLabel: they differ only in
|
|
166
166
|
* which C entry point they call and whether `key` is an id or a label.
|
|
167
167
|
*/
|
|
168
|
-
function upsertRowColumns(
|
|
168
|
+
function upsertRowColumns<Key extends number | string>(
|
|
169
169
|
handle: NativePointer,
|
|
170
170
|
caller: string,
|
|
171
|
-
upsert: UpsertRowFn
|
|
171
|
+
upsert: UpsertRowFn<Key>,
|
|
172
172
|
collection: string,
|
|
173
173
|
group: string,
|
|
174
|
-
key:
|
|
174
|
+
key: Key,
|
|
175
175
|
row: Record<string, number | bigint | string | boolean>,
|
|
176
176
|
): void {
|
|
177
177
|
const collBuf = toCString(collection);
|
|
178
178
|
const grpBuf = toCString(group);
|
|
179
|
-
const keyArg = typeof key === "string" ? toCString(key).buf : BigInt(key)
|
|
179
|
+
const keyArg = (typeof key === "string" ? toCString(key).buf : BigInt(key)) as Key extends string
|
|
180
|
+
? Uint8Array
|
|
181
|
+
: bigint;
|
|
180
182
|
const entries = Object.entries(row);
|
|
181
183
|
const columnCount = entries.length;
|
|
182
184
|
const keepalive: Allocation[] = [];
|