@kuindji/typed-sql 0.2.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 +11 -3
- package/dist/columns.d.ts +11 -3
- package/dist/columns.d.ts.map +1 -1
- package/dist/expressions.d.ts +84 -13
- package/dist/expressions.d.ts.map +1 -1
- package/dist/parsing/extract.d.ts +13 -9
- package/dist/parsing/extract.d.ts.map +1 -1
- package/dist/parsing/normalize.d.ts +9 -3
- package/dist/parsing/normalize.d.ts.map +1 -1
- package/dist/parsing/pg-literals.d.ts +10 -2
- package/dist/parsing/pg-literals.d.ts.map +1 -1
- package/dist/parsing/split.d.ts +27 -3
- package/dist/parsing/split.d.ts.map +1 -1
- package/dist/parsing/string-utils.d.ts +2 -4
- package/dist/parsing/string-utils.d.ts.map +1 -1
- package/dist/parsing/tokenize.d.ts +27 -17
- package/dist/parsing/tokenize.d.ts.map +1 -1
- package/dist/partial.d.ts +6 -6
- package/dist/partial.d.ts.map +1 -1
- package/dist/tables.d.ts +58 -13
- package/dist/tables.d.ts.map +1 -1
- package/dist/validation/dispatch.d.ts +7 -5
- package/dist/validation/dispatch.d.ts.map +1 -1
- package/dist/validation/joins.d.ts +3 -3
- package/dist/validation/joins.d.ts.map +1 -1
- package/dist/validation/return-derived.d.ts +8 -4
- package/dist/validation/return-derived.d.ts.map +1 -1
- package/dist/validation/return-derived.js.map +1 -1
- package/dist/validation/return-types.d.ts +1 -1
- package/dist/validation/return-types.d.ts.map +1 -1
- package/dist/validation/validate-columns.d.ts +27 -16
- package/dist/validation/validate-columns.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/columns.ts +168 -32
- package/src/expressions.ts +589 -63
- package/src/parsing/extract.ts +72 -32
- package/src/parsing/normalize.ts +114 -10
- package/src/parsing/pg-literals.ts +32 -10
- package/src/parsing/split.ts +236 -72
- package/src/parsing/string-utils.ts +15 -15
- package/src/parsing/tokenize.ts +224 -146
- package/src/partial.ts +9 -15
- package/src/tables.ts +546 -183
- package/src/validation/dispatch.ts +58 -52
- package/src/validation/joins.ts +15 -19
- package/src/validation/return-derived.ts +60 -4
- package/src/validation/return-types.ts +9 -3
- package/src/validation/validate-columns.ts +161 -67
package/src/expressions.ts
CHANGED
|
@@ -3,9 +3,9 @@ import type {
|
|
|
3
3
|
ColumnRef,
|
|
4
4
|
ColumnRefValidLooseWith,
|
|
5
5
|
ParseColumnRef,
|
|
6
|
-
|
|
6
|
+
QualifiedRefScan,
|
|
7
7
|
ResolveTableKey,
|
|
8
|
-
|
|
8
|
+
UnqualifiedRefScan,
|
|
9
9
|
UnqualifiedColumnValid
|
|
10
10
|
} from "./columns.js";
|
|
11
11
|
import type { AliasesInQuery, TablesInQuery } from "./tables.js";
|
|
@@ -23,7 +23,6 @@ import type {
|
|
|
23
23
|
SqlConstantType,
|
|
24
24
|
SplitBalancedParen,
|
|
25
25
|
SplitTopLevel,
|
|
26
|
-
TokenizeLoose,
|
|
27
26
|
Trim
|
|
28
27
|
} from "./parsing.js";
|
|
29
28
|
import type { AllTrue } from "./utils.js";
|
|
@@ -60,14 +59,28 @@ export type ExprToObject<
|
|
|
60
59
|
? MaybeNullableRow<RowTypeForTable<ResolveTableKey<CleanIdent<T>, Tables, Aliases, S>, S>, T, Nullable>
|
|
61
60
|
: ExprKey<E, Tables, Aliases, S> extends infer Key extends string | never
|
|
62
61
|
? Key extends string
|
|
63
|
-
? { [K in Key]: ApplyProjectionNull<ExprType<RawExpr, Tables, Aliases, S>, RawExpr, Tables, Aliases, S, Nullable> }
|
|
62
|
+
? { [K in Key]: NeverToUnknown<ApplyProjectionNull<ExprType<RawExpr, Tables, Aliases, S>, RawExpr, Tables, Aliases, S, Nullable>, RawExpr> }
|
|
64
63
|
: Record<string, unknown>
|
|
65
64
|
: Record<string, unknown>
|
|
66
65
|
: Alias extends string
|
|
67
|
-
? { [K in Alias]: ApplyProjectionNull<ExprType<RawExpr, Tables, Aliases, S>, RawExpr, Tables, Aliases, S, Nullable> }
|
|
66
|
+
? { [K in Alias]: NeverToUnknown<ApplyProjectionNull<ExprType<RawExpr, Tables, Aliases, S>, RawExpr, Tables, Aliases, S, Nullable>, RawExpr> }
|
|
68
67
|
: Record<string, unknown>
|
|
69
68
|
: Record<string, unknown>;
|
|
70
69
|
|
|
70
|
+
// A projected QUALIFIED ref that resolves to `never` (e.g. one qualified by a
|
|
71
|
+
// CTE name that the core path collected as a bogus base table — `input_ips.x`
|
|
72
|
+
// in `WITH input_ips(...) ... SELECT input_ips.x ... JOIN ...`) must surface as
|
|
73
|
+
// `unknown` in the ROW type — `never` is validation's reject signal, not a value
|
|
74
|
+
// type, and a `never` property poisons every consumer of the row. An UNQUALIFIED
|
|
75
|
+
// invalid column keeps its `never` field: that visibility is deliberate and
|
|
76
|
+
// pinned by the adversarial cast suite (`not_a_col::text` -> `{ x: never }`).
|
|
77
|
+
type NeverToUnknown<T, E extends string> =
|
|
78
|
+
[T] extends [never]
|
|
79
|
+
? E extends `${string}.${string}`
|
|
80
|
+
? unknown
|
|
81
|
+
: T
|
|
82
|
+
: T;
|
|
83
|
+
|
|
71
84
|
// Outer-join nullability for a directly-projected column. `Nullable` is the set
|
|
72
85
|
// of reference qualifiers (aliases / table names) that are nullable due to an
|
|
73
86
|
// outer join (see `NullableRelations`). When the projected expression is a plain
|
|
@@ -130,7 +143,95 @@ export type ApplyProjectionNull<
|
|
|
130
143
|
? CoalesceAllArgsNullable<SplitTopLevel<Args>, Tables, Aliases, S, Nullable> extends true
|
|
131
144
|
? T | null
|
|
132
145
|
: T
|
|
133
|
-
:
|
|
146
|
+
: [Nullable] extends [never]
|
|
147
|
+
? T
|
|
148
|
+
: [T] extends [never]
|
|
149
|
+
? ApplyJoinNull<T, E, Nullable>
|
|
150
|
+
: [T] extends [number | null]
|
|
151
|
+
? E extends `${string}${"+" | "-" | "*" | "/" | "%"}${string}`
|
|
152
|
+
? ArithRefJoinNullable<E, Tables, Aliases, S, Nullable> extends true
|
|
153
|
+
? T | null
|
|
154
|
+
: ApplyJoinNull<T, E, Nullable>
|
|
155
|
+
: ApplyJoinNull<T, E, Nullable>
|
|
156
|
+
: ApplyJoinNull<T, E, Nullable>;
|
|
157
|
+
|
|
158
|
+
// Outer-join nullability for a TOP-LEVEL ARITHMETIC projection (`A op B`).
|
|
159
|
+
// SQL NULL arithmetic is NULL, so the result is nullable when ANY operand is
|
|
160
|
+
// sourced from the nullable side of an outer join. `RefQualifier` cannot see
|
|
161
|
+
// operand refs (an arithmetic expression is not a plain column ref — or worse,
|
|
162
|
+
// its leftmost dot fakes one: `u.id + o.total` "qualifies" as `u`), so this
|
|
163
|
+
// walks the operands the same way the arithmetic TYPING did: split at the
|
|
164
|
+
// top-level operator and recurse each side. Only consulted when the projection
|
|
165
|
+
// already typed `number`/`number | null` (the arithmetic result types), under
|
|
166
|
+
// a non-empty `Nullable` set, with an operator char present — join-free
|
|
167
|
+
// queries and plain projections pay nothing. A `false` verdict falls back to
|
|
168
|
+
// `ApplyJoinNull`, so a non-arithmetic expression that slips past the op-char
|
|
169
|
+
// gate (e.g. a quoted-punct ref like `"u-1".id`) keeps its plain-ref handling.
|
|
170
|
+
type ArithRefJoinNullable<
|
|
171
|
+
E extends string,
|
|
172
|
+
Tables extends string,
|
|
173
|
+
Aliases extends string,
|
|
174
|
+
S extends DatabaseSchema,
|
|
175
|
+
Nullable extends string,
|
|
176
|
+
Steps extends any[] = []
|
|
177
|
+
> =
|
|
178
|
+
Steps["length"] extends 8
|
|
179
|
+
? false
|
|
180
|
+
: UnwrapRedundantParens<Trim<E>> extends infer SC extends string
|
|
181
|
+
? SC extends `${string}${"+" | "-" | "*" | "/" | "%"}${string}`
|
|
182
|
+
? SplitTopLevelOp<SC> extends infer SR
|
|
183
|
+
? [SR] extends [never]
|
|
184
|
+
? ArithOperandJoinNullable<SC, Tables, Aliases, S, Nullable>
|
|
185
|
+
: SR extends { __op: [infer L extends string, infer Op extends string, infer R extends string] }
|
|
186
|
+
? Op extends "||"
|
|
187
|
+
? false
|
|
188
|
+
: ArithRefJoinNullable<Trim<L>, Tables, Aliases, S, Nullable, [any, ...Steps]> extends true
|
|
189
|
+
? true
|
|
190
|
+
: ArithRefJoinNullable<Trim<R>, Tables, Aliases, S, Nullable, [any, ...Steps]>
|
|
191
|
+
: ArithOperandJoinNullable<SC, Tables, Aliases, S, Nullable>
|
|
192
|
+
: false
|
|
193
|
+
: ArithOperandJoinNullable<SC, Tables, Aliases, S, Nullable>
|
|
194
|
+
: false;
|
|
195
|
+
|
|
196
|
+
// A LEAF arithmetic operand (no top-level operator left). A whole-operand
|
|
197
|
+
// `coalesce(...)` keeps its all-args-nullable semantics (`coalesce(o.x, 0)`
|
|
198
|
+
// stays non-null even on the nullable side). A function-call operand
|
|
199
|
+
// (`sum(o.total)`) is conservatively nullable when any nullable-side
|
|
200
|
+
// qualified ref appears inside it — an all-NULL group aggregates to NULL.
|
|
201
|
+
// A plain ref consults its qualifier; literals and params stay non-null.
|
|
202
|
+
type ArithOperandJoinNullable<
|
|
203
|
+
SC extends string,
|
|
204
|
+
Tables extends string,
|
|
205
|
+
Aliases extends string,
|
|
206
|
+
S extends DatabaseSchema,
|
|
207
|
+
Nullable extends string
|
|
208
|
+
> =
|
|
209
|
+
CleanExpr<StripOuterCast<SC>> extends `coalesce(${infer Args})`
|
|
210
|
+
? CoalesceAllArgsNullable<SplitTopLevel<Args>, Tables, Aliases, S, Nullable>
|
|
211
|
+
: SC extends `${string}(${string}`
|
|
212
|
+
? NullableQualRefIn<SC, Nullable>
|
|
213
|
+
: RefQualifier<SC> extends infer Q extends string
|
|
214
|
+
? [Q] extends [never]
|
|
215
|
+
? false
|
|
216
|
+
: Q extends Nullable
|
|
217
|
+
? true
|
|
218
|
+
: false
|
|
219
|
+
: false;
|
|
220
|
+
|
|
221
|
+
// True when a `<Q>.`-qualified ref appears in `E` for any nullable qualifier
|
|
222
|
+
// `Q` — at the start, or after a boundary char that cannot be part of an
|
|
223
|
+
// identifier (so alias-suffix lookalikes like `po.x` never match `o`).
|
|
224
|
+
type NullableQualRefIn<E extends string, Nullable extends string> =
|
|
225
|
+
true extends (Nullable extends string ? QualRefIn<E, Nullable> : never)
|
|
226
|
+
? true
|
|
227
|
+
: false;
|
|
228
|
+
|
|
229
|
+
type QualRefIn<E extends string, Q extends string> =
|
|
230
|
+
E extends `${Q}.${string}`
|
|
231
|
+
? true
|
|
232
|
+
: E extends `${string}${" " | "(" | "," | "+" | "-" | "*" | "/" | "%"}${Q}.${string}`
|
|
233
|
+
? true
|
|
234
|
+
: false;
|
|
134
235
|
|
|
135
236
|
// True only when every coalesce argument is nullable. An empty/exhausted list is
|
|
136
237
|
// vacuously `true`, but the wrapper above only reaches this for a real coalesce call
|
|
@@ -229,57 +330,452 @@ export type IsBoolExpr<CE extends string> =
|
|
|
229
330
|
: false;
|
|
230
331
|
|
|
231
332
|
// Scans for a comparison operator outside parens and outside `'…'`/`"…"` quotes.
|
|
232
|
-
// `->>`, `#>>`
|
|
233
|
-
//
|
|
234
|
-
|
|
333
|
+
// `->>`, `#>>` etc. are consumed as units so their `>` is not mistaken for a
|
|
334
|
+
// comparison.
|
|
335
|
+
//
|
|
336
|
+
// Struct-jump, not per-char (the old walk minted the tail PER CHARACTER over
|
|
337
|
+
// every compare-bearing expression, including whole casted subquery bodies).
|
|
338
|
+
// Each step advances to the leftmost of `'` `"` `(` `)` (pairwise narrowing);
|
|
339
|
+
// the RUN before it — structural-char-free by construction — is tested at
|
|
340
|
+
// depth 0 with `HtcRunCheck`: a run containing `=` or `!` is a comparison
|
|
341
|
+
// outright (no non-comparison unit contains either), and a run with only
|
|
342
|
+
// `<`/`>` is scanned unit-to-unit (`->`, `->>`, `#>`, `#>>`, `@>`, `<@`,
|
|
343
|
+
// `<<`, `>>` consumed by 1-char context; the old `::` consume was a no-op —
|
|
344
|
+
// `:` never matched the compare set). Quote spans are jumped quote-to-quote
|
|
345
|
+
// (the other quote kind inside a span is data, the old InQ/InDQ suppression);
|
|
346
|
+
// an unterminated quote swallows the rest (old walk-to-EOF → `false`). The
|
|
347
|
+
// cap counts jumps, `false` on overflow as before.
|
|
348
|
+
export type HasTopLevelCompare<S extends string> = HtcJump<S, [], []>;
|
|
349
|
+
|
|
350
|
+
type HtcJump<
|
|
235
351
|
S extends string,
|
|
236
|
-
Depth extends any[]
|
|
237
|
-
Steps extends any[]
|
|
238
|
-
InQ extends boolean = false,
|
|
239
|
-
InDQ extends boolean = false
|
|
352
|
+
Depth extends any[],
|
|
353
|
+
Steps extends any[]
|
|
240
354
|
> = Steps["length"] extends 400
|
|
241
355
|
? false
|
|
242
|
-
: S extends `${infer
|
|
243
|
-
?
|
|
244
|
-
?
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
:
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
356
|
+
: S extends `${infer P}'${infer R}`
|
|
357
|
+
? P extends `${string}"${string}` | `${string}(${string}` | `${string})${string}`
|
|
358
|
+
? HtcJump2<S, Depth, Steps>
|
|
359
|
+
: HtcRunCheck<P, Depth> extends true
|
|
360
|
+
? true
|
|
361
|
+
: R extends `${string}'${infer R2}`
|
|
362
|
+
? HtcJump<R2, Depth, [any, ...Steps]>
|
|
363
|
+
: false
|
|
364
|
+
: HtcJump2<S, Depth, Steps>;
|
|
365
|
+
|
|
366
|
+
type HtcJump2<
|
|
367
|
+
S extends string,
|
|
368
|
+
Depth extends any[],
|
|
369
|
+
Steps extends any[]
|
|
370
|
+
> = S extends `${infer P}"${infer R}`
|
|
371
|
+
? P extends `${string}(${string}` | `${string})${string}`
|
|
372
|
+
? HtcJump3<S, Depth, Steps>
|
|
373
|
+
: HtcRunCheck<P, Depth> extends true
|
|
374
|
+
? true
|
|
375
|
+
: R extends `${string}"${infer R2}`
|
|
376
|
+
? HtcJump<R2, Depth, [any, ...Steps]>
|
|
377
|
+
: false
|
|
378
|
+
: HtcJump3<S, Depth, Steps>;
|
|
379
|
+
|
|
380
|
+
type HtcJump3<
|
|
381
|
+
S extends string,
|
|
382
|
+
Depth extends any[],
|
|
383
|
+
Steps extends any[]
|
|
384
|
+
> = S extends `${infer P}(${infer R}`
|
|
385
|
+
? P extends `${string})${string}`
|
|
386
|
+
? HtcJump4<S, Depth, Steps>
|
|
387
|
+
: HtcRunCheck<P, Depth> extends true
|
|
388
|
+
? true
|
|
389
|
+
: HtcJump<R, [any, ...Depth], [any, ...Steps]>
|
|
390
|
+
: HtcJump4<S, Depth, Steps>;
|
|
391
|
+
|
|
392
|
+
type HtcJump4<
|
|
393
|
+
S extends string,
|
|
394
|
+
Depth extends any[],
|
|
395
|
+
Steps extends any[]
|
|
396
|
+
> = S extends `${infer P})${infer R}`
|
|
397
|
+
? HtcRunCheck<P, Depth> extends true
|
|
398
|
+
? true
|
|
399
|
+
: HtcJump<R, Depth extends [any, ...infer D] ? D : [], [any, ...Steps]>
|
|
400
|
+
: HtcRunCheck<S, Depth>;
|
|
401
|
+
|
|
402
|
+
// A structural-char-free run is only inspected at depth 0. `=`/`!` never occur
|
|
403
|
+
// in a non-comparison unit, so their presence alone is a comparison; `<`/`>`
|
|
404
|
+
// need the unit scan.
|
|
405
|
+
type HtcRunCheck<P extends string, Depth extends any[]> =
|
|
406
|
+
Depth["length"] extends 0
|
|
407
|
+
? P extends `${string}${"=" | "!"}${string}`
|
|
408
|
+
? true
|
|
409
|
+
: P extends `${string}${"<" | ">"}${string}`
|
|
410
|
+
? HtcRunScan<P>
|
|
411
|
+
: false
|
|
281
412
|
: false;
|
|
282
413
|
|
|
414
|
+
// Unit-to-unit scan of a `<`/`>`-bearing run (no `=`/`!`, no structural chars):
|
|
415
|
+
// jump to the leftmost `<` or `>` and judge it by 1-char context — part of
|
|
416
|
+
// `<@`/`<<` (next char) or `->`/`->>`/`#>`/`#>>`/`@>`/`>>` (previous/next
|
|
417
|
+
// char) is consumed as a unit; anything else is a bare comparison.
|
|
418
|
+
type HtcRunScan<R extends string, Steps extends any[] = []> =
|
|
419
|
+
Steps["length"] extends 50
|
|
420
|
+
? false
|
|
421
|
+
: R extends `${infer A}<${infer B}`
|
|
422
|
+
? A extends `${string}>${string}`
|
|
423
|
+
? HtcRunGt<R, Steps>
|
|
424
|
+
: B extends `@${infer B2}`
|
|
425
|
+
? HtcRunScan<B2, [any, ...Steps]>
|
|
426
|
+
: B extends `<${infer B2}`
|
|
427
|
+
? HtcRunScan<B2, [any, ...Steps]>
|
|
428
|
+
: true
|
|
429
|
+
: HtcRunGt<R, Steps>;
|
|
430
|
+
|
|
431
|
+
type HtcRunGt<R extends string, Steps extends any[]> =
|
|
432
|
+
R extends `${infer A}>${infer B}`
|
|
433
|
+
? A extends `${string}${"-" | "#" | "@"}`
|
|
434
|
+
? B extends `>${infer B2}`
|
|
435
|
+
? HtcRunScan<B2, [any, ...Steps]>
|
|
436
|
+
: HtcRunScan<B, [any, ...Steps]>
|
|
437
|
+
: B extends `>${infer B2}`
|
|
438
|
+
? HtcRunScan<B2, [any, ...Steps]>
|
|
439
|
+
: true
|
|
440
|
+
: false;
|
|
441
|
+
|
|
442
|
+
// ---------------------------------------------------------------------------
|
|
443
|
+
// Tier-2 arithmetic: top-level operator split.
|
|
444
|
+
//
|
|
445
|
+
// Finds the LEFTMOST operator in {`||`, `+`, `-`, `*`, `/`, `%`} that sits at
|
|
446
|
+
// paren depth 0 outside `'…'`/`"…"` quotes, and splits the expression around
|
|
447
|
+
// it: `{ __op: [left, op, right] }`. A top-level UNMODELED operator char
|
|
448
|
+
// (single `|` bitwise-or, or the `||/` cube-root prefix) yields the abort
|
|
449
|
+
// marker `{ __ab: true }` (consumer: `unknown`); `never` means no top-level
|
|
450
|
+
// modeled operator exists. `->` / `->>` JSON arrows are consumed as units so
|
|
451
|
+
// their `-` is not mistaken for subtraction.
|
|
452
|
+
//
|
|
453
|
+
// Structure mirrors `HasTopLevelCompare` (struct-jump to the leftmost of
|
|
454
|
+
// `'` `"` `(` `)`, pairwise narrowing) but ACCUMULATES the consumed prefix so
|
|
455
|
+
// the split can be returned, and is worker/driver CHUNKED like
|
|
456
|
+
// `SplitTopLevel` (split.ts): each jump costs ~4 structural helpers plus a
|
|
457
|
+
// ≤6-level run scan at depth 0, so 80 jumps/chunk stays well under TS's
|
|
458
|
+
// ~1000 tail-count budget (round-11 lesson: budget chunks in tail counts,
|
|
459
|
+
// not jumps). Quote spans are jumped quote-to-quote (`''` escapes alternate
|
|
460
|
+
// close/re-open across jumps, so escaped content stays "inside"); an
|
|
461
|
+
// unterminated quote means no trustworthy split -> `never`.
|
|
462
|
+
type SplitTopLevelOp<S extends string> = StoDrive<StoWorker<S>>;
|
|
463
|
+
|
|
464
|
+
// `[R] extends [never]` MUST be guarded first — `never` distributes through
|
|
465
|
+
// the `extends {…}` test and would otherwise fall into the infer arm
|
|
466
|
+
// (round-10 lesson).
|
|
467
|
+
type StoDrive<R> =
|
|
468
|
+
[R] extends [never]
|
|
469
|
+
? never
|
|
470
|
+
: R extends { __c: [infer S2 extends string, infer D extends any[], infer A extends string] }
|
|
471
|
+
? StoDrive<StoWorker<S2, D, A, []>>
|
|
472
|
+
: R;
|
|
473
|
+
|
|
474
|
+
type StoWorker<
|
|
475
|
+
S extends string,
|
|
476
|
+
Depth extends any[] = [],
|
|
477
|
+
Acc extends string = "",
|
|
478
|
+
Steps extends any[] = []
|
|
479
|
+
> = Steps["length"] extends 80
|
|
480
|
+
? { __c: [S, Depth, Acc] }
|
|
481
|
+
: StoJump1<S, Depth, Acc, Steps>;
|
|
482
|
+
|
|
483
|
+
// Run-gate: a structural-char-free run is only operator-scanned at depth 0.
|
|
484
|
+
// At depth > 0 every char is data (`sum(x | y) + 1` must not abort on the
|
|
485
|
+
// nested `|`).
|
|
486
|
+
type StoRunGate<P extends string, Depth extends any[], Acc extends string, Tail extends string> =
|
|
487
|
+
Depth["length"] extends 0
|
|
488
|
+
? StoRunScan<P, Acc, Tail>
|
|
489
|
+
: never;
|
|
490
|
+
|
|
491
|
+
type StoJump1<
|
|
492
|
+
S extends string,
|
|
493
|
+
Depth extends any[],
|
|
494
|
+
Acc extends string,
|
|
495
|
+
Steps extends any[]
|
|
496
|
+
> = S extends `${infer P}'${infer R}`
|
|
497
|
+
? P extends `${string}"${string}` | `${string}(${string}` | `${string})${string}`
|
|
498
|
+
? StoJump2<S, Depth, Acc, Steps>
|
|
499
|
+
: StoRunGate<P, Depth, Acc, `'${R}`> extends infer RR
|
|
500
|
+
? [RR] extends [never]
|
|
501
|
+
? R extends `${infer Span}'${infer R2}`
|
|
502
|
+
? StoWorker<R2, Depth, `${Acc}${P}'${Span}'`, [any, ...Steps]>
|
|
503
|
+
: never
|
|
504
|
+
: RR
|
|
505
|
+
: never
|
|
506
|
+
: StoJump2<S, Depth, Acc, Steps>;
|
|
507
|
+
|
|
508
|
+
type StoJump2<
|
|
509
|
+
S extends string,
|
|
510
|
+
Depth extends any[],
|
|
511
|
+
Acc extends string,
|
|
512
|
+
Steps extends any[]
|
|
513
|
+
> = S extends `${infer P}"${infer R}`
|
|
514
|
+
? P extends `${string}(${string}` | `${string})${string}`
|
|
515
|
+
? StoJump3<S, Depth, Acc, Steps>
|
|
516
|
+
: StoRunGate<P, Depth, Acc, `"${R}`> extends infer RR
|
|
517
|
+
? [RR] extends [never]
|
|
518
|
+
? R extends `${infer Span}"${infer R2}`
|
|
519
|
+
? StoWorker<R2, Depth, `${Acc}${P}"${Span}"`, [any, ...Steps]>
|
|
520
|
+
: never
|
|
521
|
+
: RR
|
|
522
|
+
: never
|
|
523
|
+
: StoJump3<S, Depth, Acc, Steps>;
|
|
524
|
+
|
|
525
|
+
type StoJump3<
|
|
526
|
+
S extends string,
|
|
527
|
+
Depth extends any[],
|
|
528
|
+
Acc extends string,
|
|
529
|
+
Steps extends any[]
|
|
530
|
+
> = S extends `${infer P}(${infer R}`
|
|
531
|
+
? P extends `${string})${string}`
|
|
532
|
+
? StoJump4<S, Depth, Acc, Steps>
|
|
533
|
+
: StoRunGate<P, Depth, Acc, `(${R}`> extends infer RR
|
|
534
|
+
? [RR] extends [never]
|
|
535
|
+
? StoWorker<R, [any, ...Depth], `${Acc}${P}(`, [any, ...Steps]>
|
|
536
|
+
: RR
|
|
537
|
+
: never
|
|
538
|
+
: StoJump4<S, Depth, Acc, Steps>;
|
|
539
|
+
|
|
540
|
+
type StoJump4<
|
|
541
|
+
S extends string,
|
|
542
|
+
Depth extends any[],
|
|
543
|
+
Acc extends string,
|
|
544
|
+
Steps extends any[]
|
|
545
|
+
> = S extends `${infer P})${infer R}`
|
|
546
|
+
? StoRunGate<P, Depth, Acc, `)${R}`> extends infer RR
|
|
547
|
+
? [RR] extends [never]
|
|
548
|
+
// an unmatched `)` at depth 0 stays at depth 0 (pop of empty = empty)
|
|
549
|
+
? StoWorker<R, Depth extends [any, ...infer D] ? D : [], `${Acc}${P})`, [any, ...Steps]>
|
|
550
|
+
: RR
|
|
551
|
+
: never
|
|
552
|
+
: StoRunGate<S, Depth, Acc, "">;
|
|
553
|
+
|
|
554
|
+
// Leftmost modeled operator within a structural-free run `P` (no quotes or
|
|
555
|
+
// parens by construction). Pairwise narrowing, same invariant as
|
|
556
|
+
// `StlStructJump`: each level checks the matched prefix for every LATER
|
|
557
|
+
// class, so the level that fires is genuinely the leftmost. `Tail` is the
|
|
558
|
+
// untouched remainder of the whole expression after the run; the returned
|
|
559
|
+
// `right` re-attaches it.
|
|
560
|
+
type StoRunScan<P extends string, Acc extends string, Tail extends string> =
|
|
561
|
+
P extends `${infer A}+${infer B}`
|
|
562
|
+
? A extends `${string}${"-" | "*" | "/" | "%" | "|"}${string}`
|
|
563
|
+
? StoRunScan2<P, Acc, Tail>
|
|
564
|
+
: { __op: [`${Acc}${A}`, "+", `${B}${Tail}`] }
|
|
565
|
+
: StoRunScan2<P, Acc, Tail>;
|
|
566
|
+
|
|
567
|
+
type StoRunScan2<P extends string, Acc extends string, Tail extends string> =
|
|
568
|
+
P extends `${infer A}-${infer B}`
|
|
569
|
+
? A extends `${string}${"*" | "/" | "%" | "|"}${string}`
|
|
570
|
+
? StoRunScan3<P, Acc, Tail>
|
|
571
|
+
: B extends `>${infer B2}`
|
|
572
|
+
// `->` / `->>` JSON arrow: a unit, not subtraction — keep
|
|
573
|
+
// scanning the rest of the run (a leading `>` from `->>` is
|
|
574
|
+
// not an operator char and is skipped naturally). Non-tail
|
|
575
|
+
// recursion, but bounded by arrows-per-run (tiny in practice).
|
|
576
|
+
? StoRunScan<B2, `${Acc}${A}->`, Tail>
|
|
577
|
+
: { __op: [`${Acc}${A}`, "-", `${B}${Tail}`] }
|
|
578
|
+
: StoRunScan3<P, Acc, Tail>;
|
|
579
|
+
|
|
580
|
+
type StoRunScan3<P extends string, Acc extends string, Tail extends string> =
|
|
581
|
+
P extends `${infer A}*${infer B}`
|
|
582
|
+
? A extends `${string}${"/" | "%" | "|"}${string}`
|
|
583
|
+
? StoRunScan4<P, Acc, Tail>
|
|
584
|
+
: { __op: [`${Acc}${A}`, "*", `${B}${Tail}`] }
|
|
585
|
+
: StoRunScan4<P, Acc, Tail>;
|
|
586
|
+
|
|
587
|
+
type StoRunScan4<P extends string, Acc extends string, Tail extends string> =
|
|
588
|
+
P extends `${infer A}/${infer B}`
|
|
589
|
+
? A extends `${string}${"%" | "|"}${string}`
|
|
590
|
+
? StoRunScan5<P, Acc, Tail>
|
|
591
|
+
: { __op: [`${Acc}${A}`, "/", `${B}${Tail}`] }
|
|
592
|
+
: StoRunScan5<P, Acc, Tail>;
|
|
593
|
+
|
|
594
|
+
type StoRunScan5<P extends string, Acc extends string, Tail extends string> =
|
|
595
|
+
P extends `${infer A}%${infer B}`
|
|
596
|
+
? A extends `${string}|${string}`
|
|
597
|
+
? StoRunScan6<P, Acc, Tail>
|
|
598
|
+
: { __op: [`${Acc}${A}`, "%", `${B}${Tail}`] }
|
|
599
|
+
: StoRunScan6<P, Acc, Tail>;
|
|
600
|
+
|
|
601
|
+
type StoRunScan6<P extends string, Acc extends string, Tail extends string> =
|
|
602
|
+
P extends `${infer A}|${infer B}`
|
|
603
|
+
? B extends `|${infer B2}`
|
|
604
|
+
? B2 extends `/${string}`
|
|
605
|
+
// `||/` cube root — numeric prefix operator, NOT concat
|
|
606
|
+
? { __ab: true }
|
|
607
|
+
: Trim<`${Acc}${A}`> extends ""
|
|
608
|
+
// leading `||` with no left operand — unmodeled prefix op
|
|
609
|
+
? { __ab: true }
|
|
610
|
+
: { __op: [`${Acc}${A}`, "||", `${B2}${Tail}`] }
|
|
611
|
+
// single `|` (bitwise) at top level — unmodeled, conservative stop
|
|
612
|
+
: { __ab: true }
|
|
613
|
+
: never;
|
|
614
|
+
|
|
615
|
+
// `A op B` for op in {+, -, *, /, %} types `number` when BOTH operands type
|
|
616
|
+
// `number` (`| null` propagating from either side — SQL NULL arithmetic is
|
|
617
|
+
// NULL). number op number is numeric in Postgres; the interval/date hazards
|
|
618
|
+
// all require a non-number operand, which the schema types as non-number, so
|
|
619
|
+
// the both-number case is unambiguous and contract-legal. Any other operand
|
|
620
|
+
// type — including `never` — degrades to `unknown`: an operand the core path
|
|
621
|
+
// cannot resolve (a ref qualified by a joined-derived alias, a mis-split
|
|
622
|
+
// like `1e+5` -> `1e`) must NOT reject, or `ExprValid`'s never-gate would
|
|
623
|
+
// flip `ValidateSQL` to `false` on valid SQL; genuinely bogus columns are
|
|
624
|
+
// still rejected by the token-scan validators independently.
|
|
625
|
+
type ArithCombineType<
|
|
626
|
+
L extends string,
|
|
627
|
+
R extends string,
|
|
628
|
+
Tables extends string,
|
|
629
|
+
Aliases extends string,
|
|
630
|
+
S extends DatabaseSchema,
|
|
631
|
+
Steps extends any[]
|
|
632
|
+
> =
|
|
633
|
+
Trim<L> extends ""
|
|
634
|
+
? unknown
|
|
635
|
+
: ArithCombineTypes<ExprType<Trim<L>, Tables, Aliases, S, [any, ...Steps]>, R, Tables, Aliases, S, Steps>;
|
|
636
|
+
|
|
637
|
+
// Same, with the LEFT operand's type already computed (the Func-branch
|
|
638
|
+
// dispatcher gets it from `FunctionReturn` without re-parsing the call).
|
|
639
|
+
type ArithCombineTypes<
|
|
640
|
+
LT,
|
|
641
|
+
R extends string,
|
|
642
|
+
Tables extends string,
|
|
643
|
+
Aliases extends string,
|
|
644
|
+
S extends DatabaseSchema,
|
|
645
|
+
Steps extends any[]
|
|
646
|
+
> =
|
|
647
|
+
Trim<R> extends ""
|
|
648
|
+
? unknown
|
|
649
|
+
: ArithNumClass<LT> extends infer LN
|
|
650
|
+
? LN extends false
|
|
651
|
+
? unknown
|
|
652
|
+
: ArithNumClass<ExprType<Trim<R>, Tables, Aliases, S, [any, ...Steps]>> extends infer RN
|
|
653
|
+
? RN extends false
|
|
654
|
+
? unknown
|
|
655
|
+
: "nullable" extends LN | RN
|
|
656
|
+
? number | null
|
|
657
|
+
: number
|
|
658
|
+
: unknown
|
|
659
|
+
: unknown;
|
|
660
|
+
|
|
661
|
+
// `never` guarded FIRST — `[never]` matches the later arms too. A bare
|
|
662
|
+
// `null` operand classes as nullable (`price + null` -> number | null).
|
|
663
|
+
type ArithNumClass<T> =
|
|
664
|
+
[T] extends [never]
|
|
665
|
+
? false
|
|
666
|
+
: [T] extends [number]
|
|
667
|
+
? "num"
|
|
668
|
+
: [T] extends [number | null]
|
|
669
|
+
? "nullable"
|
|
670
|
+
: false;
|
|
671
|
+
|
|
672
|
+
// Scan-and-combine used where no cheaper dispatch is possible: the
|
|
673
|
+
// fallback-slot path and the op-char-in-Func-prefix path. `NoOp` is the
|
|
674
|
+
// result when the scan finds no top-level modeled operator (today's
|
|
675
|
+
// behavior at the call site); the abort marker is conservative `unknown`.
|
|
676
|
+
type ArithViaScan<
|
|
677
|
+
CE extends string,
|
|
678
|
+
Tables extends string,
|
|
679
|
+
Aliases extends string,
|
|
680
|
+
S extends DatabaseSchema,
|
|
681
|
+
Steps extends any[],
|
|
682
|
+
NoOp
|
|
683
|
+
> =
|
|
684
|
+
SplitTopLevelOp<CE> extends infer SR
|
|
685
|
+
? [SR] extends [never]
|
|
686
|
+
? NoOp
|
|
687
|
+
: SR extends { __op: [infer L extends string, infer Op extends string, infer R extends string] }
|
|
688
|
+
? Op extends "||"
|
|
689
|
+
? string
|
|
690
|
+
: ArithCombineType<L, R, Tables, Aliases, S, Steps>
|
|
691
|
+
: unknown
|
|
692
|
+
: never;
|
|
693
|
+
|
|
694
|
+
// Final-fallback-slot arithmetic (replaces Tier 1's DivByNumericLiteralType,
|
|
695
|
+
// which it subsumes: a numeric-literal divisor is just a `${number}` right
|
|
696
|
+
// operand). Sits after the column-ref branch fails, so common paths pay
|
|
697
|
+
// nothing; the char pre-gate skips the scan for operator-free expressions.
|
|
698
|
+
// `||` is unreachable here (the naive `${string}||${string}` branch runs
|
|
699
|
+
// earlier in the cascade), so the gate set is the five arithmetic chars.
|
|
700
|
+
type TopLevelArithType<
|
|
701
|
+
CE extends string,
|
|
702
|
+
Tables extends string,
|
|
703
|
+
Aliases extends string,
|
|
704
|
+
S extends DatabaseSchema,
|
|
705
|
+
Steps extends any[]
|
|
706
|
+
> =
|
|
707
|
+
CE extends `${string}${"+" | "-" | "*" | "/" | "%"}${string}`
|
|
708
|
+
? ArithViaScan<CE, Tables, Aliases, S, Steps, unknown>
|
|
709
|
+
: unknown;
|
|
710
|
+
|
|
711
|
+
// Func-branch dispatcher. The greedy `${Func}(${Args})` match anchors on the
|
|
712
|
+
// LAST `)`, so `sum(price) / count(id)` lands here with Func="sum",
|
|
713
|
+
// Args="price) / count(id" — a function call is only the WHOLE expression
|
|
714
|
+
// when its first paren group is also its last. Dispatch:
|
|
715
|
+
// - Func clean + no `)` in Args (the overwhelmingly common projection:
|
|
716
|
+
// `count(*)`, `sum(price)`, `upper(name)`, ops hidden inside quoted args)
|
|
717
|
+
// -> FunctionReturn directly, zero new cost.
|
|
718
|
+
// - Func clean + `)` in Args -> `SplitBalancedParen` (already-paid chunked
|
|
719
|
+
// primitive) resolves the first call's true extent WITHOUT a scan:
|
|
720
|
+
// rest "" means the call spans the whole expression (nested calls like
|
|
721
|
+
// `coalesce(min(x), 0)`); an operator-leading rest is arithmetic with the
|
|
722
|
+
// call as left operand; anything else (window `over (...)`, `filter`)
|
|
723
|
+
// keeps today's greedy FunctionReturn.
|
|
724
|
+
// - Operator char in Func (`price + count(id)`, `name || upper(b)`) -> the
|
|
725
|
+
// operator precedes the first paren; full top-level scan.
|
|
726
|
+
type FuncOrArithType<
|
|
727
|
+
CE extends string,
|
|
728
|
+
Func extends string,
|
|
729
|
+
Args extends string,
|
|
730
|
+
Tables extends string,
|
|
731
|
+
Aliases extends string,
|
|
732
|
+
S extends DatabaseSchema,
|
|
733
|
+
Steps extends any[]
|
|
734
|
+
> =
|
|
735
|
+
Func extends `${string}${"+" | "-" | "*" | "/" | "%" | "|"}${string}`
|
|
736
|
+
? ArithViaScan<CE, Tables, Aliases, S, Steps, FunctionReturn<CleanIdent<Func>, Args, Tables, Aliases, S, [any, ...Steps]>>
|
|
737
|
+
: Args extends `${string})${string}`
|
|
738
|
+
? CE extends `${string}(${infer AfterOpen}`
|
|
739
|
+
? SplitBalancedParen<`(${AfterOpen}`> extends { inner: infer Inner extends string; rest: infer Rest extends string }
|
|
740
|
+
? Trim<Rest> extends ""
|
|
741
|
+
? FunctionReturn<CleanIdent<Func>, Inner, Tables, Aliases, S, [any, ...Steps]>
|
|
742
|
+
: FuncRestDispatch<Trim<Rest>, Func, Args, Inner, Tables, Aliases, S, Steps>
|
|
743
|
+
: FunctionReturn<CleanIdent<Func>, Args, Tables, Aliases, S, [any, ...Steps]>
|
|
744
|
+
: FunctionReturn<CleanIdent<Func>, Args, Tables, Aliases, S, [any, ...Steps]>
|
|
745
|
+
: FunctionReturn<CleanIdent<Func>, Args, Tables, Aliases, S, [any, ...Steps]>;
|
|
746
|
+
|
|
747
|
+
// `RestT` (trimmed) is what follows the first balanced call. An arithmetic
|
|
748
|
+
// operator -> the call (typed via FunctionReturn on its TRUE arg list) is
|
|
749
|
+
// the left operand. `||` -> string (guarding the `||/` cube root). A `->`
|
|
750
|
+
// JSON arrow or any other shape (window clauses, …) -> today's greedy path.
|
|
751
|
+
type FuncRestDispatch<
|
|
752
|
+
RestT extends string,
|
|
753
|
+
Func extends string,
|
|
754
|
+
Args extends string,
|
|
755
|
+
Inner extends string,
|
|
756
|
+
Tables extends string,
|
|
757
|
+
Aliases extends string,
|
|
758
|
+
S extends DatabaseSchema,
|
|
759
|
+
Steps extends any[]
|
|
760
|
+
> =
|
|
761
|
+
RestT extends `||${infer R}`
|
|
762
|
+
? R extends `/${string}`
|
|
763
|
+
? unknown
|
|
764
|
+
: string
|
|
765
|
+
: RestT extends `+${infer R}`
|
|
766
|
+
? ArithCombineTypes<FunctionReturn<CleanIdent<Func>, Inner, Tables, Aliases, S, [any, ...Steps]>, R, Tables, Aliases, S, Steps>
|
|
767
|
+
: RestT extends `-${infer R}`
|
|
768
|
+
? R extends `>${string}`
|
|
769
|
+
? FunctionReturn<CleanIdent<Func>, Args, Tables, Aliases, S, [any, ...Steps]>
|
|
770
|
+
: ArithCombineTypes<FunctionReturn<CleanIdent<Func>, Inner, Tables, Aliases, S, [any, ...Steps]>, R, Tables, Aliases, S, Steps>
|
|
771
|
+
: RestT extends `*${infer R}`
|
|
772
|
+
? ArithCombineTypes<FunctionReturn<CleanIdent<Func>, Inner, Tables, Aliases, S, [any, ...Steps]>, R, Tables, Aliases, S, Steps>
|
|
773
|
+
: RestT extends `/${infer R}`
|
|
774
|
+
? ArithCombineTypes<FunctionReturn<CleanIdent<Func>, Inner, Tables, Aliases, S, [any, ...Steps]>, R, Tables, Aliases, S, Steps>
|
|
775
|
+
: RestT extends `%${infer R}`
|
|
776
|
+
? ArithCombineTypes<FunctionReturn<CleanIdent<Func>, Inner, Tables, Aliases, S, [any, ...Steps]>, R, Tables, Aliases, S, Steps>
|
|
777
|
+
: FunctionReturn<CleanIdent<Func>, Args, Tables, Aliases, S, [any, ...Steps]>;
|
|
778
|
+
|
|
283
779
|
// Strip a redundant fully-wrapping paren pair, repeatedly: `((expr))` and
|
|
284
780
|
// `((case ...)::text)` -> the inner expression whose parens wrap the WHOLE
|
|
285
781
|
// thing. Without this an outer operator hidden by a redundant wrap (e.g. the
|
|
@@ -349,9 +845,9 @@ export type ExprType<
|
|
|
349
845
|
? never
|
|
350
846
|
: SqlTypeToTs<CastTypeName>
|
|
351
847
|
: CE extends `${infer Func}(${infer Args})`
|
|
352
|
-
?
|
|
848
|
+
? FuncOrArithType<CE, Func, Args, Tables, Aliases, S, Steps>
|
|
353
849
|
: CE extends `${infer Func} (${infer Args})`
|
|
354
|
-
?
|
|
850
|
+
? FuncOrArithType<CE, Func, Args, Tables, Aliases, S, Steps>
|
|
355
851
|
: CE extends `${string}||${string}`
|
|
356
852
|
? string
|
|
357
853
|
: CE extends `${infer JBase}->>${string}`
|
|
@@ -380,12 +876,12 @@ export type ExprType<
|
|
|
380
876
|
? [Ref] extends [never]
|
|
381
877
|
? IsIdentifier<CE> extends true
|
|
382
878
|
? never
|
|
383
|
-
:
|
|
879
|
+
: TopLevelArithType<CE, Tables, Aliases, S, Steps>
|
|
384
880
|
: Ref extends ColumnRef<infer TableKey extends string, infer Column extends string>
|
|
385
881
|
? ColumnTypeFromTableKey<TableKey, Column, S>
|
|
386
882
|
: IsIdentifier<CE> extends true
|
|
387
883
|
? never
|
|
388
|
-
:
|
|
884
|
+
: TopLevelArithType<CE, Tables, Aliases, S, Steps>
|
|
389
885
|
: unknown
|
|
390
886
|
// A genuine TOP-LEVEL `::T` cast. As with the JSON-text
|
|
391
887
|
// operators, a `->>` / `#>>` to the right of the cast type
|
|
@@ -466,7 +962,18 @@ export type FunctionReturn<
|
|
|
466
962
|
? string
|
|
467
963
|
: Func extends "coalesce"
|
|
468
964
|
? UnionArgTypes<Args, Tables, Aliases, S, Steps>
|
|
469
|
-
|
|
965
|
+
// Postgres EXTRACT always returns a numeric
|
|
966
|
+
// value regardless of field/source, so typing
|
|
967
|
+
// it is unambiguous; it is NULL iff its source
|
|
968
|
+
// is NULL, so propagate the argument's
|
|
969
|
+
// nullability. An unmodeled argument types
|
|
970
|
+
// `unknown` (which may include null) → the
|
|
971
|
+
// conservative answer is `number | null`.
|
|
972
|
+
: Func extends "extract"
|
|
973
|
+
? null extends FirstArgType<Args, Tables, Aliases, S, Steps>
|
|
974
|
+
? number | null
|
|
975
|
+
: number
|
|
976
|
+
: unknown;
|
|
470
977
|
|
|
471
978
|
// Expression validation
|
|
472
979
|
|
|
@@ -481,13 +988,20 @@ export type ExprValid<
|
|
|
481
988
|
E extends string,
|
|
482
989
|
Tables extends string,
|
|
483
990
|
Aliases extends string,
|
|
484
|
-
S extends DatabaseSchema
|
|
991
|
+
S extends DatabaseSchema,
|
|
992
|
+
LocalRels extends string = never
|
|
485
993
|
> =
|
|
486
994
|
IsIgnorableRuntimeExpr<E> extends true
|
|
487
995
|
? true
|
|
488
996
|
: ExtractAlias<E> extends { expr: infer RawExpr extends string }
|
|
489
997
|
? IsIgnorableRuntimeExpr<RawExpr> extends true
|
|
490
998
|
? true
|
|
999
|
+
// A ref qualified by a query-local relation (CTE name) has no schema
|
|
1000
|
+
// surface to resolve against — `ExprType` yields `never` and the
|
|
1001
|
+
// token scans reject it. Bless it (lenient contract: the local
|
|
1002
|
+
// relation's output shape is validated where it is defined).
|
|
1003
|
+
: HasLocalQualifier<RawExpr, LocalRels> extends true
|
|
1004
|
+
? true
|
|
491
1005
|
: ExprType<RawExpr, Tables, Aliases, S> extends never
|
|
492
1006
|
? false
|
|
493
1007
|
: NeedsTokenRefValidation<RawExpr> extends true
|
|
@@ -495,6 +1009,17 @@ export type ExprValid<
|
|
|
495
1009
|
: FuncCompoundArgsValid<RawExpr, Tables, Aliases, S>
|
|
496
1010
|
: true;
|
|
497
1011
|
|
|
1012
|
+
// `true` when E is a qualified ref whose qualifier names a query-local relation.
|
|
1013
|
+
// The `[LocalRels] extends [never]` guard keeps the common no-CTE path free.
|
|
1014
|
+
type HasLocalQualifier<E extends string, LocalRels extends string> =
|
|
1015
|
+
[LocalRels] extends [never]
|
|
1016
|
+
? false
|
|
1017
|
+
: E extends `${infer Q}.${string}`
|
|
1018
|
+
? CleanIdent<Q> extends LocalRels
|
|
1019
|
+
? true
|
|
1020
|
+
: false
|
|
1021
|
+
: false;
|
|
1022
|
+
|
|
498
1023
|
// A function-call (or cast) projection skips the token ref-scan above
|
|
499
1024
|
// (`NeedsTokenRefValidation` is false for `${fn}(${args})`), which is why an
|
|
500
1025
|
// invalid column hidden inside an aggregate/function argument — `sum(price +
|
|
@@ -576,7 +1101,7 @@ export type ExprQualifiedRefsValid<
|
|
|
576
1101
|
Tables extends string,
|
|
577
1102
|
Aliases extends string,
|
|
578
1103
|
S extends DatabaseSchema
|
|
579
|
-
> =
|
|
1104
|
+
> = QualifiedRefScan<E> extends infer Cols
|
|
580
1105
|
? AllTrue<Cols extends string ? ColumnRefValidLooseWith<Cols, Tables, Aliases, S> : true>
|
|
581
1106
|
: true;
|
|
582
1107
|
|
|
@@ -585,7 +1110,7 @@ export type ExprUnqualifiedRefsValid<
|
|
|
585
1110
|
Tables extends string,
|
|
586
1111
|
Aliases extends string,
|
|
587
1112
|
S extends DatabaseSchema
|
|
588
|
-
> =
|
|
1113
|
+
> = UnqualifiedRefScan<E, S, Tables, Aliases> extends infer Cols
|
|
589
1114
|
? AllTrue<Cols extends string ? UnqualifiedColumnValid<Cols, Tables, Aliases, S> : true>
|
|
590
1115
|
: true;
|
|
591
1116
|
|
|
@@ -594,12 +1119,13 @@ export type ExprsValidList<
|
|
|
594
1119
|
Tables extends string,
|
|
595
1120
|
Aliases extends string,
|
|
596
1121
|
S extends DatabaseSchema,
|
|
597
|
-
Steps extends any[] = []
|
|
1122
|
+
Steps extends any[] = [],
|
|
1123
|
+
LocalRels extends string = never
|
|
598
1124
|
> = Steps["length"] extends 100
|
|
599
1125
|
? true
|
|
600
1126
|
: Exprs extends [infer H extends string, ...infer Rest extends string[]]
|
|
601
|
-
? ExprValid<H, Tables, Aliases, S> extends true
|
|
602
|
-
? ExprsValidList<Rest, Tables, Aliases, S, [any, ...Steps]>
|
|
1127
|
+
? ExprValid<H, Tables, Aliases, S, LocalRels> extends true
|
|
1128
|
+
? ExprsValidList<Rest, Tables, Aliases, S, [any, ...Steps], LocalRels>
|
|
603
1129
|
: false
|
|
604
1130
|
: true;
|
|
605
1131
|
|