@minnowdb/core 0.5.0 → 0.6.0

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.
Files changed (40) hide show
  1. package/README.md +1 -1
  2. package/dist/engine/cancellation.d.ts +2 -0
  3. package/dist/engine/cancellation.js +4 -0
  4. package/dist/engine/catalog.d.ts +3 -1
  5. package/dist/engine/catalog.js +1 -0
  6. package/dist/engine/client.d.ts +32 -4
  7. package/dist/engine/client.js +82 -15
  8. package/dist/engine/database.d.ts +23 -14
  9. package/dist/engine/database.js +516 -74
  10. package/dist/engine/defaults.js +11 -0
  11. package/dist/engine/errors.d.ts +13 -0
  12. package/dist/engine/errors.js +22 -0
  13. package/dist/engine/fts.d.ts +2 -15
  14. package/dist/engine/live.d.ts +1 -7
  15. package/dist/engine/live.js +2 -12
  16. package/dist/engine/optimizer.js +540 -38
  17. package/dist/engine/query.d.ts +5 -278
  18. package/dist/engine/query.js +78 -32
  19. package/dist/engine/schema-wire.d.ts +7 -1
  20. package/dist/engine/schema-wire.js +4 -0
  21. package/dist/engine/schema.d.ts +67 -33
  22. package/dist/engine/schema.js +138 -7
  23. package/dist/engine/sql-domains.d.ts +8 -0
  24. package/dist/engine/sql-domains.js +25 -0
  25. package/dist/engine/sql-json.js +22 -3
  26. package/dist/engine/vector.d.ts +2 -2
  27. package/dist/engine/vector.js +301 -39
  28. package/dist/engine/worker-host.js +119 -44
  29. package/dist/plan/index.d.ts +5 -4
  30. package/dist/plan/index.js +3 -3
  31. package/dist/plan/model.d.ts +218 -0
  32. package/dist/plan/model.js +1 -0
  33. package/dist/storage/types.d.ts +7 -0
  34. package/dist/storage/types.js +16 -0
  35. package/dist/transactions/index.d.ts +5 -3
  36. package/dist/transactions/index.js +58 -8
  37. package/dist/worker-protocol/index.d.ts +6 -1
  38. package/dist/worker-protocol/index.js +5 -2
  39. package/package.json +1 -1
  40. package/sql-feature-matrix.json +69 -19
@@ -1,4 +1,4 @@
1
- export const protocolVersion = 2;
1
+ export const protocolVersion = 3;
2
2
  /** Outstanding request/response pairs retained by either side of one database RPC connection. */
3
3
  export const MAX_DATABASE_RPC_IN_FLIGHT = 256;
