quiverdb 0.12.6 → 0.12.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/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 +1 -1
- package/src/lua-api.ts +98 -37
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
package/src/lua-api.ts
CHANGED
|
@@ -52,7 +52,7 @@ Lua values map to Quiver column values as follows:
|
|
|
52
52
|
| number (float) | REAL | A float is rejected for an INTEGER column. |
|
|
53
53
|
| string | TEXT | Also used for \`date_time\` columns (ISO 8601). |
|
|
54
54
|
| boolean | INTEGER 1/0 | SQLite has no boolean type; \`true\` writes 1, \`false\` 0. |
|
|
55
|
-
| \`nil\` | NULL | In query params
|
|
55
|
+
| \`nil\` | NULL | In query params (not trailing), ts rows, group cells, relations. |
|
|
56
56
|
| table (1-indexed) | array | Used for vectors/sets and column-oriented data.|
|
|
57
57
|
|
|
58
58
|
A boolean is accepted **wherever an integer is** — element scalars and arrays, query parameters,
|
|
@@ -86,10 +86,18 @@ midnight.
|
|
|
86
86
|
- **Type coercion.** An integer is accepted for a REAL column (coerced to real on insert); a float
|
|
87
87
|
is rejected for an INTEGER column. A string bound to a \`date_*\` column must parse as ISO 8601
|
|
88
88
|
(\`YYYY-MM-DD\`, optionally \`THH:MM:SS\` or \` HH:MM:SS\`). Other type mismatches raise a
|
|
89
|
-
validation error
|
|
90
|
-
- **
|
|
91
|
-
\`
|
|
92
|
-
transaction
|
|
89
|
+
validation error.
|
|
90
|
+
- **An uncaught error aborts the script, and the script is not rolled back.** Any error thrown by
|
|
91
|
+
a \`db:\` call that you do not catch with \`pcall\` stops the script and surfaces as
|
|
92
|
+
\`Failed to run Lua script: <message>\`. Outside a transaction each \`db:\` write commits on its
|
|
93
|
+
own, so the failing call changes nothing in the database, but every write that finished before
|
|
94
|
+
it **stays**. Only \`db:transaction(fn)\` and \`db:dry_run(fn)\` undo their block's database
|
|
95
|
+
writes when \`fn\` errors; nothing undoes a file a \`db:\` call wrote. A
|
|
96
|
+
\`db:begin_transaction()\` (or \`db:begin_dry_run()\`) you opened and never closed stays open
|
|
97
|
+
after the error, and closing it is up to the host. A host that runs your script inside its own
|
|
98
|
+
transaction or dry run may undo the whole run; check \`db:in_transaction()\` if it matters. To
|
|
99
|
+
make several writes all-or-nothing, put them in one \`db:transaction(function(db) ... end)\`
|
|
100
|
+
and do not \`pcall\` inside it: a caught error lets the block commit whatever ran.
|
|
93
101
|
- **Standard library.** Loaded standard libraries: base, string, table, math, coroutine, utf8.
|
|
94
102
|
That is the pure-computation set — there is no \`os\`, \`io\`, \`debug\`, or \`package\`/\`require\`,
|
|
95
103
|
and \`dofile\`/\`loadfile\` are removed (string-form \`load\` stays available). Integer division is
|
|
@@ -106,9 +114,12 @@ midnight.
|
|
|
106
114
|
\`Cannot <op>: database is in-memory, file operations are unavailable\`.
|
|
107
115
|
- **Output.** A script can \`return\` one value and the host receives it as JSON — prefer this over
|
|
108
116
|
\`print()\` when you need structured data back (\`print()\` still works and is captured). Only the
|
|
109
|
-
**first** returned value is encoded. Arrays are 1-indexed
|
|
110
|
-
|
|
111
|
-
|
|
117
|
+
**first** returned value is encoded. Arrays are 1-indexed. Iterate with \`ipairs\` only where no
|
|
118
|
+
NULL can appear: a read of a nullable column (a bulk scalar read, a vector/set inner list, a
|
|
119
|
+
time-series value column, \`db:read_time_series_row\`) has \`nil\` holes, and \`ipairs\` stops at
|
|
120
|
+
the first one (see Scalar reads). Reading a NULL yields \`nil\`; writing \`nil\` stores NULL where
|
|
121
|
+
NULL is accepted (query params except a trailing one, ts rows, group cells, relation targets —
|
|
122
|
+
but NOT element scalar attributes; see CRUD).
|
|
112
123
|
|
|
113
124
|
\`\`\`lua
|
|
114
125
|
return { ids = db:read_element_ids("Collection"), total = 3 }
|
|
@@ -126,10 +137,10 @@ midnight.
|
|
|
126
137
|
| any other table | object, keys sorted; integer keys stringify |
|
|
127
138
|
|
|
128
139
|
**A table with holes is an object, not an array.** A bulk read of a nullable column returns
|
|
129
|
-
\`nil\` holes (see
|
|
140
|
+
\`nil\` holes (see Scalar reads), so \`return db:read_scalar_integers(c, a)\` encodes as
|
|
130
141
|
\`{"1":10,"3":30}\` — not \`[10,null,30]\` — and the keys sort as text (\`"1","11","2"\`). When the
|
|
131
142
|
host needs positional scalar data, return the ids alongside and fill the holes yourself:
|
|
132
|
-
\`local v = db:read_scalar_integers(c, a); local out = {}; for i
|
|
143
|
+
\`local ids = db:read_element_ids(c); local v = db:read_scalar_integers(c, a); local out = {}; for i = 1, #ids do out[i] = v[i] or false end\`.
|
|
133
144
|
**Inside** a vector/set read an inner list with an interior NULL cell encodes as an object nested
|
|
134
145
|
in the outer array (\`[{"1":10,"3":30},[]]\`), but a trailing NULL cell leaves no trace at all:
|
|
135
146
|
\`[10, NULL]\` encodes as the plain \`[10]\`. The ids count elements, not rows, so they cannot
|
|
@@ -142,8 +153,10 @@ midnight.
|
|
|
142
153
|
self-referencing table); a result over 64 MB raises
|
|
143
154
|
\`Cannot run: script return value exceeds ... bytes of JSON\`; a string holding bytes that are not
|
|
144
155
|
valid UTF-8 raises \`Cannot run: script return value contains a string that is not valid UTF-8\`;
|
|
145
|
-
|
|
146
|
-
raises \`Cannot run: script returned a table with duplicate key '1'
|
|
156
|
+
a table where an integer key and a string key spell the same thing (\`{[1] = 'a', ['1'] = 'b'}\`)
|
|
157
|
+
raises \`Cannot run: script returned a table with duplicate key '1'\`; and a table with a key that
|
|
158
|
+
is neither an integer nor a string (a boolean, a float such as \`1.5\`, a table) raises
|
|
159
|
+
\`Cannot run: script returned a table with an unsupported key type\`.
|
|
147
160
|
- **Embedding harness may restrict further.** The library itself allows transactions and the
|
|
148
161
|
(sandboxed) CSV/file operations below. A host that runs your script (e.g. a hosted \`run_lua\`
|
|
149
162
|
tool) may disable some of them and report \`disabled in the run_lua sandbox\` — that limit comes
|
|
@@ -193,10 +206,12 @@ end) -- both writes commit together; if either throws, both roll back
|
|
|
193
206
|
\`\`\`
|
|
194
207
|
|
|
195
208
|
**Caveat:** if the host already runs your script inside a plain transaction (not a dry run), an
|
|
196
|
-
explicit \`db:begin_transaction()\` will error
|
|
197
|
-
and a mid-script \`db:commit()\` would
|
|
198
|
-
|
|
199
|
-
|
|
209
|
+
explicit \`db:begin_transaction()\` will error
|
|
210
|
+
(\`Cannot begin_transaction: transaction already active\`) and a mid-script \`db:commit()\` would
|
|
211
|
+
prematurely end the host's transaction. \`db:transaction(fn)\` begins one too, so it fails the same
|
|
212
|
+
way. When unsure whether a transaction is already open, check \`db:in_transaction()\` first, or just issue writes directly:
|
|
213
|
+
outside a transaction each \`db:\` write commits on its own, while inside the host's transaction it
|
|
214
|
+
commits or rolls back with the host's.
|
|
200
215
|
|
|
201
216
|
---
|
|
202
217
|
|
|
@@ -235,6 +250,11 @@ Rules worth knowing:
|
|
|
235
250
|
\`Cannot begin_dry_run: dry run already active\` — check \`db:in_dry_run()\` first and skip the
|
|
236
251
|
wrapper when one is already active. Do **not** call \`db:end_dry_run()\` to get around it: that
|
|
237
252
|
ends the host's dry run, and everything you write afterwards is committed for real.
|
|
253
|
+
- **Nor inside a plain transaction.** If the host (or a \`db:transaction\` block) already holds a
|
|
254
|
+
plain transaction, \`db:begin_dry_run()\` and \`db:dry_run(fn)\` throw
|
|
255
|
+
\`Cannot begin_dry_run: transaction already active\`. \`db:in_transaction()\` is true under a host
|
|
256
|
+
dry run *and* under a host transaction, so it is the one check that covers both;
|
|
257
|
+
\`db:in_dry_run()\` covers only the first.
|
|
238
258
|
- For a rough sense of how much a run touched, \`db:query_integer("SELECT total_changes()")\` gives
|
|
239
259
|
the number of rows inserted, updated or deleted on this connection.
|
|
240
260
|
|
|
@@ -293,8 +313,10 @@ Notes:
|
|
|
293
313
|
\`{ x = nil }\` is identical to \`{}\`; an update/create table that ends up with no attributes
|
|
294
314
|
**throws** (\`...must have at least one scalar attribute\` on create, \`...at least one attribute
|
|
295
315
|
to update\` on update). To leave a column unchanged, omit the key — you cannot set a scalar to
|
|
296
|
-
NULL via the element table. (\`nil\` → NULL is
|
|
297
|
-
\`upsert_time_series_row
|
|
316
|
+
NULL via the element table. (\`nil\` → NULL is accepted by \`update_relation\` and in query params
|
|
317
|
+
(not a trailing one), and as a cell in the group writers; \`upsert_time_series_row\` and
|
|
318
|
+
\`update_time_series_files\` replace the whole row, so a column you leave out (or set to \`nil\`,
|
|
319
|
+
the same thing) is cleared — see Time series and Time series files.)
|
|
298
320
|
- **\`update_relation\` points one scalar foreign-key relation at another element**, named by the
|
|
299
321
|
target's label. The column is derived from the naming convention —
|
|
300
322
|
\`lowercase(collection_to) .. "_" .. relation_type\`, so
|
|
@@ -311,7 +333,10 @@ Notes:
|
|
|
311
333
|
|
|
312
334
|
## Scalar reads (bulk, across all elements)
|
|
313
335
|
|
|
314
|
-
Each returns a flat array (1-indexed table), one value per element, in id order.
|
|
336
|
+
Each returns a flat array (1-indexed table), one value per element, in id order. A NULL is a
|
|
337
|
+
\`nil\` hole at that position, so \`ipairs\` stops at the first one and \`#\` is unreliable. Loop
|
|
338
|
+
\`for i = 1, #ids\` over \`local ids = db:read_element_ids(collection)\` and index the result with
|
|
339
|
+
\`i\`.
|
|
315
340
|
|
|
316
341
|
\`\`\`lua
|
|
317
342
|
db:read_scalar_integers(collection, attribute) -- { 42, 37, ... }
|
|
@@ -432,9 +457,10 @@ local values = db:read_time_series_row(collection, group, attribute, date_time)
|
|
|
432
457
|
\`\`\`
|
|
433
458
|
|
|
434
459
|
One value per element using **last non-null value at or before \`date_time\`** semantics. Elements
|
|
435
|
-
with no matching data yield \`nil\` in the array
|
|
436
|
-
|
|
437
|
-
\`
|
|
460
|
+
with no matching data yield \`nil\` in the array, so the result can hold \`nil\` holes: loop over
|
|
461
|
+
\`db:read_element_ids(collection)\` rather than using \`ipairs\`. \`date_time\` is an ISO 8601
|
|
462
|
+
string. A group with more than one dimension column (e.g. \`date_time\` + \`block\`) throws — read
|
|
463
|
+
it with \`read_time_series_group\`.
|
|
438
464
|
|
|
439
465
|
### Replace a whole group (column-oriented — SAME shape as the read)
|
|
440
466
|
|
|
@@ -463,10 +489,10 @@ db:update_time_series_group("Items", "data", id, ts) -- write the whole group
|
|
|
463
489
|
|
|
464
490
|
**DO NOT pass an array of row tables** (\`{ { date_time = ..., value = ... }, ... }\`) — that is the
|
|
465
491
|
\`upsert_time_series_row\` shape, not this one. Doing so raises
|
|
466
|
-
\`
|
|
467
|
-
|
|
492
|
+
\`Cannot update_time_series_group: column names must be strings; pass { column = { values... } }, not an array of row tables\`.
|
|
493
|
+
Each value of the top-level table must be an **array**, not a scalar.
|
|
468
494
|
|
|
469
|
-
**Rules** (
|
|
495
|
+
**Rules** (a violation throws; see "Errors abort the script" for what is kept):
|
|
470
496
|
- Every column value must be an array — a bare scalar throws \`column '...' must be an array of values\`.
|
|
471
497
|
- The **dimension column(s) set the row count** and must be present and fully populated: the
|
|
472
498
|
\`date_*\` ordering column, plus any extra primary-key columns in a multi-dimensional group (e.g.
|
|
@@ -478,8 +504,9 @@ column names). Each value of the top-level table must be an **array**, not a sca
|
|
|
478
504
|
column **longer** than the dimension column throws \`column '...' has length N but expected M\`.
|
|
479
505
|
- Named columns whose dimension transposes to zero rows throw (\`contain no rows; pass an empty
|
|
480
506
|
table {} to clear the group\`) — only a bare \`{}\` clears.
|
|
481
|
-
- Integer values are accepted for REAL columns (converted on insert)
|
|
482
|
-
|
|
507
|
+
- Integer values are accepted for REAL columns (converted on insert), and a boolean is written as
|
|
508
|
+
1/0. A function, a table or another unsupported Lua type throws
|
|
509
|
+
\`column '...' has unsupported Lua type\`.
|
|
483
510
|
- The element id must exist; the \`_by_label\` form takes a label in its place, with
|
|
484
511
|
\`update_element_by_label\`'s resolution and miss semantics. Every rule above applies to both.
|
|
485
512
|
|
|
@@ -503,6 +530,10 @@ db:upsert_time_series_row_by_label("Items", "data", "Item 1", {
|
|
|
503
530
|
The element id must exist; the \`_by_label\` form takes a label in its place, with
|
|
504
531
|
\`update_element_by_label\`'s resolution and miss semantics.
|
|
505
532
|
|
|
533
|
+
An existing row with the same dimension key is **replaced whole**: every value column you leave
|
|
534
|
+
out (or set to \`nil\`) is reset to NULL (or its DEFAULT). To change one value, pass every value
|
|
535
|
+
column of the row.
|
|
536
|
+
|
|
506
537
|
---
|
|
507
538
|
|
|
508
539
|
## Time series files
|
|
@@ -514,10 +545,15 @@ singleton table):
|
|
|
514
545
|
db:has_time_series_files(collection) -- boolean
|
|
515
546
|
db:list_time_series_files_columns(collection) -- { "data_file", "metadata_file", ... }
|
|
516
547
|
db:read_time_series_files(collection) -- { data_file = "path", metadata_file = nil, ... }
|
|
517
|
-
db:update_time_series_files(collection, { data_file = "path/to/data.bin"
|
|
548
|
+
db:update_time_series_files(collection, { data_file = "path/to/data.bin" }) -- metadata_file is cleared
|
|
518
549
|
\`\`\`
|
|
519
550
|
|
|
520
|
-
|
|
551
|
+
\`update_time_series_files\` **replaces the whole row**: every column you do not give a string is
|
|
552
|
+
set to NULL (or to its DEFAULT, if the schema declares one), and in Lua a \`nil\` value and a
|
|
553
|
+
missing key are the same thing. A table with no string values (\`{}\`, or only \`nil\` values)
|
|
554
|
+
changes nothing and clears nothing. A value that is not a string throws. To change one path, read
|
|
555
|
+
the row with \`db:read_time_series_files(collection)\`, change that one entry, and pass the whole
|
|
556
|
+
table back.
|
|
521
557
|
|
|
522
558
|
---
|
|
523
559
|
|
|
@@ -575,9 +611,15 @@ entirely.
|
|
|
575
611
|
## Query (parameterized SQL)
|
|
576
612
|
|
|
577
613
|
Positional \`?\` placeholders; \`params\` is an optional 1-indexed array. Each returns the first
|
|
578
|
-
column of the first row
|
|
614
|
+
column of the first row, or \`nil\` when there is no row, the value is NULL, or it is not already
|
|
615
|
+
the requested type. Only \`query_float\` converts (it widens an INTEGER), so
|
|
616
|
+
\`db:query_string("SELECT COUNT(*) ...")\` and \`db:query_integer("SELECT AVG(x) ...")\` return
|
|
617
|
+
\`nil\`; \`CAST\` in the SQL when unsure (\`SELECT CAST(AVG(x) AS INTEGER)\`). The number of
|
|
579
618
|
\`params\` must match the number of \`?\` placeholders exactly — a mismatch (too few or too many)
|
|
580
|
-
throws rather than binding NULL or ignoring extras.
|
|
619
|
+
throws rather than binding NULL or ignoring extras. A \`nil\` param binds NULL only in a table
|
|
620
|
+
constructor whose last entry is not \`nil\` (\`{ nil, 5 }\`): Lua stores no key for a \`nil\`, so
|
|
621
|
+
\`{ 5, nil }\` or \`{ nil }\` comes up short and throws that mismatch. To test for NULL, write
|
|
622
|
+
\`IS NULL\` in the SQL.
|
|
581
623
|
|
|
582
624
|
\`\`\`lua
|
|
583
625
|
db:query_string(sql, params) -- string or nil
|
|
@@ -599,9 +641,22 @@ local count = db:query_integer("SELECT COUNT(*) FROM Collection")
|
|
|
599
641
|
|
|
600
642
|
## CSV import / export
|
|
601
643
|
|
|
602
|
-
Export
|
|
603
|
-
|
|
604
|
-
|
|
644
|
+
Export one table of \`collection\` to a CSV file, or import one from a CSV file. \`group\` names a
|
|
645
|
+
vector, set or time-series group of \`collection\`; pass \`""\` for the collection's own scalar table
|
|
646
|
+
(the CSV then holds exactly the table's columns except \`id\`, \`label\` included). \`path\` is
|
|
647
|
+
sandboxed: relative paths resolve against the database file's directory and must stay inside it
|
|
648
|
+
(see Critical rules); \`options\` is optional.
|
|
649
|
+
|
|
650
|
+
**\`db:import_csv\` replaces, it does not merge.** A group import deletes every existing row of
|
|
651
|
+
that group, for every element, not only the elements named in the file, and then inserts the
|
|
652
|
+
file's rows. A scalar import (\`group = ""\`) matches elements by exact label. An element whose
|
|
653
|
+
label is in the file keeps its id and has every column overwritten from the file (a blank cell
|
|
654
|
+
writes NULL). **Every element whose label is not in the file is deleted** as
|
|
655
|
+
\`db:delete_element\` would delete it: its group rows go with it, and every relation pointing at
|
|
656
|
+
it from another table follows its \`ON DELETE\` action (\`SET NULL\` clears it, \`CASCADE\`
|
|
657
|
+
deletes the row that holds it, a group row or an element of another collection). So editing a
|
|
658
|
+
label in the file deletes that element and creates a new one. A header-only CSV empties the
|
|
659
|
+
table. To add or change a few elements, use \`db:create_element\` / \`db:update_element\` instead.
|
|
605
660
|
|
|
606
661
|
\`\`\`lua
|
|
607
662
|
db:export_csv(collection, group, path, options)
|
|
@@ -612,7 +667,7 @@ The optional \`options\` table has two keys:
|
|
|
612
667
|
|
|
613
668
|
\`\`\`lua
|
|
614
669
|
{
|
|
615
|
-
date_time_format = "%Y-%m-%d", -- strftime-style format for
|
|
670
|
+
date_time_format = "%Y-%m-%d", -- strftime-style format for every date-time column
|
|
616
671
|
|
|
617
672
|
-- enum_labels: write/read integer codes as human labels. Three nested levels:
|
|
618
673
|
-- attribute name -> locale -> { label = integer_id }
|
|
@@ -625,6 +680,9 @@ The optional \`options\` table has two keys:
|
|
|
625
680
|
}
|
|
626
681
|
\`\`\`
|
|
627
682
|
|
|
683
|
+
An options value that is not a table, an unknown key or a wrong-typed value throws
|
|
684
|
+
(\`Cannot export_csv: unknown option '...'\`).
|
|
685
|
+
|
|
628
686
|
**Precondition:** \`db:import_csv\` cannot run inside an open transaction (it opens and commits its
|
|
629
687
|
own) — it throws \`Cannot import_csv: transaction already active\`. Call it outside any
|
|
630
688
|
\`db:transaction\` / \`db:begin_transaction\` block.
|
|
@@ -812,7 +870,9 @@ File I/O is db-scoped (\`db:open_file\` / \`db:bin_to_csv\` / \`db:csv_to_bin\`)
|
|
|
812
870
|
base paths, sandboxed to the database directory (see Critical rules), and \`get_file_path()\` returns
|
|
813
871
|
the resolved absolute path. The pure-metadata builders and expression constructors live under the
|
|
814
872
|
global \`quiver\` table. Mirrors the Julia surface; aggregation ops are strings (Lua has no enums);
|
|
815
|
-
operators are \`+ - * /\` and unary \`-\`, with scalars allowed on either side.
|
|
873
|
+
operators are \`+ - * /\` and unary \`-\`, with scalars allowed on either side. A file handle does not
|
|
874
|
+
outlive the \`run()\` that opened it: any handle still open when the script returns is closed (and a
|
|
875
|
+
writer flushed), so reopen the file in each script.
|
|
816
876
|
|
|
817
877
|
\`\`\`lua
|
|
818
878
|
local md = quiver.metadata{
|
|
@@ -847,7 +907,8 @@ e:save(out_path); e:metadata() -- save path is sandboxed l
|
|
|
847
907
|
|
|
848
908
|
**\`quiver.metadata{...}\` kwargs and defaults:** \`version\` defaults to \`"1"\`; \`initial_datetime\`
|
|
849
909
|
and \`unit\` default to \`""\`; \`labels\`, \`dimensions\`, \`dimension_sizes\`, \`time_dimensions\`, and
|
|
850
|
-
\`frequencies\` default to empty arrays.
|
|
910
|
+
\`frequencies\` default to empty arrays. Only these eight keys are accepted; an unknown key or a
|
|
911
|
+
value of the wrong type throws.
|
|
851
912
|
|
|
852
913
|
**\`get_dimensions()\` / \`get_metadata()\` dimension shape** — returns an array of dimension tables:
|
|
853
914
|
|