@minnowdb/core 0.6.4 → 0.6.6

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 (43) hide show
  1. package/dist/engine/artifact-cache.d.ts +2 -1
  2. package/dist/engine/batch.d.ts +0 -1
  3. package/dist/engine/batch.js +1 -1
  4. package/dist/engine/buffered-writer.js +1 -12
  5. package/dist/engine/byte-estimates.d.ts +11 -0
  6. package/dist/engine/byte-estimates.js +25 -0
  7. package/dist/engine/database.js +78 -38
  8. package/dist/engine/fts.d.ts +0 -1
  9. package/dist/engine/fts.js +1 -1
  10. package/dist/engine/join-index.d.ts +0 -1
  11. package/dist/engine/optimizer.js +11 -3
  12. package/dist/engine/point-read.d.ts +1 -1
  13. package/dist/engine/point-read.js +3 -2
  14. package/dist/engine/query-cache.js +1 -12
  15. package/dist/engine/query.d.ts +20 -59
  16. package/dist/engine/query.js +374 -71
  17. package/dist/engine/result-wire.d.ts +2 -1
  18. package/dist/engine/schema.d.ts +18 -2
  19. package/dist/engine/schema.js +11 -2
  20. package/dist/engine/sort-keys.d.ts +2 -3
  21. package/dist/engine/sort-keys.js +1 -1
  22. package/dist/engine/sql-domains.d.ts +36 -2
  23. package/dist/engine/sql-domains.js +143 -6
  24. package/dist/engine/sql-json.d.ts +12 -2
  25. package/dist/engine/sql-json.js +41 -1
  26. package/dist/engine/sql-semantics.d.ts +2 -1
  27. package/dist/engine/vector.d.ts +7 -7
  28. package/dist/engine/vector.js +8 -4
  29. package/dist/engine/write-block-planner.d.ts +2 -1
  30. package/dist/plan/model.d.ts +19 -0
  31. package/dist/storage/opfs/leader.d.ts +0 -4
  32. package/dist/storage/opfs/leader.js +2 -2
  33. package/dist/storage/opfs/snapshot-ledger.d.ts +3 -2
  34. package/dist/storage/toolkit/index.d.ts +1 -1
  35. package/dist/storage/toolkit/record-core.d.ts +0 -1
  36. package/dist/storage/toolkit/record-core.js +0 -7
  37. package/dist/testing/opfs-shim.d.ts +2 -1
  38. package/dist/testing/simulator.js +3 -0
  39. package/dist/worker-protocol/index.d.ts +1 -1
  40. package/dist/worker-protocol/index.js +0 -1
  41. package/package.json +2 -1
  42. package/postgres-feature-profile.json +18 -3
  43. package/sql-feature-matrix.json +95 -11
@@ -26,4 +26,4 @@ export { readFully, writeFully, type SyncFileHandle } from "./sync-file.js";
26
26
  export { RecordCore, type PhysicalBlocks, type RecordCoreState } from "./record-core.js";
27
27
  export { MAX_WAL_FRAME_BYTES, WalWriter, iterateWalFrames, replayWalFrames, type ReplayedWalFrame, } from "./wal.js";
28
28
  export { ExtentPool, assertValidExtentMeta, assertValidPlacement, extentPath, validPlacement, type ExtentBatchMark, type ExtentFiles, type ExtentMeta, type ExtentPoolOptions, type Placement, } from "./extents.js";
29
- export { LOG_FORMAT_VERSION, decodeChunk, decodePostingChunk, decodeRecordJson, decodeSyncCheckpoint, encodeChunk, encodePostingChunk, encodeRecordJson, encodeSyncCheckpoint, } from "./wire.js";
29
+ export { LOG_FORMAT_VERSION, decodeChunk, decodePostingChunk, decodeRecordJson, decodeSyncCheckpoint, encodeChunk, encodePostingChunk, encodeRecordJson, encodeSyncCheckpoint, type PostingChunkEntry, } from "./wire.js";
@@ -373,7 +373,6 @@ export declare function validateFtsPostingChunks(chunks: unknown, label: string)
373
373
  export declare function validateSegmentRuntimeRecord(value: unknown, label: string): SegmentRecord;
374
374
  export declare function validateAutoIncrementReservation(count: number, atLeast: bigint | undefined): void;
375
375
  export declare function validateBeginTransactionInput(input: BeginTransactionInput): void;
