@kuindji/typed-sql 0.1.0 → 0.3.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 +7 -0
- package/dist/builder/assemble.d.ts.map +1 -1
- package/dist/builder/assemble.js +9 -10
- package/dist/builder/assemble.js.map +1 -1
- package/dist/builder/conditional-sql.d.ts +7 -2
- package/dist/builder/conditional-sql.d.ts.map +1 -1
- package/dist/builder/conditional-sql.js +9 -22
- package/dist/builder/conditional-sql.js.map +1 -1
- package/dist/builder/params.d.ts +2 -1
- package/dist/builder/params.d.ts.map +1 -1
- package/dist/builder/params.js +34 -27
- package/dist/builder/params.js.map +1 -1
- package/dist/builder/select.d.ts +7 -0
- package/dist/builder/select.d.ts.map +1 -1
- package/dist/builder/select.js +7 -2
- package/dist/builder/select.js.map +1 -1
- package/dist/builder/state.d.ts +3 -6
- package/dist/builder/state.d.ts.map +1 -1
- package/dist/builder/state.js +1 -2
- package/dist/builder/state.js.map +1 -1
- package/dist/expressions.d.ts +12 -6
- package/dist/expressions.d.ts.map +1 -1
- package/dist/parsing/extract.d.ts +2 -1
- package/dist/parsing/extract.d.ts.map +1 -1
- package/dist/parsing/normalize.d.ts +10 -5
- package/dist/parsing/normalize.d.ts.map +1 -1
- package/dist/parsing/pg-literals.d.ts +2 -2
- package/dist/parsing/pg-literals.d.ts.map +1 -1
- package/dist/parsing/tokenize.d.ts +8 -6
- package/dist/parsing/tokenize.d.ts.map +1 -1
- package/dist/tables.d.ts +2 -2
- package/dist/tables.d.ts.map +1 -1
- package/dist/validation/dispatch.d.ts +3 -1
- package/dist/validation/dispatch.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 +17 -6
- package/dist/validation/validate-columns.d.ts.map +1 -1
- package/package.json +3 -2
- package/src/builder/assemble.ts +9 -13
- package/src/builder/conditional-sql.ts +9 -27
- package/src/builder/params.ts +33 -27
- package/src/builder/select.ts +19 -2
- package/src/builder/state.ts +4 -6
- package/src/expressions.ts +47 -7
- package/src/parsing/extract.ts +18 -4
- package/src/parsing/normalize.ts +143 -48
- package/src/parsing/pg-literals.ts +23 -12
- package/src/parsing/tokenize.ts +56 -23
- package/src/tables.ts +35 -4
- package/src/validation/dispatch.ts +21 -3
- package/src/validation/return-derived.ts +60 -4
- package/src/validation/return-types.ts +9 -3
- package/src/validation/validate-columns.ts +123 -13
|
@@ -288,25 +288,43 @@ export type HasReturning<N extends string> =
|
|
|
288
288
|
? false
|
|
289
289
|
: HasReturningQuoteAware<N>;
|
|
290
290
|
|
|
291
|
+
// Quote-free fast-path: a query with no `'` and no `"` has no place for a
|
|
292
|
+
// ` returning ` to hide, so every occurrence is top-level — a single pattern test
|
|
293
|
+
// is exact and skips the ~1200-step char-walk (these run on every DML). Only
|
|
294
|
+
// quote-bearing queries pay for the walk below. The fast-path pattern matches the
|
|
295
|
+
// step-cap fallback this walk already uses, so it is consistent with prior behavior.
|
|
291
296
|
export type HasReturningQuoteAware<
|
|
292
297
|
S extends string,
|
|
293
298
|
InString extends boolean = false,
|
|
294
299
|
InDString extends boolean = false,
|
|
295
300
|
Steps extends any[] = []
|
|
301
|
+
> = string extends S
|
|
302
|
+
? false
|
|
303
|
+
: S extends `${string}'${string}`
|
|
304
|
+
? HasReturningQuoteAwareWalk<S, InString, InDString, Steps>
|
|
305
|
+
: S extends `${string}"${string}`
|
|
306
|
+
? HasReturningQuoteAwareWalk<S, InString, InDString, Steps>
|
|
307
|
+
: S extends `${string} returning ${string}` ? true : false;
|
|
308
|
+
|
|
309
|
+
type HasReturningQuoteAwareWalk<
|
|
310
|
+
S extends string,
|
|
311
|
+
InString extends boolean = false,
|
|
312
|
+
InDString extends boolean = false,
|
|
313
|
+
Steps extends any[] = []
|
|
296
314
|
> = string extends S
|
|
297
315
|
? false
|
|
298
316
|
: Steps["length"] extends 1200
|
|
299
317
|
? S extends `${string} returning ${string}` ? true : false
|
|
300
318
|
: InString extends true
|
|
301
319
|
? S extends `${infer C}${infer Rest}`
|
|
302
|
-
?
|
|
320
|
+
? HasReturningQuoteAwareWalk<Rest, C extends "'" ? false : true, InDString, [any, ...Steps]>
|
|
303
321
|
: false
|
|
304
322
|
: InDString extends true
|
|
305
323
|
? S extends `${infer C}${infer Rest}`
|
|
306
|
-
?
|
|
324
|
+
? HasReturningQuoteAwareWalk<Rest, InString, C extends `"` ? false : true, [any, ...Steps]>
|
|
307
325
|
: false
|
|
308
326
|
: S extends ` returning ${string}`
|
|
309
327
|
? true
|
|
310
328
|
: S extends `${infer C}${infer Rest}`
|
|
311
|
-
?
|
|
329
|
+
? HasReturningQuoteAwareWalk<Rest, C extends "'" ? true : false, C extends `"` ? true : false, [any, ...Steps]>
|
|
312
330
|
: false;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Derived-table & JOIN LATERAL / joined-derived result inference.
|
|
2
|
-
import type { AliasesInQuery, TablesInQuery } from "../tables.js";
|
|
2
|
+
import type { AliasesInQuery, NullableRelations, TablesInQuery } from "../tables.js";
|
|
3
3
|
import type { ApplyJoinNull, OuterCastTs, RefQualifier } from "../expressions.js";
|
|
4
|
-
import type { CleanIdent, ExtractAliasResult, ExtractFromClause, ExtractSelectList, SplitBalancedParen, SplitCommaSimple, SplitSelectList, Trim, TrimLeft } from "../parsing.js";
|
|
4
|
+
import type { CleanIdent, ExtractAliasResult, ExtractFromClause, ExtractSelectList, SplitBalancedParen, SplitCommaSimple, SplitSelectList, StripSubqueries, Trim, TrimLeft } from "../parsing.js";
|
|
5
5
|
import type { DatabaseSchema } from "../schema.js";
|
|
6
6
|
import type { MergeRow, SelectReturnWith } from "./return-types.js";
|
|
7
7
|
import type { Simplify, UnionToIntersection } from "../utils.js";
|
|
@@ -107,7 +107,30 @@ export type DerivedBodyColType<E extends string, BaseRow> =
|
|
|
107
107
|
export type DerivedTableReturn<N extends string, S extends DatabaseSchema> =
|
|
108
108
|
DerivedTableMatch<N> extends { body: infer Body extends string; alias: infer DAlias extends string; cols: infer Cols extends string[] }
|
|
109
109
|
? DerivedRenamedRow<Body, Cols, S> extends infer SubRow
|
|
110
|
-
?
|
|
110
|
+
? Simplify<MergeRow<
|
|
111
|
+
DerivedJoinedBaseRow<N, S>,
|
|
112
|
+
BuildDerivedReturn<SplitSelectList<ExtractSelectList<N>>, DAlias, SubRow>
|
|
113
|
+
>>
|
|
114
|
+
: {}
|
|
115
|
+
: {};
|
|
116
|
+
|
|
117
|
+
// Tables JOINed onto the leading derived source (`FROM (<subquery>) d JOIN base
|
|
118
|
+
// b ...`) are real outer relations, so projected refs to them (`b.col`,
|
|
119
|
+
// `b.col AS x`) must resolve against the schema — not leak as a dotted-literal
|
|
120
|
+
// key (`{"b.col": unknown}`) or an `unknown` type. Excise the subquery body
|
|
121
|
+
// (`StripSubqueries`) to recover just those outer relations, then resolve the
|
|
122
|
+
// outer SELECT list against them with outer-join nullability. Refs to the derived
|
|
123
|
+
// alias (`d.col`) don't resolve here (it isn't a real table); the derived-alias
|
|
124
|
+
// path supplies those and `MergeRow` lets it win on overlap. Gated on an actual
|
|
125
|
+
// JOIN so a plain `FROM (<subquery>) d` pays nothing extra.
|
|
126
|
+
export type DerivedJoinedBaseRow<N extends string, S extends DatabaseSchema> =
|
|
127
|
+
StripSubqueries<N> extends infer Outer extends string
|
|
128
|
+
? Outer extends `${string} join ${string}`
|
|
129
|
+
? TablesInQuery<Outer, S> extends infer OT extends string
|
|
130
|
+
? AliasesInQuery<Outer, S> extends infer OA extends string
|
|
131
|
+
? SelectReturnWith<ExtractSelectList<N>, OT, OA, S, NullableRelations<N, S>>
|
|
132
|
+
: {}
|
|
133
|
+
: {}
|
|
111
134
|
: {}
|
|
112
135
|
: {};
|
|
113
136
|
|
|
@@ -123,6 +146,37 @@ export type BuildDerivedReturn<
|
|
|
123
146
|
? BuildDerivedReturn<Rest, DAlias, SubRow, MergeRow<Acc, DerivedExprToObject<H, DAlias, SubRow>>, [any, ...Steps]>
|
|
124
147
|
: Simplify<Acc>;
|
|
125
148
|
|
|
149
|
+
// A "bare identifier": a simple unquoted name with no operator/paren/space/dot
|
|
150
|
+
// chars. Lets us tell a *plain* qualified column ref (`ip.entity_id`) apart from
|
|
151
|
+
// an expression that merely contains a dot (`sum(t.x)`, `(array_agg(a.b))[1]`).
|
|
152
|
+
export type IsBareIdent<S extends string> =
|
|
153
|
+
S extends "" ? false
|
|
154
|
+
: S extends `${string}${
|
|
155
|
+
| " " | "(" | ")" | "+" | "-" | "*" | "/" | ":" | "'" | "\"" | "["
|
|
156
|
+
| "]" | "," | "." | "=" | "<" | ">" | "|" | "%" | "!" | "~" | "@"
|
|
157
|
+
}${string}`
|
|
158
|
+
? false
|
|
159
|
+
: true;
|
|
160
|
+
|
|
161
|
+
// The qualifier of a *simple* qualified column ref `Q.col` (both parts bare),
|
|
162
|
+
// else `never` (functions/expressions/quoted refs don't qualify).
|
|
163
|
+
export type SimpleQualRef<E extends string> =
|
|
164
|
+
CleanIdent<E> extends `${infer Q}.${infer Col}`
|
|
165
|
+
? IsBareIdent<Q> extends true
|
|
166
|
+
? IsBareIdent<Col> extends true ? Q : never
|
|
167
|
+
: never
|
|
168
|
+
: never;
|
|
169
|
+
|
|
170
|
+
// True when `RawExpr` is a simple qualified column ref pointing at something
|
|
171
|
+
// OTHER than the derived alias — i.e. a JOINed base table (`ip.entity_id`,
|
|
172
|
+
// `t.name`). `DerivedTableReturn` resolves those against the schema separately,
|
|
173
|
+
// so the derived-alias path skips them rather than emitting a bogus dotted key.
|
|
174
|
+
export type IsForeignSimpleRef<RawExpr extends string, DAlias extends string> =
|
|
175
|
+
SimpleQualRef<RawExpr> extends infer Q extends string
|
|
176
|
+
? [Q] extends [never] ? false
|
|
177
|
+
: Q extends DAlias ? (DAlias extends Q ? false : true) : true
|
|
178
|
+
: false;
|
|
179
|
+
|
|
126
180
|
// `Nullable` carries the outer-join nullable-qualifier set. When the derived
|
|
127
181
|
// table is the nullable side of an outer join (`LEFT JOIN LATERAL (...) d`), its
|
|
128
182
|
// exposed columns must gain `| null` (Postgres: the whole derived row is NULL when
|
|
@@ -130,7 +184,9 @@ export type BuildDerivedReturn<
|
|
|
130
184
|
// don't pass it are unaffected.
|
|
131
185
|
export type DerivedExprToObject<E extends string, DAlias extends string, SubRow, Nullable extends string = never> =
|
|
132
186
|
ExtractAliasResult<E> extends { expr: infer RawExpr extends string; alias: infer OutAlias }
|
|
133
|
-
?
|
|
187
|
+
? IsForeignSimpleRef<RawExpr, DAlias> extends true
|
|
188
|
+
? {}
|
|
189
|
+
: [OutAlias] extends [never]
|
|
134
190
|
? CleanIdent<RawExpr> extends "*"
|
|
135
191
|
? SubRow
|
|
136
192
|
: CleanIdent<RawExpr> extends `${DAlias}.*`
|
|
@@ -259,9 +259,15 @@ export type SelectAliases<
|
|
|
259
259
|
? Acc
|
|
260
260
|
: Exprs extends [infer H extends string, ...infer Rest extends string[]]
|
|
261
261
|
? ExtractAlias<H> extends { alias: infer Alias }
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
262
|
+
// A non-aliased projection yields `alias: never`. Guard it in tuple
|
|
263
|
+
// position: a NAKED `never extends string ?` arm collapses the whole
|
|
264
|
+
// conditional (and with it the entire recursion) to `never`, silently
|
|
265
|
+
// dropping every alias the list DOES define.
|
|
266
|
+
? [Alias] extends [never]
|
|
267
|
+
? SelectAliases<Rest, Acc, [any, ...Steps]>
|
|
268
|
+
: Alias extends string
|
|
269
|
+
? SelectAliases<Rest, Acc | Alias, [any, ...Steps]>
|
|
270
|
+
: SelectAliases<Rest, Acc, [any, ...Steps]>
|
|
265
271
|
: SelectAliases<Rest, Acc, [any, ...Steps]>
|
|
266
272
|
: Acc;
|
|
267
273
|
|
|
@@ -4,9 +4,9 @@ import type { AllTrue, And, StartsWith } from "../utils.js";
|
|
|
4
4
|
import type { CleanIdent, DQuoteSpaceSentinel, ExceedsLengthBudget, ExtractAliasResult, ExtractBefore, ExtractConflictColumns, ExtractConflictUpdateExcludedCols, ExtractConflictUpdateSetColumns, ExtractInsertColumns, ExtractLastWhere, ExtractReturningList, ExtractSelectList, ExtractUpdateSetColumns, ReplaceAll, SplitSelectList, StripSubqueries, TokenizeLoose, Trim } from "../parsing.js";
|
|
5
5
|
import type { ColumnRefValidLooseWith, IsSimpleRefPart, QualifiedColumnRefs, ResolveAlias, TableKeysByName, UnqualifiedColumnRefs, UnqualifiedColumnValid } from "../columns.js";
|
|
6
6
|
import type { ColumnsExistInTable, RefScanBeforeOrderBy, RefScanOrderBy, RefScanSegment, SelectAliasesInQuery, SelectAliasSet } from "./return-types.js";
|
|
7
|
-
import type { CteRow, SingleCteMatch } from "./cte.js";
|
|
7
|
+
import type { CteNames, CteRow, SingleCteMatch } from "./cte.js";
|
|
8
8
|
import type { DatabaseSchema } from "../schema.js";
|
|
9
|
-
import type { DerivedRenamedRow, DerivedTableMatch } from "./return-derived.js";
|
|
9
|
+
import type { AliasHasNoSpace, DerivedRenamedRow, DerivedTableMatch } from "./return-derived.js";
|
|
10
10
|
import type { ExprsValidList } from "../expressions.js";
|
|
11
11
|
import type { HasReturning, QueryKind, ValidateSQLNormalized } from "./dispatch.js";
|
|
12
12
|
|
|
@@ -309,7 +309,7 @@ export type ColumnsValidInSelectOrReturningFor<
|
|
|
309
309
|
HasReturning<N> extends true
|
|
310
310
|
? [Tables] extends [never]
|
|
311
311
|
? true
|
|
312
|
-
: ExprsValidList<SplitSelectList<ReplaceAll<ExtractReturningList<N>, DQuoteSpaceSentinel, " ">>, Tables, Aliases, S
|
|
312
|
+
: ExprsValidList<SplitSelectList<ReplaceAll<ExtractReturningList<N>, DQuoteSpaceSentinel, " ">>, Tables, Aliases, S, [], SelectLocalRels<N>>
|
|
313
313
|
: QueryKind<N> extends "select"
|
|
314
314
|
? [Tables] extends [never]
|
|
315
315
|
? true
|
|
@@ -318,9 +318,18 @@ export type ColumnsValidInSelectOrReturningFor<
|
|
|
318
318
|
// The alias set restores those spaces, so restore them here too or a
|
|
319
319
|
// qualifier through a space-bearing quoted alias (`"user alias".id`)
|
|
320
320
|
// would never match its registered alias (round-12 regression).
|
|
321
|
-
: ExprsValidList<SplitSelectList<ReplaceAll<ExtractSelectList<N>, DQuoteSpaceSentinel, " ">>, Tables, Aliases, S
|
|
321
|
+
: ExprsValidList<SplitSelectList<ReplaceAll<ExtractSelectList<N>, DQuoteSpaceSentinel, " ">>, Tables, Aliases, S, [], SelectLocalRels<N>>
|
|
322
322
|
: true;
|
|
323
323
|
|
|
324
|
+
// Query-local relation names whose qualified refs the projection-list validator
|
|
325
|
+
// must bless: a `WITH`-query's CTE names. (A CTE that reaches the core validator
|
|
326
|
+
// — e.g. `WITH ... SELECT ... JOIN ...` — is collected into `TablesInQuery` as a
|
|
327
|
+
// bogus base table for the ref-resolution, so `cte.col` in the SELECT list would
|
|
328
|
+
// otherwise resolve `never` and falsely reject.) Gated on the `with ` prefix so
|
|
329
|
+
// CTE-free queries pay nothing.
|
|
330
|
+
type SelectLocalRels<N extends string> =
|
|
331
|
+
N extends `with ${string}` ? CteNames<N> : never;
|
|
332
|
+
|
|
324
333
|
// insert
|
|
325
334
|
|
|
326
335
|
export type ColumnsValidInInsert<N extends string, S extends DatabaseSchema> =
|
|
@@ -368,10 +377,66 @@ export type QualifiedColumnRefsValidFor<
|
|
|
368
377
|
Tables extends string,
|
|
369
378
|
Aliases extends string,
|
|
370
379
|
LooseTokens extends string[]
|
|
380
|
+
> =
|
|
381
|
+
// Common path: with no CTE and no parenthesised FROM source there is no local
|
|
382
|
+
// relation that could qualify a ref, so skip the (per-ref) local-relation
|
|
383
|
+
// blessing entirely — exact prior behavior, zero added cost.
|
|
384
|
+
HasLocalRelations<N> extends true
|
|
385
|
+
? QualifiedRefsValidWithLocal<N, S, Tables, Aliases, LooseTokens, CteNames<N>>
|
|
386
|
+
: QualifiedColumnRefs<LooseTokens, S, Tables, Aliases> extends infer Cols
|
|
387
|
+
? AllTrue<Cols extends string ? ColumnRefValidLooseWith<Cols, Tables, Aliases, S> : true>
|
|
388
|
+
: true;
|
|
389
|
+
|
|
390
|
+
// A "local relation" is a query-local name that is NOT a base table: a CTE name, or
|
|
391
|
+
// the alias bound to a derived / VALUES / subquery FROM source (`from (…) [as]
|
|
392
|
+
// x[(cols)]`). Its columns are not modeled in the schema, so a ref qualified by it
|
|
393
|
+
// must be accepted leniently rather than resolved against a (non-existent) base
|
|
394
|
+
// table. Blessing only ever turns a reject into an accept — never the reverse —
|
|
395
|
+
// consistent with the lenient-parser contract.
|
|
396
|
+
type HasLocalRelations<N extends string> =
|
|
397
|
+
N extends `with ${string}` ? true :
|
|
398
|
+
N extends `${string} from (${string}` ? true :
|
|
399
|
+
N extends `${string} join (${string}` ? true :
|
|
400
|
+
false;
|
|
401
|
+
|
|
402
|
+
type QualifiedRefsValidWithLocal<
|
|
403
|
+
N extends string,
|
|
404
|
+
S extends DatabaseSchema,
|
|
405
|
+
Tables extends string,
|
|
406
|
+
Aliases extends string,
|
|
407
|
+
LooseTokens extends string[],
|
|
408
|
+
Ctes extends string
|
|
371
409
|
> = QualifiedColumnRefs<LooseTokens, S, Tables, Aliases> extends infer Cols
|
|
372
|
-
? AllTrue<
|
|
410
|
+
? AllTrue<
|
|
411
|
+
Cols extends string
|
|
412
|
+
? IsLocalRelation<RefQualifierOf<Cols>, Ctes, N> extends true
|
|
413
|
+
? true
|
|
414
|
+
: ColumnRefValidLooseWith<Cols, Tables, Aliases, S>
|
|
415
|
+
: true
|
|
416
|
+
>
|
|
373
417
|
: true;
|
|
374
418
|
|
|
419
|
+
// The qualifier (text before the first `.`) of a qualified column ref.
|
|
420
|
+
type RefQualifierOf<Col extends string> =
|
|
421
|
+
Col extends `${infer Q}.${string}` ? CleanIdent<Q> : never;
|
|
422
|
+
|
|
423
|
+
type IsLocalRelation<Q extends string, Ctes extends string, N extends string> =
|
|
424
|
+
[Q] extends [never] ? false :
|
|
425
|
+
Q extends Ctes ? true :
|
|
426
|
+
IsDerivedSourceAlias<Q, N>;
|
|
427
|
+
|
|
428
|
+
// Detect `… ) [as] q …` / `… ) [as] q( …` — q is the alias bound to a parenthesised
|
|
429
|
+
// (derived / VALUES / subquery) FROM source. N is already normalized (lowercase
|
|
430
|
+
// outside quotes); q is the cleaned, lowercased qualifier.
|
|
431
|
+
type IsDerivedSourceAlias<Q extends string, N extends string> =
|
|
432
|
+
N extends `${string}) as ${Q} ${string}` ? true :
|
|
433
|
+
N extends `${string}) as ${Q}(${string}` ? true :
|
|
434
|
+
N extends `${string}) as ${Q}` ? true :
|
|
435
|
+
N extends `${string}) ${Q} ${string}` ? true :
|
|
436
|
+
N extends `${string}) ${Q}(${string}` ? true :
|
|
437
|
+
N extends `${string}) ${Q}` ? true :
|
|
438
|
+
false;
|
|
439
|
+
|
|
375
440
|
// Once a table is given a range alias (`FROM products p`), PostgreSQL hides the
|
|
376
441
|
// original table name as a correlation name for that query level — `products.id`
|
|
377
442
|
// is then invalid; only `p.id` works. The lenient qualified-ref check accepts
|
|
@@ -402,15 +467,21 @@ export type NoAliasShadowedQualifiers<
|
|
|
402
467
|
> =
|
|
403
468
|
[AliasedTableKeys<Aliases>] extends [never]
|
|
404
469
|
? true
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
470
|
+
// A shadowable qualifier is a `qualifier.column` token, which requires a
|
|
471
|
+
// `.`. With no `.` anywhere, `QualifiedColumnRefs` accumulates `never` and
|
|
472
|
+
// `AllTrue<never>` is `true` — so skip the whole-query `TokenizeLoose<N>`
|
|
473
|
+
// re-walk (computed nowhere else) on dot-free queries. Exact-equivalent.
|
|
474
|
+
: N extends `${string}.${string}`
|
|
475
|
+
? AllTrue<
|
|
476
|
+
QualifiedColumnRefs<TokenizeLoose<N>, S, Tables, Aliases> extends infer R
|
|
477
|
+
? R extends `${infer Q}.${string}`
|
|
478
|
+
? QualifierShadowedByAlias<Q, Tables, Aliases, S> extends true
|
|
479
|
+
? false
|
|
480
|
+
: true
|
|
410
481
|
: true
|
|
411
482
|
: true
|
|
412
|
-
|
|
413
|
-
|
|
483
|
+
>
|
|
484
|
+
: true;
|
|
414
485
|
|
|
415
486
|
// A table introduced INSIDE a subquery is in scope only there — it must not
|
|
416
487
|
// satisfy an UNQUALIFIED column reference in the OUTER query. The whole-query
|
|
@@ -483,7 +554,46 @@ export type UnqualifiedColumnRefsValidFor<
|
|
|
483
554
|
Cols extends string
|
|
484
555
|
? CleanIdent<Cols> extends SelectAliases
|
|
485
556
|
? true
|
|
486
|
-
: UnqualifiedColumnValid<Cols, Tables, Aliases, S>
|
|
557
|
+
: UnqualifiedColumnValid<Cols, Tables, Aliases, S> extends true
|
|
558
|
+
? true
|
|
559
|
+
// A derived/VALUES source's column-alias list (`... ) as
|
|
560
|
+
// src(id, t)`) survives tokenization as bare identifier tokens,
|
|
561
|
+
// and `t` is not a schema column anywhere — bless names that
|
|
562
|
+
// belong to such a list. Checked ONLY after normal resolution
|
|
563
|
+
// fails, so valid queries never pay for the extraction, and
|
|
564
|
+
// blessing can only turn a reject into an accept (lenient
|
|
565
|
+
// contract).
|
|
566
|
+
: CleanIdent<Cols> extends SourceAliasListCols<N>
|
|
567
|
+
? true
|
|
568
|
+
: false
|
|
487
569
|
: true
|
|
488
570
|
>
|
|
489
571
|
: true;
|
|
572
|
+
|
|
573
|
+
// Every column name bound by a derived-source column-alias list — `) as p(a, b)`
|
|
574
|
+
// / `) p(a, b)` — plus CTE column-alias lists (`with c(x, y) as (`). Collected
|
|
575
|
+
// across ALL occurrences in the (normalized, single-line) query. The alias word
|
|
576
|
+
// itself must be space-free, mirroring `DerivedColsAfterAlias`'s guard, so a
|
|
577
|
+
// trailing `WHERE ... IN (...)` paren group is not misread as a column list.
|
|
578
|
+
type SourceAliasListCols<N extends string> =
|
|
579
|
+
N extends `with ${infer CteHead}(${infer CteCols}) as (${infer Rest}`
|
|
580
|
+
? AliasHasNoSpace<Trim<CteHead>> extends true
|
|
581
|
+
? ColNamesFromList<CteCols> | DerivedAliasListCols<Rest>
|
|
582
|
+
: DerivedAliasListCols<N>
|
|
583
|
+
: DerivedAliasListCols<N>;
|
|
584
|
+
|
|
585
|
+
type DerivedAliasListCols<N extends string, Steps extends any[] = []> =
|
|
586
|
+
Steps["length"] extends 15
|
|
587
|
+
? never
|
|
588
|
+
: N extends `${string}) as ${infer Rest}`
|
|
589
|
+
? Rest extends `${infer Alias}(${infer Cols})${infer Tail}`
|
|
590
|
+
? AliasHasNoSpace<Trim<Alias>> extends true
|
|
591
|
+
? ColNamesFromList<Cols> | DerivedAliasListCols<Tail, [any, ...Steps]>
|
|
592
|
+
: DerivedAliasListCols<Tail, [any, ...Steps]>
|
|
593
|
+
: never
|
|
594
|
+
: never;
|
|
595
|
+
|
|
596
|
+
type ColNamesFromList<L extends string> =
|
|
597
|
+
L extends `${infer A},${infer R}`
|
|
598
|
+
? CleanIdent<A> | ColNamesFromList<R>
|
|
599
|
+
: CleanIdent<L>;
|