@minnowdb/core 0.3.0 → 0.4.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.
- package/README.md +4 -2
- package/dist/block-format/block.d.ts +32 -13
- package/dist/block-format/block.js +179 -55
- package/dist/block-format/checksum.d.ts +2 -1
- package/dist/block-format/checksum.js +5 -2
- package/dist/block-format/codecs.d.ts +7 -2
- package/dist/block-format/codecs.js +53 -12
- package/dist/block-format/column.d.ts +3 -2
- package/dist/block-format/column.js +96 -30
- package/dist/block-format/index.d.ts +2 -2
- package/dist/block-format/index.js +2 -2
- package/dist/block-format/physical.d.ts +8 -2
- package/dist/block-format/physical.js +14 -6
- package/dist/block-format/types.d.ts +7 -3
- package/dist/block-format/types.js +0 -1
- package/dist/block-format/unicode.d.ts +11 -0
- package/dist/block-format/unicode.js +46 -0
- package/dist/date-value.d.ts +15 -0
- package/dist/date-value.js +64 -0
- package/dist/engine/artifact-cache.d.ts +0 -1
- package/dist/engine/artifact-cache.js +0 -1
- package/dist/engine/batch.d.ts +5 -2
- package/dist/engine/batch.js +0 -1
- package/dist/engine/buffered-writer.d.ts +2 -1
- package/dist/engine/buffered-writer.js +34 -9
- package/dist/engine/cache-limits.d.ts +25 -0
- package/dist/engine/cache-limits.js +25 -0
- package/dist/engine/catalog.d.ts +5 -6
- package/dist/engine/catalog.js +5 -5
- package/dist/engine/client.d.ts +27 -7
- package/dist/engine/client.js +197 -31
- package/dist/engine/database.d.ts +161 -38
- package/dist/engine/database.js +4320 -1523
- package/dist/engine/defaults.d.ts +0 -1
- package/dist/engine/defaults.js +2 -4
- package/dist/engine/errors.d.ts +19 -1
- package/dist/engine/errors.js +32 -2
- package/dist/engine/fts.d.ts +0 -6
- package/dist/engine/fts.js +39 -14
- package/dist/engine/group-index.d.ts +0 -1
- package/dist/engine/group-index.js +6 -11
- package/dist/engine/index.d.ts +16 -11
- package/dist/engine/index.js +12 -10
- package/dist/engine/join-index.d.ts +0 -1
- package/dist/engine/join-index.js +2 -2
- package/dist/engine/keyed-live.d.ts +57 -0
- package/dist/engine/keyed-live.js +226 -0
- package/dist/engine/live-api.d.ts +4 -0
- package/dist/engine/live-api.js +4 -0
- package/dist/engine/live.d.ts +43 -26
- package/dist/engine/live.js +499 -129
- package/dist/engine/memory.d.ts +2 -1
- package/dist/engine/memory.js +3 -2
- package/dist/engine/optimizer.d.ts +0 -1
- package/dist/engine/optimizer.js +4 -4
- package/dist/engine/query-api.d.ts +3 -0
- package/dist/engine/query-api.js +3 -0
- package/dist/engine/query-cache.d.ts +0 -1
- package/dist/engine/query-cache.js +4 -4
- package/dist/engine/query.d.ts +4 -7
- package/dist/engine/query.js +348 -155
- package/dist/engine/result-wire.d.ts +0 -1
- package/dist/engine/result-wire.js +2 -2
- package/dist/engine/schema-wire.d.ts +1 -2
- package/dist/engine/schema-wire.js +0 -1
- package/dist/engine/schema.d.ts +3 -8
- package/dist/engine/schema.js +41 -24
- package/dist/engine/sort-keys.d.ts +0 -1
- package/dist/engine/sort-keys.js +2 -2
- package/dist/engine/sql-domains.d.ts +6 -1
- package/dist/engine/sql-domains.js +209 -45
- package/dist/engine/sql-driver.d.ts +2 -2
- package/dist/engine/sql-driver.js +0 -1
- package/dist/engine/sql-json.d.ts +0 -1
- package/dist/engine/sql-json.js +53 -7
- package/dist/engine/sql-semantics.d.ts +8 -5
- package/dist/engine/sql-semantics.js +427 -65
- package/dist/engine/typed-live.d.ts +67 -0
- package/dist/engine/typed-live.js +349 -0
- package/dist/engine/vector.d.ts +7 -1
- package/dist/engine/vector.js +202 -58
- package/dist/engine/worker-host.d.ts +8 -2
- package/dist/engine/worker-host.js +616 -152
- package/dist/engine/worker.d.ts +0 -1
- package/dist/engine/worker.js +3 -3
- package/dist/engine/write-block-planner.d.ts +18 -0
- package/dist/engine/write-block-planner.js +112 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/plan/index.d.ts +0 -1
- package/dist/plan/index.js +0 -1
- package/dist/storage/index.d.ts +1 -1
- package/dist/storage/index.js +1 -1
- package/dist/storage/indexeddb.d.ts +74 -65
- package/dist/storage/indexeddb.js +12301 -2076
- package/dist/storage/memory.d.ts +56 -66
- package/dist/storage/memory.js +1004 -139
- package/dist/storage/opfs/files.d.ts +18 -7
- package/dist/storage/opfs/files.js +115 -28
- package/dist/storage/opfs/index.d.ts +1 -1
- package/dist/storage/opfs/index.js +1 -1
- package/dist/storage/opfs/leader.d.ts +403 -91
- package/dist/storage/opfs/leader.js +4306 -602
- package/dist/storage/opfs/rpc.d.ts +15 -3
- package/dist/storage/opfs/rpc.js +223 -2
- package/dist/storage/opfs/snapshot-ledger.d.ts +40 -0
- package/dist/storage/opfs/snapshot-ledger.js +281 -0
- package/dist/storage/opfs/store.d.ts +26 -121
- package/dist/storage/opfs/store.js +436 -388
- package/dist/storage/persistence.d.ts +37 -0
- package/dist/storage/persistence.js +78 -0
- package/dist/storage/snapshot-stream.d.ts +24 -0
- package/dist/storage/snapshot-stream.js +897 -0
- package/dist/storage/snapshot.d.ts +9 -99
- package/dist/storage/snapshot.js +33 -309
- package/dist/storage/toolkit/extents.d.ts +41 -7
- package/dist/storage/toolkit/extents.js +402 -39
- package/dist/storage/toolkit/index.d.ts +3 -4
- package/dist/storage/toolkit/index.js +27 -3
- package/dist/storage/toolkit/record-core.d.ts +174 -61
- package/dist/storage/toolkit/record-core.js +5401 -849
- package/dist/storage/toolkit/sync-file.d.ts +10 -1
- package/dist/storage/toolkit/sync-file.js +50 -2
- package/dist/storage/toolkit/wal.d.ts +23 -5
- package/dist/storage/toolkit/wal.js +89 -27
- package/dist/storage/toolkit/wire.d.ts +4 -6
- package/dist/storage/toolkit/wire.js +80 -25
- package/dist/storage/types.d.ts +1271 -218
- package/dist/storage/types.js +1471 -138
- package/dist/testing/block-store-conformance.d.ts +0 -1
- package/dist/testing/block-store-conformance.js +877 -138
- package/dist/testing/index.d.ts +42 -31
- package/dist/testing/index.js +100 -68
- package/dist/testing/opfs-shim.d.ts +13 -3
- package/dist/testing/opfs-shim.js +40 -6
- package/dist/testing/simulator.d.ts +0 -1
- package/dist/testing/simulator.js +43 -12
- package/dist/testing/sqllogictest.d.ts +0 -1
- package/dist/testing/sqllogictest.js +0 -1
- package/dist/transactions/index.d.ts +81 -20
- package/dist/transactions/index.js +1050 -268
- package/dist/worker-protocol/index.d.ts +3 -2
- package/dist/worker-protocol/index.js +2 -5
- package/package.json +49 -1
- package/dist/block-format/block.d.ts.map +0 -1
- package/dist/block-format/block.js.map +0 -1
- package/dist/block-format/checksum.d.ts.map +0 -1
- package/dist/block-format/checksum.js.map +0 -1
- package/dist/block-format/codecs.d.ts.map +0 -1
- package/dist/block-format/codecs.js.map +0 -1
- package/dist/block-format/column.d.ts.map +0 -1
- package/dist/block-format/column.js.map +0 -1
- package/dist/block-format/index.d.ts.map +0 -1
- package/dist/block-format/index.js.map +0 -1
- package/dist/block-format/physical.d.ts.map +0 -1
- package/dist/block-format/physical.js.map +0 -1
- package/dist/block-format/types.d.ts.map +0 -1
- package/dist/block-format/types.js.map +0 -1
- package/dist/engine/artifact-cache.d.ts.map +0 -1
- package/dist/engine/artifact-cache.js.map +0 -1
- package/dist/engine/batch.d.ts.map +0 -1
- package/dist/engine/batch.js.map +0 -1
- package/dist/engine/buffered-writer.d.ts.map +0 -1
- package/dist/engine/buffered-writer.js.map +0 -1
- package/dist/engine/catalog.d.ts.map +0 -1
- package/dist/engine/catalog.js.map +0 -1
- package/dist/engine/client.d.ts.map +0 -1
- package/dist/engine/client.js.map +0 -1
- package/dist/engine/coordinator.d.ts +0 -17
- package/dist/engine/coordinator.d.ts.map +0 -1
- package/dist/engine/coordinator.js +0 -60
- package/dist/engine/coordinator.js.map +0 -1
- package/dist/engine/database.d.ts.map +0 -1
- package/dist/engine/database.js.map +0 -1
- package/dist/engine/defaults.d.ts.map +0 -1
- package/dist/engine/defaults.js.map +0 -1
- package/dist/engine/errors.d.ts.map +0 -1
- package/dist/engine/errors.js.map +0 -1
- package/dist/engine/fts.d.ts.map +0 -1
- package/dist/engine/fts.js.map +0 -1
- package/dist/engine/group-index.d.ts.map +0 -1
- package/dist/engine/group-index.js.map +0 -1
- package/dist/engine/index.d.ts.map +0 -1
- package/dist/engine/index.js.map +0 -1
- package/dist/engine/join-index.d.ts.map +0 -1
- package/dist/engine/join-index.js.map +0 -1
- package/dist/engine/live.d.ts.map +0 -1
- package/dist/engine/live.js.map +0 -1
- package/dist/engine/memory.d.ts.map +0 -1
- package/dist/engine/memory.js.map +0 -1
- package/dist/engine/optimizer.d.ts.map +0 -1
- package/dist/engine/optimizer.js.map +0 -1
- package/dist/engine/query-cache.d.ts.map +0 -1
- package/dist/engine/query-cache.js.map +0 -1
- package/dist/engine/query.d.ts.map +0 -1
- package/dist/engine/query.js.map +0 -1
- package/dist/engine/result-wire.d.ts.map +0 -1
- package/dist/engine/result-wire.js.map +0 -1
- package/dist/engine/schema-wire.d.ts.map +0 -1
- package/dist/engine/schema-wire.js.map +0 -1
- package/dist/engine/schema.d.ts.map +0 -1
- package/dist/engine/schema.js.map +0 -1
- package/dist/engine/sort-keys.d.ts.map +0 -1
- package/dist/engine/sort-keys.js.map +0 -1
- package/dist/engine/sql-domains.d.ts.map +0 -1
- package/dist/engine/sql-domains.js.map +0 -1
- package/dist/engine/sql-driver.d.ts.map +0 -1
- package/dist/engine/sql-driver.js.map +0 -1
- package/dist/engine/sql-json.d.ts.map +0 -1
- package/dist/engine/sql-json.js.map +0 -1
- package/dist/engine/sql-semantics.d.ts.map +0 -1
- package/dist/engine/sql-semantics.js.map +0 -1
- package/dist/engine/vector.d.ts.map +0 -1
- package/dist/engine/vector.js.map +0 -1
- package/dist/engine/worker-host.d.ts.map +0 -1
- package/dist/engine/worker-host.js.map +0 -1
- package/dist/engine/worker.d.ts.map +0 -1
- package/dist/engine/worker.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/plan/index.d.ts.map +0 -1
- package/dist/plan/index.js.map +0 -1
- package/dist/storage/fixture-shape.d.ts +0 -42
- package/dist/storage/fixture-shape.d.ts.map +0 -1
- package/dist/storage/fixture-shape.js +0 -146
- package/dist/storage/fixture-shape.js.map +0 -1
- package/dist/storage/index.d.ts.map +0 -1
- package/dist/storage/index.js.map +0 -1
- package/dist/storage/indexeddb.d.ts.map +0 -1
- package/dist/storage/indexeddb.js.map +0 -1
- package/dist/storage/memory.d.ts.map +0 -1
- package/dist/storage/memory.js.map +0 -1
- package/dist/storage/opfs/files.d.ts.map +0 -1
- package/dist/storage/opfs/files.js.map +0 -1
- package/dist/storage/opfs/index.d.ts.map +0 -1
- package/dist/storage/opfs/index.js.map +0 -1
- package/dist/storage/opfs/leader.d.ts.map +0 -1
- package/dist/storage/opfs/leader.js.map +0 -1
- package/dist/storage/opfs/rpc.d.ts.map +0 -1
- package/dist/storage/opfs/rpc.js.map +0 -1
- package/dist/storage/opfs/store.d.ts.map +0 -1
- package/dist/storage/opfs/store.js.map +0 -1
- package/dist/storage/snapshot.d.ts.map +0 -1
- package/dist/storage/snapshot.js.map +0 -1
- package/dist/storage/toolkit/extents.d.ts.map +0 -1
- package/dist/storage/toolkit/extents.js.map +0 -1
- package/dist/storage/toolkit/index.d.ts.map +0 -1
- package/dist/storage/toolkit/index.js.map +0 -1
- package/dist/storage/toolkit/record-core.d.ts.map +0 -1
- package/dist/storage/toolkit/record-core.js.map +0 -1
- package/dist/storage/toolkit/sync-file.d.ts.map +0 -1
- package/dist/storage/toolkit/sync-file.js.map +0 -1
- package/dist/storage/toolkit/wal.d.ts.map +0 -1
- package/dist/storage/toolkit/wal.js.map +0 -1
- package/dist/storage/toolkit/wire.d.ts.map +0 -1
- package/dist/storage/toolkit/wire.js.map +0 -1
- package/dist/storage/types.d.ts.map +0 -1
- package/dist/storage/types.js.map +0 -1
- package/dist/testing/block-store-conformance.d.ts.map +0 -1
- package/dist/testing/block-store-conformance.js.map +0 -1
- package/dist/testing/index.d.ts.map +0 -1
- package/dist/testing/index.js.map +0 -1
- package/dist/testing/opfs-shim.d.ts.map +0 -1
- package/dist/testing/opfs-shim.js.map +0 -1
- package/dist/testing/seeds.d.ts +0 -11
- package/dist/testing/seeds.d.ts.map +0 -1
- package/dist/testing/seeds.js +0 -50
- package/dist/testing/seeds.js.map +0 -1
- package/dist/testing/simulator.d.ts.map +0 -1
- package/dist/testing/simulator.js.map +0 -1
- package/dist/testing/sqllogictest.d.ts.map +0 -1
- package/dist/testing/sqllogictest.js.map +0 -1
- package/dist/transactions/index.d.ts.map +0 -1
- package/dist/transactions/index.js.map +0 -1
- package/dist/worker-protocol/index.d.ts.map +0 -1
- package/dist/worker-protocol/index.js.map +0 -1
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { assertWellFormedString } from "../block-format/unicode.js";
|
|
2
|
+
import { dateIsoString } from "../date-value.js";
|
|
3
|
+
import { MAX_SQL_SCALAR_RESULT_CHARACTERS, MAX_SQL_NUMERIC_DIGITS, MAX_SQL_STRUCTURED_VALUE_DEPTH, MAX_SQL_STRUCTURED_VALUE_ITEMS, } from "./cache-limits.js";
|
|
1
4
|
/** Internal string encodings for PostgreSQL value domains carried by string vectors. */
|
|
2
5
|
const PREFIX = "\u0000minnow-domain:";
|
|
3
6
|
const TEXT_VALUE = `${PREFIX}text:`;
|
|
@@ -24,23 +27,25 @@ export function externalSqlTextValue(value) {
|
|
|
24
27
|
? value.slice(TEXT_VALUE.length)
|
|
25
28
|
: value;
|
|
26
29
|
}
|
|
27
|
-
const powersOfTen = [1n];
|
|
28
30
|
function pow10(exponent) {
|
|
29
31
|
if (!Number.isSafeInteger(exponent) || exponent < 0 || exponent > 100_000) {
|
|
30
32
|
throw new RangeError(`Decimal scale is outside the supported range: ${String(exponent)}`);
|
|
31
33
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
return powersOfTen[exponent] ?? 1n;
|
|
34
|
+
// Retaining every intermediate power up to 100,000 consumes quadratic memory in the number
|
|
35
|
+
// of decimal digits. Native exponentiation constructs only the requested result.
|
|
36
|
+
return 10n ** BigInt(exponent);
|
|
36
37
|
}
|
|
37
38
|
function decimalParts(value) {
|
|
38
39
|
const source = String(value).trim();
|
|
40
|
+
assertBoundedDomainString(source, "NUMERIC value");
|
|
39
41
|
const match = /^([+-]?)(?:(\d+)(?:\.(\d*))?|\.(\d+))(?:[eE]([+-]?\d+))?$/.exec(source);
|
|
40
42
|
if (match === null)
|
|
41
43
|
throw new TypeError(`Invalid NUMERIC value: ${String(value)}`);
|
|
42
44
|
const fraction = match[3] ?? match[4] ?? "";
|
|
43
45
|
const digits = `${match[2] ?? "0"}${fraction}`.replace(/^0+(?=\d)/, "");
|
|
46
|
+
if (digits.length > MAX_SQL_NUMERIC_DIGITS) {
|
|
47
|
+
throw new RangeError(`NUMERIC value exceeds ${String(MAX_SQL_NUMERIC_DIGITS)} significant digits`);
|
|
48
|
+
}
|
|
44
49
|
const exponent = Number(match[5] ?? 0);
|
|
45
50
|
if (!Number.isSafeInteger(exponent))
|
|
46
51
|
throw new RangeError(`Invalid NUMERIC exponent: ${source}`);
|
|
@@ -114,7 +119,7 @@ export function exactNumericValue(value, precision, scale) {
|
|
|
114
119
|
throw new RangeError(`NUMERIC(${String(precision)}, ${String(declaredScale)}) overflow`);
|
|
115
120
|
}
|
|
116
121
|
}
|
|
117
|
-
return NUMERIC
|
|
122
|
+
return boundedTaggedDomainValue(NUMERIC, formatDecimal(normalizeDecimal(adjusted)), "NUMERIC value");
|
|
118
123
|
}
|
|
119
124
|
export function exactNumericBinary(operator, left, right) {
|
|
120
125
|
const leftTagged = taggedDecimalParts(left);
|
|
@@ -156,7 +161,7 @@ export function exactNumericBinary(operator, left, right) {
|
|
|
156
161
|
}
|
|
157
162
|
result = { coefficient, scale };
|
|
158
163
|
}
|
|
159
|
-
return NUMERIC
|
|
164
|
+
return boundedTaggedDomainValue(NUMERIC, formatDecimal(normalizeDecimal(result)), "NUMERIC result");
|
|
160
165
|
}
|
|
161
166
|
export function exactNumericCompare(left, right) {
|
|
162
167
|
const leftTagged = taggedDecimalParts(left);
|
|
@@ -174,25 +179,116 @@ export function exactNumericCompare(left, right) {
|
|
|
174
179
|
const bc = b.coefficient * pow10(scale - b.scale);
|
|
175
180
|
return ac === bc ? 0 : ac < bc ? -1 : 1;
|
|
176
181
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
182
|
+
/**
|
|
183
|
+
* Serializes a JSON value while bounding traversal, nesting, and output before concatenation.
|
|
184
|
+
* Canonical mode sorts object names directly in the wire text (including integer-looking names,
|
|
185
|
+
* which JavaScript object enumeration would otherwise silently reorder).
|
|
186
|
+
*/
|
|
187
|
+
export function boundedJsonText(value, canonical, label = "JSON value") {
|
|
188
|
+
const state = { active: new Set(), pieces: [], items: 0, length: 0 };
|
|
189
|
+
encodeBoundedJson(value, canonical, label, 0, state);
|
|
190
|
+
return state.pieces.join("");
|
|
191
|
+
}
|
|
192
|
+
function encodeBoundedJson(value, canonical, label, depth, state) {
|
|
193
|
+
state.items += 1;
|
|
194
|
+
if (state.items > MAX_SQL_STRUCTURED_VALUE_ITEMS) {
|
|
195
|
+
throw new RangeError(`${label} cannot exceed ${String(MAX_SQL_STRUCTURED_VALUE_ITEMS)} values and names`);
|
|
196
|
+
}
|
|
197
|
+
if (depth > MAX_SQL_STRUCTURED_VALUE_DEPTH) {
|
|
198
|
+
throw new RangeError(`${label} cannot exceed ${String(MAX_SQL_STRUCTURED_VALUE_DEPTH)} levels`);
|
|
199
|
+
}
|
|
200
|
+
if (value === null) {
|
|
201
|
+
appendJsonPiece(state, "null", label);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
if (typeof value === "boolean") {
|
|
205
|
+
appendJsonPiece(state, value ? "true" : "false", label);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
if (typeof value === "number") {
|
|
209
|
+
if (!Number.isFinite(value))
|
|
210
|
+
throw new TypeError("JSON numbers must be finite");
|
|
211
|
+
appendJsonPiece(state, JSON.stringify(value), label);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
if (typeof value === "string") {
|
|
215
|
+
assertBoundedDomainString(value, label);
|
|
216
|
+
appendJsonPiece(state, JSON.stringify(value), label);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
if (value instanceof Date) {
|
|
220
|
+
const timestamp = dateIsoString(value);
|
|
221
|
+
appendJsonPiece(state, JSON.stringify(timestamp), label);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
if (typeof value !== "object")
|
|
225
|
+
throw new TypeError(`${label} cannot be represented as JSON`);
|
|
226
|
+
const object = value;
|
|
227
|
+
if (state.active.has(object))
|
|
228
|
+
throw new TypeError(`${label} cannot contain a cycle`);
|
|
229
|
+
state.active.add(object);
|
|
230
|
+
try {
|
|
231
|
+
if (Array.isArray(value)) {
|
|
232
|
+
appendJsonPiece(state, "[", label);
|
|
233
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
234
|
+
if (index > 0)
|
|
235
|
+
appendJsonPiece(state, ",", label);
|
|
236
|
+
encodeBoundedJson(value[index], canonical, label, depth + 1, state);
|
|
237
|
+
}
|
|
238
|
+
appendJsonPiece(state, "]", label);
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
const prototype = Object.getPrototypeOf(value);
|
|
242
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
243
|
+
throw new TypeError(`${label} accepts only JSON objects, arrays, and scalar values`);
|
|
244
|
+
}
|
|
245
|
+
const record = value;
|
|
246
|
+
// Object.keys() would allocate one array slot per property before the item cap can fire.
|
|
247
|
+
// Plain JSON objects need no prototype walk, so collect at most the admitted count instead.
|
|
248
|
+
const keys = [];
|
|
249
|
+
for (const key in record) {
|
|
250
|
+
if (!Object.hasOwn(record, key))
|
|
251
|
+
continue;
|
|
252
|
+
if (keys.length >= MAX_SQL_STRUCTURED_VALUE_ITEMS - state.items) {
|
|
253
|
+
throw new RangeError(`${label} cannot exceed ${String(MAX_SQL_STRUCTURED_VALUE_ITEMS)} values and names`);
|
|
254
|
+
}
|
|
255
|
+
keys.push(key);
|
|
185
256
|
}
|
|
186
|
-
if (
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
257
|
+
if (canonical)
|
|
258
|
+
keys.sort();
|
|
259
|
+
appendJsonPiece(state, "{", label);
|
|
260
|
+
for (let index = 0; index < keys.length; index += 1) {
|
|
261
|
+
const key = keys[index] ?? "";
|
|
262
|
+
state.items += 1;
|
|
263
|
+
if (state.items > MAX_SQL_STRUCTURED_VALUE_ITEMS) {
|
|
264
|
+
throw new RangeError(`${label} cannot exceed ${String(MAX_SQL_STRUCTURED_VALUE_ITEMS)} values and names`);
|
|
265
|
+
}
|
|
266
|
+
assertBoundedDomainString(key, `${label} object name`);
|
|
267
|
+
if (index > 0)
|
|
268
|
+
appendJsonPiece(state, ",", label);
|
|
269
|
+
appendJsonPiece(state, JSON.stringify(key), label);
|
|
270
|
+
appendJsonPiece(state, ":", label);
|
|
271
|
+
encodeBoundedJson(record[key], canonical, label, depth + 1, state);
|
|
192
272
|
}
|
|
193
|
-
|
|
194
|
-
}
|
|
195
|
-
|
|
273
|
+
appendJsonPiece(state, "}", label);
|
|
274
|
+
}
|
|
275
|
+
finally {
|
|
276
|
+
state.active.delete(object);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
function appendJsonPiece(state, piece, label) {
|
|
280
|
+
const next = state.length + piece.length;
|
|
281
|
+
if (!Number.isSafeInteger(next) || next > MAX_SQL_SCALAR_RESULT_CHARACTERS) {
|
|
282
|
+
throw new RangeError(`${label} exceeds ${String(MAX_SQL_SCALAR_RESULT_CHARACTERS)} characters`);
|
|
283
|
+
}
|
|
284
|
+
state.length = next;
|
|
285
|
+
state.pieces.push(piece);
|
|
286
|
+
}
|
|
287
|
+
function assertBoundedDomainString(value, label) {
|
|
288
|
+
if (value.length > MAX_SQL_SCALAR_RESULT_CHARACTERS) {
|
|
289
|
+
throw new RangeError(`${label} exceeds ${String(MAX_SQL_SCALAR_RESULT_CHARACTERS)} characters`);
|
|
290
|
+
}
|
|
291
|
+
assertWellFormedString(value, label);
|
|
196
292
|
}
|
|
197
293
|
export function jsonDomainValue(value, binary) {
|
|
198
294
|
if (value === null || value === undefined)
|
|
@@ -205,6 +301,7 @@ export function jsonDomainValue(value, binary) {
|
|
|
205
301
|
? JSON_VALUE
|
|
206
302
|
: undefined;
|
|
207
303
|
const source = prefix === undefined ? value : value.slice(prefix.length);
|
|
304
|
+
assertBoundedDomainString(source, "JSON value");
|
|
208
305
|
try {
|
|
209
306
|
parsed = JSON.parse(source);
|
|
210
307
|
}
|
|
@@ -212,14 +309,15 @@ export function jsonDomainValue(value, binary) {
|
|
|
212
309
|
throw new TypeError("Invalid JSON value");
|
|
213
310
|
}
|
|
214
311
|
}
|
|
215
|
-
const text = binary ?
|
|
216
|
-
return (binary ? JSONB_VALUE : JSON_VALUE
|
|
312
|
+
const text = boundedJsonText(parsed, binary, binary ? "JSONB value" : "JSON value");
|
|
313
|
+
return boundedTaggedDomainValue(binary ? JSONB_VALUE : JSON_VALUE, text, binary ? "JSONB value" : "JSON value");
|
|
217
314
|
}
|
|
218
315
|
export function uuidDomainValue(value) {
|
|
219
316
|
if (value === null || value === undefined)
|
|
220
317
|
return null;
|
|
221
318
|
if (typeof value !== "string")
|
|
222
319
|
throw new TypeError("UUID accepts a string value");
|
|
320
|
+
assertBoundedDomainString(value, "UUID value");
|
|
223
321
|
const source = (value.startsWith(UUID_VALUE) ? value.slice(UUID_VALUE.length) : value).toLowerCase();
|
|
224
322
|
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(source)) {
|
|
225
323
|
throw new TypeError(`Invalid UUID value: ${value}`);
|
|
@@ -231,6 +329,7 @@ export function timeDomainValue(value) {
|
|
|
231
329
|
return null;
|
|
232
330
|
if (typeof value !== "string")
|
|
233
331
|
throw new TypeError("TIME accepts a string value");
|
|
332
|
+
assertBoundedDomainString(value, "TIME value");
|
|
234
333
|
const source = value.startsWith(TIME_VALUE) ? value.slice(TIME_VALUE.length) : value;
|
|
235
334
|
const match = /^(\d{2}):(\d{2})(?::(\d{2})(\.\d{1,6})?)?$/.exec(source);
|
|
236
335
|
const hours = match?.[1];
|
|
@@ -248,10 +347,26 @@ export function timeDomainValue(value) {
|
|
|
248
347
|
export function intervalDomainValue(value) {
|
|
249
348
|
if (value === null || value === undefined)
|
|
250
349
|
return null;
|
|
251
|
-
if (typeof value === "string" && value.startsWith(INTERVAL_VALUE))
|
|
252
|
-
return value;
|
|
253
350
|
if (typeof value !== "string")
|
|
254
351
|
throw new TypeError("INTERVAL accepts a string value");
|
|
352
|
+
assertBoundedDomainString(value, "INTERVAL value");
|
|
353
|
+
if (value.startsWith(INTERVAL_VALUE)) {
|
|
354
|
+
const source = value.slice(INTERVAL_VALUE.length);
|
|
355
|
+
let decoded;
|
|
356
|
+
try {
|
|
357
|
+
decoded = JSON.parse(source);
|
|
358
|
+
}
|
|
359
|
+
catch {
|
|
360
|
+
throw new TypeError("Invalid tagged INTERVAL value");
|
|
361
|
+
}
|
|
362
|
+
if (!Array.isArray(decoded) ||
|
|
363
|
+
decoded.length !== 3 ||
|
|
364
|
+
!decoded.every((part) => Number.isSafeInteger(part)) ||
|
|
365
|
+
JSON.stringify(decoded) !== source) {
|
|
366
|
+
throw new TypeError("Invalid tagged INTERVAL value");
|
|
367
|
+
}
|
|
368
|
+
return value;
|
|
369
|
+
}
|
|
255
370
|
const source = value.trim();
|
|
256
371
|
let months = 0;
|
|
257
372
|
let days = 0;
|
|
@@ -288,23 +403,43 @@ export function intervalDomainValue(value) {
|
|
|
288
403
|
!Number.isSafeInteger(microseconds)) {
|
|
289
404
|
throw new TypeError(`Invalid INTERVAL value: ${value}`);
|
|
290
405
|
}
|
|
291
|
-
return INTERVAL_VALUE
|
|
406
|
+
return boundedTaggedDomainValue(INTERVAL_VALUE, JSON.stringify([months, days, microseconds]), "INTERVAL value");
|
|
292
407
|
}
|
|
293
408
|
export function arrayDomainValue(values) {
|
|
294
|
-
return ARRAY_VALUE
|
|
409
|
+
return boundedTaggedDomainValue(ARRAY_VALUE, boundedJsonText(values.map(externalSqlDomainValue), true, "ARRAY value"), "ARRAY value");
|
|
295
410
|
}
|
|
296
411
|
export function enumDomainValue(value, name, values) {
|
|
297
412
|
if (value === null || value === undefined)
|
|
298
413
|
return null;
|
|
299
414
|
if (typeof value !== "string")
|
|
300
415
|
throw new TypeError(`Enum ${name} accepts a string value`);
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
416
|
+
assertBoundedDomainString(value, `Enum ${name} value`);
|
|
417
|
+
let source = value;
|
|
418
|
+
// Enum labels are arbitrary strings. Prefer an exact declared label before recognizing the
|
|
419
|
+
// internal tag namespace, so a legal label can begin with that prefix just like ordinary TEXT.
|
|
420
|
+
if (!values.includes(value) && value.startsWith(ENUM_VALUE)) {
|
|
421
|
+
const encoded = value.slice(ENUM_VALUE.length);
|
|
422
|
+
let decoded;
|
|
423
|
+
try {
|
|
424
|
+
decoded = JSON.parse(encoded);
|
|
425
|
+
}
|
|
426
|
+
catch {
|
|
427
|
+
throw new TypeError(`Invalid tagged enum ${name} value`);
|
|
428
|
+
}
|
|
429
|
+
if (!Array.isArray(decoded) ||
|
|
430
|
+
decoded.length !== 3 ||
|
|
431
|
+
decoded[0] !== name ||
|
|
432
|
+
!Number.isSafeInteger(decoded[1]) ||
|
|
433
|
+
typeof decoded[2] !== "string" ||
|
|
434
|
+
JSON.stringify(decoded) !== encoded) {
|
|
435
|
+
throw new TypeError(`Invalid tagged enum ${name} value`);
|
|
436
|
+
}
|
|
437
|
+
source = decoded[2];
|
|
438
|
+
}
|
|
304
439
|
const index = values.indexOf(source);
|
|
305
440
|
if (index === -1)
|
|
306
441
|
throw new TypeError(`${source} is not a value of enum ${name}`);
|
|
307
|
-
return ENUM_VALUE
|
|
442
|
+
return boundedTaggedDomainValue(ENUM_VALUE, boundedJsonText([name, index, source], false, `Enum ${name} value`), `Enum ${name} value`);
|
|
308
443
|
}
|
|
309
444
|
export function enumDomainCompare(left, right) {
|
|
310
445
|
if (typeof left !== "string" ||
|
|
@@ -340,6 +475,7 @@ export function normalizeSqlDomainValue(domain, value) {
|
|
|
340
475
|
if (typeof value !== "string")
|
|
341
476
|
throw new TypeError("ARRAY columns accept JSON array text");
|
|
342
477
|
const source = value.startsWith(ARRAY_VALUE) ? value.slice(ARRAY_VALUE.length) : value;
|
|
478
|
+
assertBoundedDomainString(source, "ARRAY value");
|
|
343
479
|
let parsed;
|
|
344
480
|
try {
|
|
345
481
|
parsed = JSON.parse(source);
|
|
@@ -349,9 +485,12 @@ export function normalizeSqlDomainValue(domain, value) {
|
|
|
349
485
|
}
|
|
350
486
|
if (!Array.isArray(parsed))
|
|
351
487
|
throw new TypeError("ARRAY value must be an array");
|
|
352
|
-
|
|
488
|
+
// JSON array text contains ordinary JSON strings. Do not interpret a string that happens to
|
|
489
|
+
// begin with Minnow's internal domain prefix as an already-tagged SQL scalar.
|
|
490
|
+
return boundedTaggedDomainValue(ARRAY_VALUE, boundedJsonText(parsed, true, "ARRAY value"), "ARRAY value");
|
|
353
491
|
}
|
|
354
492
|
const collators = new Map();
|
|
493
|
+
const MAX_COLLATOR_CACHE_ENTRIES = 64;
|
|
355
494
|
export function collatedDomainValue(value, collation) {
|
|
356
495
|
if (value === null || value === undefined)
|
|
357
496
|
return null;
|
|
@@ -359,16 +498,20 @@ export function collatedDomainValue(value, collation) {
|
|
|
359
498
|
if (typeof value !== "string" || typeof collation !== "string") {
|
|
360
499
|
throw new TypeError("COLLATE requires a string value and collation name");
|
|
361
500
|
}
|
|
501
|
+
assertBoundedDomainString(value, "COLLATE value");
|
|
502
|
+
assertBoundedDomainString(collation, "COLLATE name");
|
|
362
503
|
const locale = collation === "POSIX" ? "C" : collation;
|
|
363
|
-
if (locale !== "C"
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
504
|
+
if (locale !== "C")
|
|
505
|
+
collatorFor(locale, collation);
|
|
506
|
+
return boundedTaggedDomainValue(COLLATION_VALUE, boundedJsonText([locale, value], false, "COLLATE value"), "COLLATE value");
|
|
507
|
+
}
|
|
508
|
+
function boundedTaggedDomainValue(prefix, value, label) {
|
|
509
|
+
// The prefix is an internal physical tag and is removed at the JavaScript boundary. Limit the
|
|
510
|
+
// user-visible payload, while the fixed tag adds only a few bounded bytes in storage.
|
|
511
|
+
if (value.length > MAX_SQL_SCALAR_RESULT_CHARACTERS) {
|
|
512
|
+
throw new RangeError(`${label} exceeds ${String(MAX_SQL_SCALAR_RESULT_CHARACTERS)} characters`);
|
|
370
513
|
}
|
|
371
|
-
return
|
|
514
|
+
return prefix + value;
|
|
372
515
|
}
|
|
373
516
|
export function collatedDomainCompare(left, right) {
|
|
374
517
|
const decode = (value) => typeof value === "string" && value.startsWith(COLLATION_VALUE)
|
|
@@ -390,7 +533,29 @@ export function collatedDomainCompare(left, right) {
|
|
|
390
533
|
const rightValue = b?.[1] ?? right;
|
|
391
534
|
if (locale === "C")
|
|
392
535
|
return leftValue === rightValue ? 0 : leftValue < rightValue ? -1 : 1;
|
|
393
|
-
return (
|
|
536
|
+
return collatorFor(locale, locale).compare(leftValue, rightValue);
|
|
537
|
+
}
|
|
538
|
+
function collatorFor(locale, displayName) {
|
|
539
|
+
const cached = collators.get(locale);
|
|
540
|
+
if (cached !== undefined) {
|
|
541
|
+
collators.delete(locale);
|
|
542
|
+
collators.set(locale, cached);
|
|
543
|
+
return cached;
|
|
544
|
+
}
|
|
545
|
+
let created;
|
|
546
|
+
try {
|
|
547
|
+
created = new Intl.Collator(locale, { usage: "sort", sensitivity: "variant" });
|
|
548
|
+
}
|
|
549
|
+
catch {
|
|
550
|
+
throw new TypeError(`Unsupported collation: ${displayName}`);
|
|
551
|
+
}
|
|
552
|
+
if (collators.size >= MAX_COLLATOR_CACHE_ENTRIES) {
|
|
553
|
+
const oldest = collators.keys().next().value;
|
|
554
|
+
if (oldest !== undefined)
|
|
555
|
+
collators.delete(oldest);
|
|
556
|
+
}
|
|
557
|
+
collators.set(locale, created);
|
|
558
|
+
return created;
|
|
394
559
|
}
|
|
395
560
|
export function externalSqlDomainValue(value) {
|
|
396
561
|
if (typeof value !== "string")
|
|
@@ -418,4 +583,3 @@ export function externalSqlDomainValue(value) {
|
|
|
418
583
|
export function isSqlDomainValue(value) {
|
|
419
584
|
return typeof value === "string" && value.startsWith(PREFIX);
|
|
420
585
|
}
|
|
421
|
-
//# sourceMappingURL=sql-domains.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Catalog } from "./catalog.js";
|
|
2
|
-
import type { ExecuteResult, QueryOptions } from "./database.js";
|
|
2
|
+
import type { ExecuteResult, QueryCursorOptions, QueryOptions } from "./database.js";
|
|
3
3
|
import type { QueryResult, QueryValue } from "./query.js";
|
|
4
4
|
/**
|
|
5
5
|
* The stable SQL boundary used by clients and third-party adapters.
|
|
@@ -10,10 +10,10 @@ import type { QueryResult, QueryValue } from "./query.js";
|
|
|
10
10
|
*/
|
|
11
11
|
export interface MinnowSqlExecutor {
|
|
12
12
|
query(sql: string, options?: QueryOptions): Promise<QueryResult>;
|
|
13
|
+
queryCursor(sql: string, options?: QueryCursorOptions): AsyncIterableIterator<QueryResult>;
|
|
13
14
|
execute(sql: string, params?: readonly QueryValue[]): Promise<ExecuteResult>;
|
|
14
15
|
}
|
|
15
16
|
/** SQL execution plus the catalog surface schema-aware adapters need. */
|
|
16
17
|
export interface MinnowSqlDriver extends MinnowSqlExecutor {
|
|
17
18
|
introspect(): Promise<Catalog>;
|
|
18
19
|
}
|
|
19
|
-
//# sourceMappingURL=sql-driver.d.ts.map
|
package/dist/engine/sql-json.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { stringArgument } from "./sql-semantics.js";
|
|
2
2
|
import { externalSqlDomainValue } from "./sql-domains.js";
|
|
3
|
+
import { MAX_CACHEABLE_TEXT_CHARACTERS, MAX_SQL_SCALAR_RESULT_CHARACTERS } from "./cache-limits.js";
|
|
3
4
|
export function parseJsonPath(path, caller) {
|
|
4
5
|
const text = stringArgument(caller, path).trim();
|
|
6
|
+
if (text.length > MAX_CACHEABLE_TEXT_CHARACTERS) {
|
|
7
|
+
throw new RangeError(`${caller} path exceeds ${String(MAX_CACHEABLE_TEXT_CHARACTERS)} characters`);
|
|
8
|
+
}
|
|
5
9
|
if (!text.startsWith("$"))
|
|
6
10
|
throw new TypeError(`${caller} paths start at $`);
|
|
7
11
|
const steps = [];
|
|
@@ -42,8 +46,9 @@ export function parseJsonPath(path, caller) {
|
|
|
42
46
|
export function jsonAtPath(document, path, caller) {
|
|
43
47
|
const steps = parseJsonPath(path, caller);
|
|
44
48
|
let current;
|
|
49
|
+
const source = boundedJsonDocument(document, caller);
|
|
45
50
|
try {
|
|
46
|
-
current = JSON.parse(
|
|
51
|
+
current = JSON.parse(source);
|
|
47
52
|
}
|
|
48
53
|
catch {
|
|
49
54
|
// A document that is not JSON selects nothing rather than failing the whole statement,
|
|
@@ -75,6 +80,9 @@ export function jsonIsValid(document, kind) {
|
|
|
75
80
|
document = externalSqlDomainValue(document);
|
|
76
81
|
if (typeof document !== "string")
|
|
77
82
|
return false;
|
|
83
|
+
if (document.length > MAX_SQL_SCALAR_RESULT_CHARACTERS) {
|
|
84
|
+
throw new RangeError(`JSON document exceeds ${String(MAX_SQL_SCALAR_RESULT_CHARACTERS)} characters`);
|
|
85
|
+
}
|
|
78
86
|
let parsed;
|
|
79
87
|
try {
|
|
80
88
|
parsed = JSON.parse(document);
|
|
@@ -97,7 +105,7 @@ export function jsonIsValid(document, kind) {
|
|
|
97
105
|
/** A SQL value as its JSON counterpart: datetimes serialize as ISO text, like every cast. */
|
|
98
106
|
export function jsonValueOf(value) {
|
|
99
107
|
const external = externalSqlDomainValue(value);
|
|
100
|
-
return external instanceof Date ? external
|
|
108
|
+
return external instanceof Date ? dateIsoString(external) : external;
|
|
101
109
|
}
|
|
102
110
|
/**
|
|
103
111
|
* JSON_ARRAY(v, ...) and JSON_OBJECT(k, v, ...) (T811/T812). The omitted null-handling clause is
|
|
@@ -106,7 +114,8 @@ export function jsonValueOf(value) {
|
|
|
106
114
|
*/
|
|
107
115
|
export function jsonConstructor(name, values) {
|
|
108
116
|
if (name === "JSON_ARRAY") {
|
|
109
|
-
|
|
117
|
+
const members = values.map((value) => boundedJsonValue(value ?? null, "JSON_ARRAY value"));
|
|
118
|
+
return joinBoundedJson("[", members, "]", "JSON_ARRAY result");
|
|
110
119
|
}
|
|
111
120
|
const members = [];
|
|
112
121
|
for (let index = 0; index + 1 < values.length; index += 2) {
|
|
@@ -116,7 +125,7 @@ export function jsonConstructor(name, values) {
|
|
|
116
125
|
}
|
|
117
126
|
let key;
|
|
118
127
|
if (rawKey instanceof Date)
|
|
119
|
-
key = rawKey
|
|
128
|
+
key = dateIsoString(rawKey);
|
|
120
129
|
else if (typeof rawKey === "string")
|
|
121
130
|
key = rawKey;
|
|
122
131
|
else if (typeof rawKey === "number" || typeof rawKey === "boolean")
|
|
@@ -127,8 +136,45 @@ export function jsonConstructor(name, values) {
|
|
|
127
136
|
// Build JSON text directly. WITHOUT UNIQUE KEYS is the default, so duplicate names must be
|
|
128
137
|
// preserved; assigning through a JavaScript object would collapse them and mishandle
|
|
129
138
|
// special names such as "__proto__".
|
|
130
|
-
|
|
139
|
+
const encodedKey = boundedJsonValue(key, "JSON_OBJECT key");
|
|
140
|
+
const encodedValue = boundedJsonValue(member ?? null, "JSON_OBJECT value");
|
|
141
|
+
members.push(`${encodedKey}:${encodedValue}`);
|
|
142
|
+
}
|
|
143
|
+
return joinBoundedJson("{", members, "}", "JSON_OBJECT result");
|
|
144
|
+
}
|
|
145
|
+
function boundedJsonDocument(value, caller) {
|
|
146
|
+
const document = stringArgument(caller, externalSqlDomainValue(value));
|
|
147
|
+
if (document.length > MAX_SQL_SCALAR_RESULT_CHARACTERS) {
|
|
148
|
+
throw new RangeError(`${caller} document exceeds ${String(MAX_SQL_SCALAR_RESULT_CHARACTERS)} characters`);
|
|
149
|
+
}
|
|
150
|
+
return document;
|
|
151
|
+
}
|
|
152
|
+
function boundedJsonValue(value, label) {
|
|
153
|
+
const normalized = jsonValueOf(value);
|
|
154
|
+
if (typeof normalized === "string" && normalized.length > MAX_SQL_SCALAR_RESULT_CHARACTERS) {
|
|
155
|
+
throw new RangeError(`${label} exceeds ${String(MAX_SQL_SCALAR_RESULT_CHARACTERS)} characters`);
|
|
156
|
+
}
|
|
157
|
+
const encoded = JSON.stringify(normalized);
|
|
158
|
+
if (typeof encoded !== "string")
|
|
159
|
+
throw new TypeError(`${label} is not JSON serializable`);
|
|
160
|
+
if (encoded.length > MAX_SQL_SCALAR_RESULT_CHARACTERS) {
|
|
161
|
+
throw new RangeError(`${label} exceeds ${String(MAX_SQL_SCALAR_RESULT_CHARACTERS)} characters`);
|
|
162
|
+
}
|
|
163
|
+
return encoded;
|
|
164
|
+
}
|
|
165
|
+
function joinBoundedJson(prefix, members, suffix, label) {
|
|
166
|
+
let length = prefix.length + suffix.length;
|
|
167
|
+
for (const member of members) {
|
|
168
|
+
length += member.length;
|
|
169
|
+
if (!Number.isSafeInteger(length) || length > MAX_SQL_SCALAR_RESULT_CHARACTERS) {
|
|
170
|
+
throw new RangeError(`${label} exceeds ${String(MAX_SQL_SCALAR_RESULT_CHARACTERS)} characters`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (members.length > 1)
|
|
174
|
+
length += members.length - 1;
|
|
175
|
+
if (!Number.isSafeInteger(length) || length > MAX_SQL_SCALAR_RESULT_CHARACTERS) {
|
|
176
|
+
throw new RangeError(`${label} exceeds ${String(MAX_SQL_SCALAR_RESULT_CHARACTERS)} characters`);
|
|
131
177
|
}
|
|
132
|
-
return
|
|
178
|
+
return `${prefix}${members.join(",")}${suffix}`;
|
|
133
179
|
}
|
|
134
|
-
|
|
180
|
+
import { dateIsoString } from "../date-value.js";
|
|
@@ -18,10 +18,14 @@ export declare function encodeSqlEqualityValue(value: unknown): readonly unknown
|
|
|
18
18
|
* integer and clamps to 0..30, with ties rounded away from zero.
|
|
19
19
|
*/
|
|
20
20
|
export declare function roundSqlNumber(value: number, precision?: number): number;
|
|
21
|
-
/**
|
|
22
|
-
export
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
/** A whole-string SQL pattern matcher. Unlike RegExp, test never coerces its input. */
|
|
22
|
+
export interface SqlPatternMatcher {
|
|
23
|
+
test(value: string): boolean;
|
|
24
|
+
}
|
|
25
|
+
/** Compiles SQL LIKE without exposing input to the host regular-expression engine. */
|
|
26
|
+
export declare function compileLikePattern(pattern: string, caseInsensitive?: boolean, escape?: string): SqlPatternMatcher;
|
|
27
|
+
/** PostgreSQL SIMILAR TO compiled to a Thompson NFA with bounded deterministic work. */
|
|
28
|
+
export declare function compileSimilarPattern(pattern: string, escape?: string): SqlPatternMatcher;
|
|
25
29
|
/** Defines an own enumerable result-column property, including the special name `__proto__`. */
|
|
26
30
|
export declare function defineSqlResultProperty(target: Record<string, unknown>, name: string, value: unknown): void;
|
|
27
31
|
/**
|
|
@@ -30,4 +34,3 @@ export declare function defineSqlResultProperty(target: Record<string, unknown>,
|
|
|
30
34
|
* a silent coercion here would make `LENGTH(42)` answer instead of failing.
|
|
31
35
|
*/
|
|
32
36
|
export declare function stringArgument(name: string, value: unknown): string;
|
|
33
|
-
//# sourceMappingURL=sql-semantics.d.ts.map
|