4
4
  export function parseRequest(value) {
@@ -64,8 +64,11 @@ export function parseRpcRequest(value) {
64
64
  if (typeof value !== "object" || value === null)
65
65
  return null;
66
66
  const candidate = value;
67
- if (candidate.kind !== "rpc-init" && candidate.kind !== "rpc-call")
67
+ if (candidate.kind !== "rpc-init" &&
68
+ candidate.kind !== "rpc-call" &&
69
+ candidate.kind !== "rpc-cancel") {
68
70
  return null;
71
+ }
69
72
  if (candidate.version !== protocolVersion)
70
73
  throw new Error("Unsupported protocol version");
71
74
  if (typeof candidate.requestId !== "string" || candidate.requestId.length === 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minnowdb/core",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "A columnar SQL database for the browser: PostgreSQL-style SQL over durable IndexedDB or OPFS data, with no server or WebAssembly module.",
5
5
  "license": "MIT",
6
6
  "author": "Eric Wilhite",
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": 4,
2
+ "version": 5,
3
3
  "description": "Executable PostgreSQL compatibility examples for MinnowDatabase. postgres-feature-profile.json records differences, extensions, and embedded exclusions.",
4
4
  "features": [
5
5
  {
@@ -20,7 +20,8 @@
20
20
  {
21
21
  "id": "select.distinct",
22
22
  "status": "supported",
23
- "example": "SELECT DISTINCT region FROM rows"
23
+ "example": "SELECT DISTINCT region FROM rows",
24
+ "notes": "Plain projections and SELECT DISTINCT * are supported. DISTINCT combined with grouping, HAVING, or aggregate/window expressions is rejected."
24
25
  },
25
26
  {
26
27
  "id": "select.scalar-subquery",
@@ -127,7 +128,7 @@
127
128
  "id": "group-by.rollup",
128
129
  "status": "supported",
129
130
  "example": "SELECT region, SUM(amount) AS total FROM rows GROUP BY ROLLUP(region)",
130
- "notes": "ROLLUP/CUBE/GROUPING SETS desugar into a UNION ALL of grouped blocks. The GROUPING() marker is deliberately unsupported, so rollup NULLs and data NULLs are indistinguishable. SQLite itself has none of these."
131
+ "notes": "ROLLUP/CUBE/GROUPING SETS desugar into a UNION ALL of grouped blocks. GROUPING() distinguishes rolled-up columns from data NULLs. SQLite itself has none of these."
131
132
  },
132
133
  {
133
134
  "id": "group-by.grouping-sets",
@@ -245,7 +246,7 @@
245
246
  "id": "mutation.update-keyed",
246
247
  "status": "supported",
247
248
  "example": "UPDATE keyed SET score = score + 1 WHERE score > 0",
248
- "notes": "Requires a unique-key table; read-then-mutate, not serializable."
249
+ "notes": "Requires a unique-key table. A standalone statement reads and publishes inside one retryable write scope; an explicit transaction stages it with the transaction's other statements."
249
250
  },
250
251
  {
251
252
  "id": "mutation.delete-keyed",
@@ -316,7 +317,7 @@
316
317
  "id": "predicate.quantified",
317
318
  "status": "supported",
318
319
  "example": "SELECT region FROM rows WHERE amount > ALL (SELECT amount FROM dims)",
319
- "notes": "ANY/SOME/ALL with full three-valued logic; correlated forms are rejected. SQLite itself has no quantified comparisons."
320
+ "notes": "ANY/SOME/ALL use full three-valued logic. Correlated forms are supported as top-level WHERE predicates. SQLite itself has no quantified comparisons."
320
321
  },
321
322
  {
322
323
  "id": "predicate.ilike",
@@ -387,30 +388,67 @@
387
388
  "id": "subquery.correlated",
388
389
  "status": "supported",
389
390
  "example": "SELECT region FROM rows r WHERE amount > (SELECT AVG(amount) FROM rows q WHERE q.region = r.region)",
390
- "notes": "Equality-correlated subqueries decorrelate into derived-table joins at compile time; both executors run plain joins."
391
+ "notes": "Correlated subqueries decorrelate into derived-table joins at compile time; both executors run plain joins."
391
392
  },
392
393
  {
393
394
  "id": "subquery.correlated-exists",
394
395
  "status": "supported",
395
396
  "example": "SELECT amount FROM rows r WHERE EXISTS (SELECT region FROM dims d WHERE d.region = r.region)",
396
- "notes": "EXISTS joins the subquery's distinct correlation keys; NOT EXISTS becomes a left join checked with IS NULL."
397
+ "notes": "Top-level EXISTS and NOT EXISTS lower to semi-joins and anti-joins. Nested boolean forms use hidden per-probe match flags."
397
398
  },
398
399
  {
399
400
  "id": "subquery.correlated-select",
400
401
  "status": "supported",
401
402
  "example": "SELECT r.region, (SELECT AVG(q.amount) FROM rows q WHERE q.region = r.region) AS regional FROM rows r",
402
- "notes": "Correlated scalar aggregates decorrelate in the select list too, outside grouped queries."
403
+ "notes": "Correlated scalar aggregates decorrelate in the select list. In a grouped query, their outer references must be GROUP BY columns and the scalar cannot sit inside an outer aggregate."
404
+ },
405
+ {
406
+ "id": "subquery.correlated-select-grouped",
407
+ "status": "supported",
408
+ "example": "SELECT r.region, COUNT(*) AS c, (SELECT AVG(q.amount) FROM rows q WHERE q.region = r.region) AS regional FROM rows r GROUP BY r.region",
409
+ "notes": "The decorrelated value is functionally determined by the outer grouping keys and is carried as an internal group key."
410
+ },
411
+ {
412
+ "id": "subquery.correlated-scalar-non-equi",
413
+ "status": "supported",
414
+ "example": "SELECT r.amount, (SELECT COUNT(*) FROM rows q WHERE q.amount < r.amount) AS lower_count FROM rows r",
415
+ "notes": "Non-equality scalar aggregates group inner rows once per distinct outer probe tuple, then join the result back by equality."
416
+ },
417
+ {
418
+ "id": "subquery.correlated-exists-expression",
419
+ "status": "supported",
420
+ "example": "SELECT r.amount FROM rows r WHERE r.amount > 100 OR EXISTS (SELECT d.region FROM dims d WHERE d.region = r.region)",
421
+ "notes": "Correlated EXISTS and NOT EXISTS remain set-at-a-time when nested below OR, NOT, or CASE."
403
422
  },
404
423
  {
405
424
  "id": "subquery.correlated-non-equi",
406
425
  "status": "supported",
407
426
  "example": "SELECT r.amount FROM rows r WHERE EXISTS (SELECT q.amount FROM rows q WHERE q.amount < r.amount)",
408
- "notes": "Non-equality EXISTS and NOT EXISTS correlations lower to semi-joins and anti-joins; correlated scalar aggregates and IN remain equality-only."
427
+ "notes": "Non-equality EXISTS and NOT EXISTS correlations lower to semi-joins and anti-joins. Scalar aggregates use distinct outer probes."
428
+ },
429
+ {
430
+ "id": "subquery.correlated-in-non-equi",
431
+ "status": "supported",
432
+ "example": "SELECT r.amount FROM rows r WHERE r.region IN (SELECT q.region FROM rows q WHERE q.amount < r.amount)",
433
+ "notes": "A range-correlated IN predicate lowers to a semi-join carrying both its correlation and membership comparisons."
409
434
  },
410
435
  {
411
436
  "id": "subquery.correlated-not-in",
412
437
  "status": "supported",
413
- "example": "SELECT region FROM rows r WHERE region NOT IN (SELECT d.region FROM dims d WHERE d.region = r.region)"
438
+ "example": "SELECT region FROM rows r WHERE region NOT IN (SELECT d.region FROM dims d WHERE d.region = r.region)",
439
+ "notes": "Correlated NOT IN preserves empty-set and NULL semantics rather than treating it as a simple anti-join."
440
+ },
441
+ {
442
+ "id": "subquery.correlated-not-in-non-equi",
443
+ "status": "supported",
444
+ "example": "SELECT r.amount FROM rows r WHERE 'north' NOT IN (SELECT q.region FROM rows q WHERE q.amount < r.amount)",
445
+ "notes": "Range correlation uses an anti-join for exact matches plus per-probe total and non-NULL counts."
446
+ },
447
+ {
448
+ "id": "subquery.correlated-quantified",
449
+ "status": "supported",
450
+ "example": "SELECT r.amount FROM rows r WHERE r.amount > ALL (SELECT q.amount FROM rows q WHERE q.region = r.region)",
451
+ "notes": "At top-level WHERE, correlated ANY lowers to a semi-join on true comparisons and ALL to an anti-join on false or unknown comparisons."
414
452
  },
415
453
  {
416
454
  "id": "cte.recursive",
@@ -456,13 +494,13 @@
456
494
  "id": "aggregate.filter",
457
495
  "status": "supported",
458
496
  "example": "SELECT region, COUNT(*) FILTER (WHERE amount > 5) AS big FROM rows GROUP BY region",
459
- "notes": "Desugars into a CASE inside the aggregate, so it works with every aggregate and DISTINCT."
497
+ "notes": "Desugars into a CASE inside COUNT/SUM/AVG/MIN/MAX and preserves DISTINCT. JSON_ARRAYAGG does not support FILTER."
460
498
  },
461
499
  {
462
500
  "id": "window.aggregate-over",
463
501
  "status": "supported",
464
502
  "example": "SELECT SUM(amount) OVER (PARTITION BY region) AS total FROM rows",
465
- "notes": "Default frame only: whole partition without OVER ordering, running with peers when ordered."
503
+ "notes": "Without explicit framing, the default is the whole partition when unordered and a peer-aware running frame when ordered. ROWS, RANGE, GROUPS, and exclusions are tracked separately below."
466
504
  },
467
505
  {
468
506
  "id": "window.in-expression",
@@ -569,7 +607,7 @@
569
607
  "id": "trigger.create-after",
570
608
  "status": "supported",
571
609
  "example": "CREATE TRIGGER keyed_audit AFTER INSERT ON keyed BEGIN INSERT INTO rows (region, amount) VALUES (NEW.name, NEW.score); END",
572
- "notes": "AFTER and BEFORE row triggers on INSERT/UPDATE/DELETE, executed atomically inside the triggering commit with NEW/OLD references. Bodies: INSERT ... VALUES into keyless tables; UPDATE/DELETE against keyed tables. One cascade level is allowed; deeper chains error at write time."
610
+ "notes": "AFTER and BEFORE row triggers on INSERT/UPDATE/DELETE execute atomically with NEW/OLD references. Bodies support parameter-free INSERT ... VALUES into keyless tables and UPDATE/DELETE against keyed tables; ON CONFLICT and RETURNING are rejected. One cascade level is allowed."
573
611
  },
574
612
  {
575
613
  "id": "trigger.create-before",
@@ -970,8 +1008,8 @@
970
1008
  {
971
1009
  "id": "json.object",
972
1010
  "status": "supported",
973
- "example": "SELECT JSON_OBJECT('a' VALUE 1, 'missing' VALUE NULL) AS document",
974
- "notes": "Defaults to NULL ON NULL and WITHOUT UNIQUE KEYS. NULL keys are rejected. The constructor returns JSON text; cast or store it as JSON/JSONB for domain validation and JSONB canonicalization."
1011
+ "example": "SELECT JSON_OBJECT('a' VALUE 1, 'detail' VALUE JSON_OBJECT('name' VALUE 'Acme')) AS document",
1012
+ "notes": "Defaults to NULL ON NULL and WITHOUT UNIQUE KEYS. NULL keys are rejected. JSON-producing arguments embed as documents rather than escaped strings; ordinary text remains a string. The constructor returns JSON text; cast or store it as JSON/JSONB for domain validation and JSONB canonicalization."
975
1013
  },
976
1014
  {
977
1015
  "id": "json.array",
@@ -990,6 +1028,12 @@
990
1028
  "example": "CREATE TABLE defaulted (id INTEGER PRIMARY KEY, tier TEXT DEFAULT 'basic')",
991
1029
  "notes": "DEFAULT accepts a variable-free scalar expression. Omission or SQL DEFAULT invokes it; explicit NULL follows the column's independent nullability."
992
1030
  },
1031
+ {
1032
+ "id": "ddl.generated-column",
1033
+ "status": "supported",
1034
+ "example": "CREATE TABLE generated_value (base INTEGER, doubled INTEGER GENERATED ALWAYS AS (base * 2) STORED)",
1035
+ "notes": "Stored generated columns recompute on every write and may be indexed, but cannot be caller-assigned or used as Minnow's row-addressing primary/unique key. Expressions are immutable and row-local."
1036
+ },
993
1037
  {
994
1038
  "id": "ddl.create-table-key-clause",
995
1039
  "status": "supported",
@@ -1117,7 +1161,13 @@
1117
1161
  "id": "from.lateral",
1118
1162
  "status": "supported",
1119
1163
  "example": "SELECT x.amount FROM rows r, LATERAL (SELECT amount FROM dims WHERE dims.region = r.region) x",
1120
- "notes": "Equality-correlated derived sources become set-at-a-time joins. Correlated grouping, ordering, and LIMIT remain unsupported."
1164
+ "notes": "Equality and range-correlated derived sources become set-at-a-time joins. Correlated grouping, ordering, and LIMIT remain unsupported."
1165
+ },
1166
+ {
1167
+ "id": "from.lateral-non-equi",
1168
+ "status": "supported",
1169
+ "example": "SELECT x.amount FROM rows r, LATERAL (SELECT q.amount FROM rows q WHERE q.amount < r.amount) x",
1170
+ "notes": "Non-equality correlation lowers to an ordinary general join rather than executing the derived source once per outer row."
1121
1171
  },
1122
1172
  {
1123
1173
  "id": "aggregate.string-agg",
@@ -1146,8 +1196,8 @@
1146
1196
  {
1147
1197
  "id": "aggregate.json",
1148
1198
  "status": "supported",
1149
- "example": "SELECT JSON_ARRAYAGG(region) AS regions FROM rows",
1150
- "notes": "Supports DISTINCT, includes SQL NULL as JSON null, and returns NULL for empty input. FILTER, window use, aggregate-local ORDER BY, and explicit NULL/ABSENT clauses are not supported; input order is otherwise unspecified."
1199
+ "example": "SELECT JSON_ARRAYAGG(JSON_OBJECT('region' VALUE region)) AS regions FROM rows",
1200
+ "notes": "Supports DISTINCT, embeds JSON-producing inputs as documents, includes SQL NULL as JSON null, and returns NULL for empty input. FILTER, window use, aggregate-local ORDER BY, and explicit NULL/ABSENT clauses are not supported; input order is otherwise unspecified."
1151
1201
  },
1152
1202
  {
1153
1203
  "id": "type.array",
@@ -1184,7 +1234,7 @@
1184
1234
  "id": "ddl.create-view",
1185
1235
  "status": "supported",
1186
1236
  "example": "CREATE VIEW west AS SELECT region, amount FROM rows WHERE region = 'west'",
1187
- "notes": "The catalog stores the query text and the schema inferred from it, so a view answers the same questions a table does and reads expand it into the query it stands for — anywhere a read runs, including inside a write scope. A view is never a write target. CREATE OR REPLACE redefines one; a view stacked on it follows the new definition, and a cycle two redefinitions close is caught on the next read rather than recursed."
1237
+ "notes": "The catalog stores the query text and inferred schema, so reads expand a view anywhere a table can be read, including inside a write scope. A view is never a write target. CREATE OR REPLACE redefines one; dependent views follow it and cycles are rejected on read. CREATE VIEW column-name lists are not supported."
1188
1238
  },
1189
1239
  {
1190
1240
  "id": "ddl.drop-view",