@rindle/query-compiler 0.4.3
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/LICENSE +201 -0
- package/README.md +42 -0
- package/dist/ast.d.ts +2 -0
- package/dist/ast.d.ts.map +1 -0
- package/dist/ast.js +2 -0
- package/dist/ast.js.map +1 -0
- package/dist/catalog.d.ts +51 -0
- package/dist/catalog.d.ts.map +1 -0
- package/dist/catalog.js +7 -0
- package/dist/catalog.js.map +1 -0
- package/dist/complete-ordering.d.ts +3 -0
- package/dist/complete-ordering.d.ts.map +1 -0
- package/dist/complete-ordering.js +44 -0
- package/dist/complete-ordering.js.map +1 -0
- package/dist/dialect.d.ts +49 -0
- package/dist/dialect.d.ts.map +1 -0
- package/dist/dialect.js +35 -0
- package/dist/dialect.js.map +1 -0
- package/dist/errors.d.ts +13 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +26 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -0
- package/dist/pg-types.d.ts +20 -0
- package/dist/pg-types.d.ts.map +1 -0
- package/dist/pg-types.js +107 -0
- package/dist/pg-types.js.map +1 -0
- package/dist/postgres.d.ts +7 -0
- package/dist/postgres.d.ts.map +1 -0
- package/dist/postgres.js +134 -0
- package/dist/postgres.js.map +1 -0
- package/dist/reject.d.ts +3 -0
- package/dist/reject.d.ts.map +1 -0
- package/dist/reject.js +74 -0
- package/dist/reject.js.map +1 -0
- package/dist/walker.d.ts +11 -0
- package/dist/walker.d.ts.map +1 -0
- package/dist/walker.js +350 -0
- package/dist/walker.js.map +1 -0
- package/package.json +39 -0
- package/src/ast.ts +18 -0
- package/src/catalog.ts +59 -0
- package/src/complete-ordering.ts +48 -0
- package/src/dialect.ts +85 -0
- package/src/errors.ts +30 -0
- package/src/index.ts +74 -0
- package/src/pg-types.ts +119 -0
- package/src/postgres.ts +152 -0
- package/src/reject.ts +71 -0
- package/src/walker.ts +418 -0
package/dist/pg-types.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// Postgres type classification — the predicates that decide which `::text::<type>` cast a
|
|
2
|
+
// value gets (§6.2). Ported verbatim from mono's `zero-cache/src/types/pg-data-type.ts` (the
|
|
3
|
+
// maps + `formatTypeForLookup`), plus the temporal helpers the projection reconciliation needs.
|
|
4
|
+
// Keeping these faithful to z2s is what keeps the filter-side casts bug-for-bug aligned.
|
|
5
|
+
// The map *values* are irrelevant to the predicates (they test key membership); kept as in
|
|
6
|
+
// mono for fidelity and in case the value-type mapping is needed later.
|
|
7
|
+
const pgToZqlNumericTypeMap = Object.freeze({
|
|
8
|
+
smallint: "number",
|
|
9
|
+
integer: "number",
|
|
10
|
+
int: "number",
|
|
11
|
+
int2: "number",
|
|
12
|
+
int4: "number",
|
|
13
|
+
int8: "number",
|
|
14
|
+
bigint: "number",
|
|
15
|
+
smallserial: "number",
|
|
16
|
+
serial: "number",
|
|
17
|
+
serial2: "number",
|
|
18
|
+
serial4: "number",
|
|
19
|
+
serial8: "number",
|
|
20
|
+
bigserial: "number",
|
|
21
|
+
decimal: "number",
|
|
22
|
+
numeric: "number",
|
|
23
|
+
real: "number",
|
|
24
|
+
"double precision": "number",
|
|
25
|
+
float: "number",
|
|
26
|
+
float4: "number",
|
|
27
|
+
float8: "number",
|
|
28
|
+
});
|
|
29
|
+
const pgToZqlNativeStringTypeMap = Object.freeze({
|
|
30
|
+
bpchar: "string",
|
|
31
|
+
character: "string",
|
|
32
|
+
"character varying": "string",
|
|
33
|
+
text: "string",
|
|
34
|
+
varchar: "string",
|
|
35
|
+
});
|
|
36
|
+
const pgToZqlTextRepresentedTypeMap = Object.freeze({
|
|
37
|
+
cidr: "string",
|
|
38
|
+
ean13: "string",
|
|
39
|
+
inet: "string",
|
|
40
|
+
isbn: "string",
|
|
41
|
+
isbn13: "string",
|
|
42
|
+
ismn: "string",
|
|
43
|
+
ismn13: "string",
|
|
44
|
+
issn: "string",
|
|
45
|
+
issn13: "string",
|
|
46
|
+
macaddr: "string",
|
|
47
|
+
macaddr8: "string",
|
|
48
|
+
pg_lsn: "string",
|
|
49
|
+
upc: "string",
|
|
50
|
+
uuid: "string",
|
|
51
|
+
});
|
|
52
|
+
const pgToZqlStringTypeMap = Object.freeze({
|
|
53
|
+
...pgToZqlNativeStringTypeMap,
|
|
54
|
+
...pgToZqlTextRepresentedTypeMap,
|
|
55
|
+
});
|
|
56
|
+
/** Strips args (the `(32)` in `char(32)`) and lowercases — mono's `formatTypeForLookup`.
|
|
57
|
+
* Every predicate normalizes through this, so `VARCHAR(255)` / `UUID` classify correctly. */
|
|
58
|
+
export function formatTypeForLookup(pgType) {
|
|
59
|
+
const startOfArgs = pgType.indexOf("(");
|
|
60
|
+
if (startOfArgs === -1)
|
|
61
|
+
return pgType.toLowerCase();
|
|
62
|
+
return pgType.toLowerCase().substring(0, startOfArgs);
|
|
63
|
+
}
|
|
64
|
+
export function isPgNumberType(pgType) {
|
|
65
|
+
return Object.hasOwn(pgToZqlNumericTypeMap, formatTypeForLookup(pgType));
|
|
66
|
+
}
|
|
67
|
+
export function isPgNativeStringType(pgType) {
|
|
68
|
+
return Object.hasOwn(pgToZqlNativeStringTypeMap, formatTypeForLookup(pgType));
|
|
69
|
+
}
|
|
70
|
+
export function isPgTextRepresentedType(pgType) {
|
|
71
|
+
return Object.hasOwn(pgToZqlTextRepresentedTypeMap, formatTypeForLookup(pgType));
|
|
72
|
+
}
|
|
73
|
+
export function isPgStringType(pgType) {
|
|
74
|
+
return Object.hasOwn(pgToZqlStringTypeMap, formatTypeForLookup(pgType));
|
|
75
|
+
}
|
|
76
|
+
// ── temporal helpers (projection reconciliation, §6.2) ──────────────────────────────────────
|
|
77
|
+
const TEMPORAL_TYPES = new Set([
|
|
78
|
+
"date",
|
|
79
|
+
"time",
|
|
80
|
+
"time without time zone",
|
|
81
|
+
"time with time zone",
|
|
82
|
+
"timetz",
|
|
83
|
+
"timestamp",
|
|
84
|
+
"timestamp without time zone",
|
|
85
|
+
"timestamp with time zone",
|
|
86
|
+
"timestamptz",
|
|
87
|
+
]);
|
|
88
|
+
/** A temporal type projected as epoch-ms (outbound) and reconciled via `to_timestamp`/interval
|
|
89
|
+
* (inbound). Everything else projects raw. */
|
|
90
|
+
export function isTemporalType(pgType) {
|
|
91
|
+
return TEMPORAL_TYPES.has(formatTypeForLookup(pgType));
|
|
92
|
+
}
|
|
93
|
+
// Offset-bearing time-of-day types: `EXTRACT(EPOCH FROM …)` can go negative (e.g. 01:00+02 =
|
|
94
|
+
// 23:00 UTC prev day = -3600s), so the outbound projection normalizes into 0..86_400_000 ms.
|
|
95
|
+
const TIME_OF_DAY_TZ_TYPES = new Set(["timetz", "time with time zone"]);
|
|
96
|
+
/**
|
|
97
|
+
* Whether an outbound temporal projection needs the `mod 1 day` normalization. **Deliberately
|
|
98
|
+
* narrower than z2s's `selectIdent`**, which (via a switch fall-through) also normalizes
|
|
99
|
+
* `timestamptz` — that would collapse a full timestamp to time-of-day. The design (§6.2) is
|
|
100
|
+
* explicit that `timestamptz` projects as plain epoch-ms and only the offset-bearing
|
|
101
|
+
* *time-of-day* types are modular-normalized; the §9 parity harness against the engine's value
|
|
102
|
+
* model is the check that keeps this honest.
|
|
103
|
+
*/
|
|
104
|
+
export function needsTimeOfDayNormalization(pgType) {
|
|
105
|
+
return TIME_OF_DAY_TZ_TYPES.has(formatTypeForLookup(pgType));
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=pg-types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pg-types.js","sourceRoot":"","sources":["../src/pg-types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Dialect } from "./dialect.ts";
|
|
2
|
+
/**
|
|
3
|
+
* The Postgres dialect — the BYO-Postgres server-mutator read target. Emits `(sql, params)`
|
|
4
|
+
* with `$N::text::<type>` parameters and never imports a driver (Invariant 7).
|
|
5
|
+
*/
|
|
6
|
+
export declare const postgresDialect: Dialect;
|
|
7
|
+
//# sourceMappingURL=postgres.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../src/postgres.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,OAAO,EAAa,MAAM,cAAc,CAAC;AAqHvD;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,OAY7B,CAAC"}
|
package/dist/postgres.js
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// The Postgres dialect leaf (§6.1/§6.2) — the product target. The walker is shared with the
|
|
2
|
+
// SQLite oracle; this file supplies only the Postgres-specific leaves:
|
|
3
|
+
//
|
|
4
|
+
// - JSON assembly via `jsonb_build_object` / `jsonb_agg` / `'[]'::jsonb` (the design's §6.1
|
|
5
|
+
// choice — the direct analogue of `rindle-d2s`'s `json_object`, NOT z2s's `row_to_json`);
|
|
6
|
+
// - explicit `ASC NULLS FIRST` / `DESC NULLS LAST` (§8 — Postgres's default is the opposite
|
|
7
|
+
// of the engine's null-low order);
|
|
8
|
+
// - the outbound projection reconciliation (temporal → epoch-ms);
|
|
9
|
+
// - the inbound filter/paging cast strategy `$N::text::<type>`, ported verbatim from z2s's
|
|
10
|
+
// `sql.ts` — the `::text` intermediate pins the driver's parameter description to text, and
|
|
11
|
+
// the `::<type>` half is value-model↔native reconciliation (§6.2). Values are parameterized.
|
|
12
|
+
//
|
|
13
|
+
// NOTE: `$N` numbering is per-occurrence (no de-duplication of repeated values). z2s reuses a
|
|
14
|
+
// placeholder for a repeated (value,type); that is a plan-cache optimization, not correctness —
|
|
15
|
+
// deferred (203 §7 posture).
|
|
16
|
+
import { formatTypeForLookup, isPgNativeStringType, isPgNumberType, isPgStringType, isPgTextRepresentedType, isTemporalType, needsTimeOfDayNormalization, } from "./pg-types.js";
|
|
17
|
+
function quoteIdent(name) {
|
|
18
|
+
return `"${name.replace(/"/g, '""')}"`;
|
|
19
|
+
}
|
|
20
|
+
/** The Postgres cast for a literal whose type is inferred from its JS value (no column). */
|
|
21
|
+
function pgTypeForLiteralType(litType) {
|
|
22
|
+
// `double precision` is IEEE-754 like a JS number, so it round-trips any zql number exactly.
|
|
23
|
+
return litType === "boolean" ? "boolean" : litType === "number" ? "double precision" : "text";
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The inbound value↔native cast for a filter/paging value against a known column type — the
|
|
27
|
+
* verbatim port of z2s's `formatCommonToSingularAndPlural` (`sql.ts:194`). `vp` is the value
|
|
28
|
+
* placeholder (`$N`, or `value` inside an array unnest). `isComparison` widens strings to bare
|
|
29
|
+
* `text` and numbers to `double precision` so neither is forced to the column's width/precision.
|
|
30
|
+
*/
|
|
31
|
+
function pgCast(vp, type, isEnum, isComparison) {
|
|
32
|
+
const t = formatTypeForLookup(type);
|
|
33
|
+
// Temporal: epoch-ms ↔ native. The zone-naive spellings get `AT TIME ZONE 'UTC'`.
|
|
34
|
+
if (t === "timestamptz" || t === "timestamp with time zone") {
|
|
35
|
+
return `to_timestamp(${vp}::text::numeric / 1000.0)`;
|
|
36
|
+
}
|
|
37
|
+
if (t === "date" || t === "timestamp" || t === "timestamp without time zone") {
|
|
38
|
+
return `to_timestamp(${vp}::text::numeric / 1000.0) AT TIME ZONE 'UTC'`;
|
|
39
|
+
}
|
|
40
|
+
if (t === "timetz" || t === "time with time zone") {
|
|
41
|
+
return `(${vp}::text::int * interval'1ms')::time`;
|
|
42
|
+
}
|
|
43
|
+
if (t === "time" || t === "time without time zone") {
|
|
44
|
+
return `(${vp}::text::int * interval'1ms')::time AT TIME ZONE 'UTC'`;
|
|
45
|
+
}
|
|
46
|
+
if (t === "uuid")
|
|
47
|
+
return `${vp}::text::uuid`;
|
|
48
|
+
if (isEnum)
|
|
49
|
+
return `${vp}::text::"${type}"`;
|
|
50
|
+
if (isPgNativeStringType(type))
|
|
51
|
+
return isComparison ? `${vp}::text` : `${vp}::text::${type}`;
|
|
52
|
+
if (isPgTextRepresentedType(type))
|
|
53
|
+
return `${vp}::text::${type}`;
|
|
54
|
+
if (isPgNumberType(type)) {
|
|
55
|
+
return isComparison ? `${vp}::text::double precision` : `${vp}::text::${type}`;
|
|
56
|
+
}
|
|
57
|
+
return `${vp}::text::${type}`;
|
|
58
|
+
}
|
|
59
|
+
/** The text-pinned serialization of a bound value — z2s's `stringify` (`sql.ts:120`). Strings
|
|
60
|
+
* and enum/string columns bind their raw text; everything else binds its JSON text form so the
|
|
61
|
+
* `$N::text::…` cast re-parses it authoritatively. */
|
|
62
|
+
function stringifyForColumn(value, col) {
|
|
63
|
+
if (value === null)
|
|
64
|
+
return null;
|
|
65
|
+
if (col.isArray)
|
|
66
|
+
return JSON.stringify(value);
|
|
67
|
+
if (col.isEnum || isPgStringType(col.type))
|
|
68
|
+
return String(value);
|
|
69
|
+
return JSON.stringify(value);
|
|
70
|
+
}
|
|
71
|
+
function bindValue(params, value, col, isComparison) {
|
|
72
|
+
const idx = params.length + 1;
|
|
73
|
+
// Free literal (no column context): infer the cast from the JS type.
|
|
74
|
+
if (col === null) {
|
|
75
|
+
if (value === null) {
|
|
76
|
+
params.push(null);
|
|
77
|
+
return `$${idx}`;
|
|
78
|
+
}
|
|
79
|
+
const litType = typeof value;
|
|
80
|
+
params.push(litType === "string" ? value : JSON.stringify(value));
|
|
81
|
+
return `$${idx}::text::${pgTypeForLiteralType(litType)}`;
|
|
82
|
+
}
|
|
83
|
+
params.push(stringifyForColumn(value, col));
|
|
84
|
+
if (value === null && col.isArray)
|
|
85
|
+
return `$${idx}`;
|
|
86
|
+
if (col.isArray) {
|
|
87
|
+
// Unnest a JSON array param, casting each element (z2s's `formatPlural`, `sql.ts:260`).
|
|
88
|
+
return `ARRAY(SELECT ${pgCast("value", col.type, col.isEnum, isComparison)} FROM jsonb_array_elements_text($${idx}::text::jsonb))`;
|
|
89
|
+
}
|
|
90
|
+
return pgCast(`$${idx}`, col.type, col.isEnum, isComparison);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Project a stored column into the value model's representation (§6.2 outbound half). Temporal
|
|
94
|
+
* columns become **integer epoch-ms** — the value model always snaps to whole milliseconds, so
|
|
95
|
+
* `::bigint` rounds off the native µs resolution (unlike z2s, which projects the non-normalized
|
|
96
|
+
* path as an unsnapped float). Time-of-day-with-offset types additionally wrap `mod 1 day`.
|
|
97
|
+
* Enums (their label), uuid, numeric, json, and the scalar types project raw. See
|
|
98
|
+
* `needsTimeOfDayNormalization` for the deliberate divergence from z2s on `timestamptz`.
|
|
99
|
+
*
|
|
100
|
+
* The rounding mode (`::bigint` = round-half-to-even) must match pg-source's projector for exact
|
|
101
|
+
* parity — it is the one shared value-model contract (§10), not an independent choice here.
|
|
102
|
+
*/
|
|
103
|
+
function projectColumn(colRef, col) {
|
|
104
|
+
if (!col || col.isEnum || !isTemporalType(col.type))
|
|
105
|
+
return colRef;
|
|
106
|
+
const normalize = needsTimeOfDayNormalization(col.type);
|
|
107
|
+
const toMs = (epoch) => normalize ? `((${epoch})::bigint + 86400000) % 86400000` : `(${epoch})::bigint`;
|
|
108
|
+
if (col.isArray) {
|
|
109
|
+
return `CASE WHEN ${colRef} IS NULL THEN NULL ELSE ARRAY(SELECT ${toMs(`EXTRACT(EPOCH FROM unnest(${colRef})) * 1000`)}) END`;
|
|
110
|
+
}
|
|
111
|
+
return toMs(`EXTRACT(EPOCH FROM ${colRef}) * 1000`);
|
|
112
|
+
}
|
|
113
|
+
function orderDir(dir) {
|
|
114
|
+
// Engine order is null-low; Postgres's default is the opposite, so make it explicit (§8).
|
|
115
|
+
return dir === "asc" ? "ASC NULLS FIRST" : "DESC NULLS LAST";
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* The Postgres dialect — the BYO-Postgres server-mutator read target. Emits `(sql, params)`
|
|
119
|
+
* with `$N::text::<type>` parameters and never imports a driver (Invariant 7).
|
|
120
|
+
*/
|
|
121
|
+
export const postgresDialect = {
|
|
122
|
+
name: "postgres",
|
|
123
|
+
objectBuild: (parts) => `jsonb_build_object(${parts.join(", ")})`,
|
|
124
|
+
groupArray: (elem, orderSql) => `jsonb_agg(${elem}${orderSql})`,
|
|
125
|
+
emptyArray: "'[]'::jsonb",
|
|
126
|
+
reassertJson: (x) => x, // jsonb is a real type; no re-assert needed
|
|
127
|
+
trueLit: "TRUE",
|
|
128
|
+
falseLit: "FALSE",
|
|
129
|
+
orderDir,
|
|
130
|
+
projectColumn,
|
|
131
|
+
bindValue,
|
|
132
|
+
quoteIdent,
|
|
133
|
+
};
|
|
134
|
+
//# sourceMappingURL=postgres.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres.js","sourceRoot":"","sources":["../src/postgres.ts"],"names":[],"mappings":"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"}
|
package/dist/reject.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reject.d.ts","sourceRoot":"","sources":["../src/reject.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,GAAG,EAAa,MAAM,UAAU,CAAC;AAG/C,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,CAahD"}
|
package/dist/reject.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Reject AST features the compiler does not lower, anywhere in the tree — so an unhandled
|
|
2
|
+
// field fails loudly rather than producing silently-wrong SQL. Faithful port of
|
|
3
|
+
// `rindle-d2s`'s `reject_unsupported` family (lib.rs:194).
|
|
4
|
+
//
|
|
5
|
+
// `start` (a paging bound) is lowered as a keyset predicate, but only on the **materialized
|
|
6
|
+
// collections** the compiler emits (the root and each `related` child). It is *not* honored
|
|
7
|
+
// inside an EXISTS/NOT EXISTS subquery, nor inside a `count(child)` aggregate — a `start`
|
|
8
|
+
// there is rejected here rather than silently dropped.
|
|
9
|
+
import { unsupported } from "./errors.js";
|
|
10
|
+
export function rejectUnsupported(ast) {
|
|
11
|
+
for (const rel of ast.related ?? []) {
|
|
12
|
+
if (rel.subquery.aggregate) {
|
|
13
|
+
// A `count(child)` aggregate reduces its rows away: a `start`/`limit`/nested `related`
|
|
14
|
+
// would change the IVM count but `count_expr` ignores them — reject rather than diverge.
|
|
15
|
+
rejectAggregateSubquery(rel.subquery);
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
rejectUnsupported(rel.subquery); // collection: start honored, recurse
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
if (ast.where) {
|
|
22
|
+
rejectUnsupportedCond(ast.where);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function rejectAggregateSubquery(ast) {
|
|
26
|
+
if (ast.start)
|
|
27
|
+
throw unsupported("start inside a count aggregate");
|
|
28
|
+
if (ast.limit !== undefined)
|
|
29
|
+
throw unsupported("limit inside a count aggregate");
|
|
30
|
+
if (ast.related && ast.related.length > 0) {
|
|
31
|
+
throw unsupported("nested related inside a count aggregate");
|
|
32
|
+
}
|
|
33
|
+
if (ast.where)
|
|
34
|
+
rejectUnsupportedCond(ast.where);
|
|
35
|
+
}
|
|
36
|
+
function rejectUnsupportedCond(cond) {
|
|
37
|
+
switch (cond.type) {
|
|
38
|
+
case "simple":
|
|
39
|
+
return;
|
|
40
|
+
case "correlatedSubquery":
|
|
41
|
+
rejectStartAnywhere(cond.related.subquery); // EXISTS: its `start` is not honored
|
|
42
|
+
return;
|
|
43
|
+
case "and":
|
|
44
|
+
case "or":
|
|
45
|
+
for (const c of cond.conditions)
|
|
46
|
+
rejectUnsupportedCond(c);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/** Reject a `start` bound *anywhere* in `ast` (the EXISTS subtrees the compiler does not
|
|
51
|
+
* page). Conservative: rejects even positions a clever lowering might honor. */
|
|
52
|
+
function rejectStartAnywhere(ast) {
|
|
53
|
+
if (ast.start)
|
|
54
|
+
throw unsupported("start (paging) inside an EXISTS subquery");
|
|
55
|
+
for (const rel of ast.related ?? [])
|
|
56
|
+
rejectStartAnywhere(rel.subquery);
|
|
57
|
+
if (ast.where)
|
|
58
|
+
rejectStartAnywhereCond(ast.where);
|
|
59
|
+
}
|
|
60
|
+
function rejectStartAnywhereCond(cond) {
|
|
61
|
+
switch (cond.type) {
|
|
62
|
+
case "simple":
|
|
63
|
+
return;
|
|
64
|
+
case "correlatedSubquery":
|
|
65
|
+
rejectStartAnywhere(cond.related.subquery);
|
|
66
|
+
return;
|
|
67
|
+
case "and":
|
|
68
|
+
case "or":
|
|
69
|
+
for (const c of cond.conditions)
|
|
70
|
+
rejectStartAnywhereCond(c);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=reject.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reject.js","sourceRoot":"","sources":["../src/reject.ts"],"names":[],"mappings":"AAAA,0FAA0F;AAC1F,gFAAgF;AAChF,2DAA2D;AAC3D,EAAE;AACF,4FAA4F;AAC5F,4FAA4F;AAC5F,0FAA0F;AAC1F,uDAAuD;AAGvD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,UAAU,iBAAiB,CAAC,GAAQ;IACxC,KAAK,MAAM,GAAG,IAAI,GAAG,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;QACpC,IAAI,GAAG,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC;YAC3B,uFAAuF;YACvF,yFAAyF;YACzF,uBAAuB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACxC,CAAC;aAAM,CAAC;YACN,iBAAiB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,qCAAqC;QACxE,CAAC;IACH,CAAC;IACD,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;QACd,qBAAqB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC;AACH,CAAC;AAED,SAAS,uBAAuB,CAAC,GAAQ;IACvC,IAAI,GAAG,CAAC,KAAK;QAAE,MAAM,WAAW,CAAC,gCAAgC,CAAC,CAAC;IACnE,IAAI,GAAG,CAAC,KAAK,KAAK,SAAS;QAAE,MAAM,WAAW,CAAC,gCAAgC,CAAC,CAAC;IACjF,IAAI,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1C,MAAM,WAAW,CAAC,yCAAyC,CAAC,CAAC;IAC/D,CAAC;IACD,IAAI,GAAG,CAAC,KAAK;QAAE,qBAAqB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AAClD,CAAC;AAED,SAAS,qBAAqB,CAAC,IAAe;IAC5C,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,QAAQ;YACX,OAAO;QACT,KAAK,oBAAoB;YACvB,mBAAmB,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,qCAAqC;YACjF,OAAO;QACT,KAAK,KAAK,CAAC;QACX,KAAK,IAAI;YACP,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,UAAU;gBAAE,qBAAqB,CAAC,CAAC,CAAC,CAAC;YAC1D,OAAO;IACX,CAAC;AACH,CAAC;AAED;iFACiF;AACjF,SAAS,mBAAmB,CAAC,GAAQ;IACnC,IAAI,GAAG,CAAC,KAAK;QAAE,MAAM,WAAW,CAAC,0CAA0C,CAAC,CAAC;IAC7E,KAAK,MAAM,GAAG,IAAI,GAAG,CAAC,OAAO,IAAI,EAAE;QAAE,mBAAmB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACvE,IAAI,GAAG,CAAC,KAAK;QAAE,uBAAuB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACpD,CAAC;AAED,SAAS,uBAAuB,CAAC,IAAe;IAC9C,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,QAAQ;YACX,OAAO;QACT,KAAK,oBAAoB;YACvB,mBAAmB,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YAC3C,OAAO;QACT,KAAK,KAAK,CAAC;QACX,KAAK,IAAI;YACP,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,UAAU;gBAAE,uBAAuB,CAAC,CAAC,CAAC,CAAC;YAC5D,OAAO;IACX,CAAC;AACH,CAAC"}
|
package/dist/walker.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Ast } from "./ast.ts";
|
|
2
|
+
import type { Catalog } from "./catalog.ts";
|
|
3
|
+
import type { Dialect } from "./dialect.ts";
|
|
4
|
+
export interface Compiled {
|
|
5
|
+
sql: string;
|
|
6
|
+
params: unknown[];
|
|
7
|
+
}
|
|
8
|
+
/** Compile `ast` against `catalog` for `dialect`. `singular` ⇒ a `.one()` root (single
|
|
9
|
+
* object or `null`); else a plural root (a JSON array). Pure: mutates only a clone. */
|
|
10
|
+
export declare function compileWith(ast: Ast, catalog: Catalog, dialect: Dialect, singular: boolean): Compiled;
|
|
11
|
+
//# sourceMappingURL=walker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"walker.d.ts","sourceRoot":"","sources":["../src/walker.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,GAAG,EAQJ,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,OAAO,EAAc,MAAM,cAAc,CAAC;AACxD,OAAO,KAAK,EAAE,OAAO,EAAa,MAAM,cAAc,CAAC;AAgBvD,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,OAAO,EAAE,CAAC;CACnB;AAED;wFACwF;AACxF,wBAAgB,WAAW,CACzB,GAAG,EAAE,GAAG,EACR,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,OAAO,GAChB,QAAQ,CAOV"}
|