376
- export declare function validateLeaseExpiration(expiresAt: string): void;
377
376
  export declare function validateTempRunPage(page: TempRunPage): void;
378
377
  export declare function validateTempRunPageIdentity(ownerId: string, runId: string, pageIndex: number): void;
379
378
  export {};
@@ -6382,13 +6382,6 @@ function validateTempOwnerRecord(record) {
6382
6382
  throw new RangeError("Temp owner record must be created at revision zero");
6383
6383
  }
6384
6384
  }
6385
- export function validateLeaseExpiration(expiresAt) {
6386
- if (typeof expiresAt !== "string" ||
6387
- expiresAt.length === 0 ||
6388
- !Number.isFinite(Date.parse(expiresAt))) {
6389
- throw new TypeError("Lease expiration must be valid");
6390
- }
6391
- }
6392
6385
  function isCanonicalTimestamp(value) {
6393
6386
  const timestamp = Date.parse(value);
6394
6387
  return Number.isFinite(timestamp) && dateIsoString(new Date(timestamp)) === value;
@@ -21,7 +21,7 @@
21
21
  * toolkit's `SyncFileHandle` surface, in plain vitest, with crashes and quota failures on tap.
22
22
  */
23
23
  export type WriteFault = (path: string, phase: "create" | "write" | "flush") => void;
24
- export type DeleteFault = (path: string) => void;
24
+ type DeleteFault = (path: string) => void;
25
25
  export type TransferLimit = (path: string, operation: "read" | "write", requestedBytes: number, at: number) => number | undefined;
26
26
  export declare class MemoryOpfs {
27
27
  #private;
@@ -43,3 +43,4 @@ export declare class MemoryOpfs {
43
43
  /** Test-side in-place corruption: existing sync handles observe the changed byte. */
44
44
  corruptFileByte(path: string, offset: number): void;
45
45
  }
46
+ export {};
@@ -572,6 +572,9 @@ function summarizeCollection(result) {
572
572
  retainedTransactions: result.retainedTransactionCount,
573
573
  };
574
574
  }
575
+ // Stream-identical copy of `mulberry32` in ./seeds.ts. This module ships in the tarball and
576
+ // seeds.ts does not (it reads MINNOW_SEED and the unpublished regression-seeds.json), so the
577
+ // published simulator cannot import the canonical copy.
575
578
  function mulberry32(seed) {
576
579
  let state = seed >>> 0;
577
580
  return () => {
@@ -1,7 +1,7 @@
1
1
  export declare const protocolVersion: 3;
2
2
  /** Outstanding request/response pairs retained by either side of one database RPC connection. */
3
3
  export declare const MAX_DATABASE_RPC_IN_FLIGHT = 256;
4
- export type WorkerOperation = "benchmark" | "cancelBenchmark" | "memorySample" | "datasetList" | "datasetCreate" | "datasetDelete" | "runQuery" | "suiteReference" | "suiteWrite" | "suiteFeatureMatrix" | "suiteLive";
4
+ export type WorkerOperation = "benchmark" | "cancelBenchmark" | "datasetList" | "datasetCreate" | "datasetDelete" | "runQuery" | "suiteReference" | "suiteWrite" | "suiteFeatureMatrix" | "suiteLive";
5
5
  export interface WorkerRequest<T = unknown> {
6
6
  version: typeof protocolVersion;
7
7
  requestId: string;
@@ -105,7 +105,6 @@ function isOperation(value) {
105
105
  return [
106
106
  "benchmark",
107
107
  "cancelBenchmark",
108
- "memorySample",
109
108
  "datasetList",
110
109
  "datasetCreate",
111
110
  "datasetDelete",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minnowdb/core",
3
- "version": "0.6.4",
3
+ "version": "0.6.6",
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",
@@ -125,6 +125,7 @@
125
125
  "!dist/storage/fixture-shape.*",
126
126
  "!dist/engine/storage-test-helpers.*",
127
127
  "!dist/testing/seeds.*",
128
+ "!dist/testing/oracle.*",
128
129
  "!dist/*.tsbuildinfo",
129
130
  "sql-feature-matrix.json",
130
131
  "postgres-feature-profile.json"
@@ -111,7 +111,7 @@
111
111
  {
112
112
  "id": "function.string-extended",
113
113
  "classification": "different",
114
- "reason": "The bundled form includes INSTR; PostgreSQL spells that function strpos with reversed arguments."
114
+ "reason": "The bundled form includes INSTR; PostgreSQL has no INSTR and spells the same (string, substring) lookup STRPOS, with the arguments in the same order."
115
115
  },
116
116
  {
117
117
  "id": "function.trim-multi-character",
@@ -134,10 +134,15 @@
134
134
  "classification": "different",
135
135
  "reason": "Both engines accept the correlated JSON aggregate and agree on its JSON value, but Minnow returns JSON text while PostgreSQL returns a native JSON value."
136
136
  },
137
+ {
138
+ "id": "literal.scientific",
139
+ "classification": "different",
140
+ "reason": "PostgreSQL types every scientific-notation constant NUMERIC and renders it as text. Minnow evaluates it exactly but returns a number whenever the value reads back identically from one, keeping ordinary constants number-typed at the JavaScript boundary; a constant that stays exact renders fully expanded, as PostgreSQL renders it."
141
+ },
137
142
  {
138
143
  "id": "type.exact-numeric",
139
144
  "classification": "different",
140
- "reason": "Minnow preserves exact decimals at the JavaScript boundary as strings; PGlite's default decoder returns this NUMERIC value as a number."
145
+ "reason": "Minnow preserves exact decimals at the JavaScript boundary as strings, as PGlite's default decoder also does. A declared scale renders at exactly that scale as PostgreSQL does; a bare NUMERIC column, a derived arithmetic result, and a value cast or concatenated to text render canonically, without the trailing fractional zeros PostgreSQL preserves. Division and AVG select their result scale the way PostgreSQL does, so quotient digits agree — including AVG over a column whose declared scale exceeds the selection. The canonical encoding does drop a stored value's display scale, so an explicit arithmetic quotient (such as SUM(v) / COUNT(v)) over a column declared with more than about twenty fractional digits can carry fewer digits than PostgreSQL, which floors the selection at the operand's display scale. An arithmetic or comparison expression mixing a float column with a constant Float64 cannot represent stays exact, where PostgreSQL casts the constant to float8 and rounds it before evaluating."
141
146
  },
142
147
  {
143
148
  "id": "type.json-jsonb",
@@ -164,6 +169,16 @@
164
169
  "classification": "different",
165
170
  "reason": "The JSON value agrees, but Minnow returns compact JSON text while PostgreSQL's text rendering includes spaces."
166
171
  },
172
+ {
173
+ "id": "json.arrow",
174
+ "classification": "different",
175
+ "reason": "The JSON value agrees, but Minnow returns compact JSON text at the JavaScript boundary while PostgreSQL clients commonly decode the json result natively. Minnow's parameters carry JavaScript types, so an integer parameter key selects an array element directly where an untyped PostgreSQL placeholder resolves to the text-key operator and needs an explicit ::int cast. Minnow's JSON values also compare as their canonical text, so -> results are valid comparison operands where PostgreSQL has no json = json operator."
176
+ },
177
+ {
178
+ "id": "json.arrow-untyped",
179
+ "classification": "extension",
180
+ "reason": "PostgreSQL requires a json-typed document for -> and ->>; Minnow also accepts ordinary JSON text directly, as its SQL/JSON functions do."
181
+ },
167
182
  {
168
183
  "id": "json.object",
169
184
  "classification": "different",
@@ -172,7 +187,7 @@
172
187
  {
173
188
  "id": "json.array",
174
189
  "classification": "different",
175
- "reason": "The JSON value agrees, but Minnow returns compact JSON text while PostgreSQL's text rendering includes spaces."
190
+ "reason": "Minnow defaults JSON_ARRAY to NULL ON NULL, so a NULL argument becomes a JSON null where PostgreSQL's ABSENT ON NULL default drops it; Minnow also returns compact JSON text while PostgreSQL's rendering includes spaces."
176
191
  },
177
192
  {
178
193
  "id": "trigger.create-after",
@@ -676,7 +676,7 @@
676
676
  "CREATE TABLE stats (region TEXT UNIQUE, total REAL)",
677
677
  "INSERT INTO stats (region, total) VALUES ('east', 0), ('west', 0)"
678
678
  ],
679
- "notes": "UPDATE and DELETE trigger bodies run against keyed tables, reading current state each firing. Touching the same target row twice in one firing is rejected."
679
+ "notes": "UPDATE and DELETE trigger bodies run against keyed tables, reading current state each firing. One body statement touching the same target row for two triggering rows in one firing is rejected; separate body statements may each touch that row."
680
680
  },
681
681
  {
682
682
  "id": "trigger.drop",
@@ -700,7 +700,7 @@
700
700
  "id": "expression.concat",
701
701
  "status": "supported",
702
702
  "example": "SELECT region || '-' || label AS tag FROM dims",
703
- "notes": "|| concatenates strings and propagates NULL; non-string operands are a type error."
703
+ "notes": "|| concatenates strings and propagates NULL; non-string operands are a type error. One operand must be ordinary text, matching PostgreSQL's operator resolution: array and JSONB || are structural concatenation (see array.concat and json.concat), and two non-text domain operands have no || operator. A domain value concatenated with text renders in its Minnow text form."
704
704
  },
705
705
  {
706
706
  "id": "expression.modulo",
@@ -730,7 +730,7 @@
730
730
  "id": "expression.coalesce",
731
731
  "status": "supported",
732
732
  "example": "SELECT COALESCE(region, 'unknown') AS region_label FROM rows",
733
- "notes": "Arguments evaluate left to right; the first non-NULL value wins. All non-NULL arguments must share one type."
733
+ "notes": "Arguments evaluate left to right; the first non-NULL value wins. Arguments are not type-checked: mixed-type arguments are accepted and return the first non-NULL value as-is, where PostgreSQL requires a common type."
734
734
  },
735
735
  {
736
736
  "id": "expression.date-trunc",
@@ -958,7 +958,7 @@
958
958
  "id": "datetime.localtime",
959
959
  "status": "supported",
960
960
  "example": "SELECT LOCALTIME IS NOT NULL AS ticking",
961
- "notes": "The engine has no TIME type, so LOCALTIME reads as an 'HH:MM:SS' string, like SQLite's CURRENT_TIME."
961
+ "notes": "LOCALTIME reads as an 'HH:MM:SS' string of the current UTC time, like SQLite's CURRENT_TIME — Minnow has no session time zone, where PostgreSQL renders the session-local wall clock."
962
962
  },
963
963
  {
964
964
  "id": "predicate.row-comparison",
@@ -985,6 +985,24 @@
985
985
  "status": "supported",
986
986
  "example": "SELECT 1_000 AS thousand"
987
987
  },
988
+ {
989
+ "id": "literal.exact-decimal",
990
+ "status": "supported",
991
+ "example": "SELECT 1.000000000000000000000000 / 3 = CAST('0.333333333333333333333333' AS NUMERIC) AS exact_thirds",
992
+ "notes": "Decimal constants keep their exact digits, as PostgreSQL's NUMERIC typing does. Arithmetic among constants is exact — 0.1 + 0.2 is 0.3 — and a quotient's scale follows the operands' written scales. A result that reads back identically from a JavaScript number is returned as a number; one the number boundary would visibly round is returned as an exact decimal string."
993
+ },
994
+ {
995
+ "id": "literal.big-integer",
996
+ "status": "supported",
997
+ "example": "SELECT 10000000000000000001 = CAST('10000000000000000001' AS NUMERIC) AS same",
998
+ "notes": "An integer constant beyond 2^53 stays exact instead of rounding or failing. A value Float64 cannot hold is returned as a decimal string, and writing one to an INTEGER or BIGINT column is rejected rather than silently rounded."
999
+ },
1000
+ {
1001
+ "id": "literal.scientific",
1002
+ "status": "supported",
1003
+ "example": "SELECT 2.5e-1 AS quarter",
1004
+ "notes": "Scientific notation is a numeric constant, as in PostgreSQL. PostgreSQL renders every such constant as NUMERIC text; Minnow returns a number when the value reads back identically from one, and renders a constant that stays exact fully expanded, as PostgreSQL renders it."
1005
+ },
988
1006
  {
989
1007
  "id": "limit.with-ties",
990
1008
  "status": "supported",
@@ -1075,7 +1093,31 @@
1075
1093
  "id": "json.array",
1076
1094
  "status": "supported",
1077
1095
  "example": "SELECT JSON_ARRAY(1, NULL, 2) AS document",
1078
- "notes": "Defaults to NULL ON NULL. The explicit NULL/ABSENT clause is not supported. The constructor returns JSON text; cast or store it as JSON/JSONB for domain validation and JSONB canonicalization."
1096
+ "notes": "Defaults to NULL ON NULL, unlike PostgreSQL's ABSENT ON NULL default for JSON_ARRAY. The explicit NULL/ABSENT clause is not supported. The constructor returns JSON text; cast or store it as JSON/JSONB for domain validation and JSONB canonicalization."
1097
+ },
1098
+ {
1099
+ "id": "json.arrow",
1100
+ "status": "supported",
1101
+ "example": "SELECT CAST('{\"a\": {\"b\": [5, 6]}}' AS JSON) -> 'a' -> 'b' -> 1 AS element",
1102
+ "notes": "PostgreSQL's -> member/element access, chainable and usable anywhere an expression is. A text key selects an object member; an integer key selects an array element. The result is a JSON value, so a selected string keeps its quotes and a selected JSON null stays the document null. Behaviour follows the json type: a document of the wrong shape for the key selects NULL, without jsonb's scalar-as-one-element-array reading. A document that is not JSON is an error, matching the operators' typed-json requirement."
1103
+ },
1104
+ {
1105
+ "id": "json.arrow-text",
1106
+ "status": "supported",
1107
+ "example": "SELECT CAST('{\"items\": [\"x\", \"y\"]}' AS JSON) -> 'items' ->> 0 AS first_item",
1108
+ "notes": "->> returns text: strings unquoted, other scalars as their JSON rendering, objects and arrays serialized as compact JSON text, and a selected JSON null as SQL NULL."
1109
+ },
1110
+ {
1111
+ "id": "json.arrow-index",
1112
+ "status": "supported",
1113
+ "example": "SELECT CAST('[\"x\", \"y\", \"z\"]' AS JSON) ->> -1 AS last_item",
1114
+ "notes": "Negative element positions count from the end of the array; positions out of range in either direction select NULL."
1115
+ },
1116
+ {
1117
+ "id": "json.arrow-untyped",
1118
+ "status": "supported",
1119
+ "example": "SELECT '{\"a\": {\"b\": [\"x\", \"y\"]}}' -> 'a' -> 'b' ->> 1 AS second_item",
1120
+ "notes": "Like Minnow's SQL/JSON functions, the arrows accept ordinary JSON text and stored JSON/JSONB values directly; PostgreSQL requires a json-typed document to resolve the operator."
1079
1121
  },
1080
1122
  {
1081
1123
  "id": "ddl.create-table-if-not-exists",
@@ -1114,7 +1156,7 @@
1114
1156
  "id": "type.exact-numeric",
1115
1157
  "status": "supported",
1116
1158
  "example": "SELECT CAST(1.25 AS DECIMAL(12, 2)) AS amount",
1117
- "notes": "Exact through storage, arithmetic, comparison, aggregates, and windows. Division retains 20 fractional digits."
1159
+ "notes": "Exact through storage, arithmetic, comparison, aggregates, and windows. Division selects its result scale the way PostgreSQL does: at least sixteen significant digits, never fewer fractional digits than either operand, rounded half away from zero."
1118
1160
  },
1119
1161
  {
1120
1162
  "id": "type.json-jsonb",
@@ -1131,8 +1173,15 @@
1131
1173
  {
1132
1174
  "id": "type.interval",
1133
1175
  "status": "supported",
1134
- "example": "SELECT CAST('1 day' AS INTERVAL) AS span",
1135
- "notes": "Preserves separate month, day, and microsecond fields. Interval arithmetic is not yet exposed."
1176
+ "example": "SELECT joined + INTERVAL '1 day' AS next_day FROM rows",
1177
+ "notes": "Preserves separate month, day, and microsecond fields, and a date or datetime accepts + and - INTERVAL. Interval-valued arithmetic is not supported (see type.interval-arithmetic)."
1178
+ },
1179
+ {
1180
+ "id": "type.interval-arithmetic",
1181
+ "status": "unsupported",
1182
+ "example": "SELECT INTERVAL '1 day' + INTERVAL '2 hours' AS total",
1183
+ "error": "Date arithmetic requires a date or datetime value",
1184
+ "notes": "PostgreSQL adds intervals into a combined interval and subtracts timestamps into an interval. Minnow's interval arithmetic only shifts a date or datetime by + or - INTERVAL, so an interval-valued result is refused."
1136
1185
  },
1137
1186
  {
1138
1187
  "id": "ddl.enum",
@@ -1239,7 +1288,14 @@
1239
1288
  "id": "json.table",
1240
1289
  "status": "supported",
1241
1290
  "example": "SELECT j.a FROM JSON_TABLE('{\"a\":1}', '$' COLUMNS (a INTEGER PATH '$.a')) AS j",
1242
- "notes": "Constant documents support $ and $[*] row paths. Correlated documents are not yet supported."
1291
+ "notes": "Constant documents support $ and $[*] row paths. A document read from row data is refused (see json.table-correlated)."
1292
+ },
1293
+ {
1294
+ "id": "json.table-correlated",
1295
+ "status": "unsupported",
1296
+ "example": "SELECT jt.v FROM (SELECT '[1,2]' AS document) AS d, JSON_TABLE(d.document, '$[*]' COLUMNS (v INTEGER PATH '$')) AS jt",
1297
+ "error": "JSON_TABLE currently requires a constant document",
1298
+ "notes": "PostgreSQL evaluates JSON_TABLE laterally against each source row's document. Minnow expands only constant documents, so a column-valued document is refused at compile time."
1243
1299
  },
1244
1300
  {
1245
1301
  "id": "predicate.similar-to",
@@ -1263,7 +1319,35 @@
1263
1319
  "id": "type.array",
1264
1320
  "status": "supported",
1265
1321
  "example": "SELECT ARRAY[1, 2] AS pair",
1266
- "notes": "Constructors and array columns use canonical JSON text at the JavaScript boundary. Subscripts and array operators are not yet supported."
1322
+ "notes": "Constructors and array columns use canonical JSON text at the JavaScript boundary. Subscripts and array operators are not supported (see type.array-subscript, type.array-any, and array.concat)."
1323
+ },
1324
+ {
1325
+ "id": "type.array-subscript",
1326
+ "status": "unsupported",
1327
+ "example": "SELECT (ARRAY[1, 2, 3])[1] AS first_element",
1328
+ "error": "Array subscripts are not supported",
1329
+ "notes": "PostgreSQL reads array elements with one-based subscripts and slices. Minnow arrays are opaque JSON text values; element access is refused at parse time."
1330
+ },
1331
+ {
1332
+ "id": "type.array-any",
1333
+ "status": "unsupported",
1334
+ "example": "SELECT 2 = ANY(ARRAY[1, 2, 3]) AS found",
1335
+ "error": "ANY/ALL take a subquery",
1336
+ "notes": "PostgreSQL's ANY, ALL, and SOME accept an array operand as well as a subquery. Minnow implements only the subquery form, so the array variant is refused at compile time."
1337
+ },
1338
+ {
1339
+ "id": "array.concat",
1340
+ "status": "unsupported",
1341
+ "example": "SELECT ARRAY[1, 2] || ARRAY[3] AS combined",
1342
+ "error": "PostgreSQL array concatenation (||) is not supported",
1343
+ "notes": "PostgreSQL's array || array and array || element are structural concatenation. Minnow refuses || whenever either operand is an array rather than inventing a text concatenation PostgreSQL does not have."
1344
+ },
1345
+ {
1346
+ "id": "json.concat",
1347
+ "status": "unsupported",
1348
+ "example": "SELECT CAST('{\"a\":1}' AS JSONB) || CAST('{\"b\":2}' AS JSONB) AS merged",
1349
+ "error": "PostgreSQL JSONB concatenation (||) is not supported",
1350
+ "notes": "PostgreSQL's jsonb || jsonb merges documents structurally. Minnow refuses || whenever either operand is JSONB rather than inventing a text concatenation PostgreSQL does not have."
1267
1351
  },
1268
1352
  {
1269
1353
  "id": "type.time",
@@ -1294,7 +1378,7 @@
1294
1378
  "id": "ddl.create-view",
1295
1379
  "status": "supported",
1296
1380
  "example": "CREATE VIEW west AS SELECT region, amount FROM rows WHERE region = 'west'",
1297
- "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."
1381
+ "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 a definition that would create a cycle is rejected when the view is defined. CREATE VIEW column-name lists are not supported."
1298
1382
  },
1299
1383
  {
1300
1384
  "id": "ddl.drop-view",