effect-qb 0.16.0 → 0.19.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 -0
- package/dist/index.js +8065 -0
- package/dist/mysql.js +4036 -2418
- package/dist/postgres/metadata.js +2536 -625
- package/dist/postgres.js +8248 -7857
- package/dist/sqlite.js +8854 -0
- package/dist/standard.js +8019 -0
- package/package.json +15 -3
- package/src/casing.ts +71 -0
- package/src/index.ts +2 -0
- package/src/internal/casing.ts +89 -0
- package/src/internal/column-state.ts +11 -6
- package/src/internal/column.ts +44 -7
- package/src/internal/datatypes/define.ts +2 -1
- package/src/internal/datatypes/enrich.ts +23 -0
- package/src/internal/datatypes/lookup.ts +14 -7
- package/src/internal/derived-table.ts +7 -13
- package/src/internal/dialect-renderers/mysql.ts +2046 -0
- package/src/{postgres/internal/sql-expression-renderer.ts → internal/dialect-renderers/postgres.ts} +867 -283
- package/src/{mysql/internal/sql-expression-renderer.ts → internal/dialect-renderers/sqlite.ts} +834 -358
- package/src/internal/dialect.ts +37 -0
- package/src/internal/dsl-mutation-runtime.ts +29 -10
- package/src/internal/dsl-plan-runtime.ts +41 -24
- package/src/internal/dsl-query-runtime.ts +11 -31
- package/src/internal/dsl-transaction-ddl-runtime.ts +61 -15
- package/src/internal/executor.ts +57 -15
- package/src/internal/expression-ast.ts +3 -2
- package/src/internal/grouping-key.ts +216 -9
- package/src/internal/implication-runtime.ts +3 -2
- package/src/internal/json/types.ts +155 -40
- package/src/internal/predicate/context.ts +14 -1
- package/src/internal/predicate/key.ts +19 -2
- package/src/internal/predicate/runtime.ts +30 -3
- package/src/internal/query.d.ts +38 -11
- package/src/internal/query.ts +315 -54
- package/src/internal/renderer.ts +51 -6
- package/src/internal/runtime/driver-value-mapping.ts +58 -0
- package/src/internal/runtime/normalize.ts +74 -43
- package/src/internal/runtime/schema.ts +5 -3
- package/src/internal/runtime/value.ts +153 -30
- package/src/internal/scalar.ts +6 -1
- package/src/internal/schema-derivation.d.ts +12 -61
- package/src/internal/schema-derivation.ts +90 -38
- package/src/internal/schema-expression.ts +2 -2
- package/src/internal/sql-expression-renderer.ts +19 -0
- package/src/internal/standard-dsl.ts +6885 -0
- package/src/internal/table-options.ts +229 -62
- package/src/internal/table.d.ts +33 -32
- package/src/internal/table.ts +469 -160
- package/src/mysql/column-extension.ts +3 -0
- package/src/mysql/column.ts +27 -12
- package/src/mysql/datatypes/index.ts +24 -2
- package/src/mysql/errors/catalog.ts +5 -5
- package/src/mysql/errors/normalize.ts +2 -2
- package/src/mysql/executor.ts +7 -5
- package/src/mysql/internal/dialect.ts +9 -4
- package/src/mysql/internal/dsl.ts +906 -324
- package/src/mysql/internal/renderer.ts +7 -2
- package/src/mysql/json.ts +37 -0
- package/src/mysql/query-extension.ts +16 -0
- package/src/mysql/query.ts +9 -2
- package/src/mysql/renderer.ts +31 -4
- package/src/mysql.ts +4 -12
- package/src/postgres/column-extension.ts +28 -0
- package/src/postgres/column.ts +9 -13
- package/src/postgres/datatypes/index.d.ts +2 -1
- package/src/postgres/datatypes/index.ts +3 -2
- package/src/postgres/errors/normalize.ts +2 -2
- package/src/postgres/executor.ts +55 -10
- package/src/postgres/function/core.ts +20 -4
- package/src/postgres/function/index.ts +1 -17
- package/src/postgres/internal/dialect.ts +9 -4
- package/src/postgres/internal/dsl.ts +850 -359
- package/src/postgres/internal/renderer.ts +7 -2
- package/src/postgres/internal/schema-ddl.ts +22 -9
- package/src/postgres/internal/schema-model.ts +244 -10
- package/src/postgres/json.ts +100 -24
- package/src/postgres/jsonb.ts +38 -0
- package/src/postgres/query-extension.ts +2 -0
- package/src/postgres/query.ts +9 -2
- package/src/postgres/renderer.ts +31 -4
- package/src/postgres/schema-management.ts +108 -16
- package/src/postgres/schema.ts +98 -15
- package/src/postgres/table.ts +203 -398
- package/src/postgres/type.ts +8 -7
- package/src/postgres.ts +9 -11
- package/src/sqlite/column-extension.ts +3 -0
- package/src/sqlite/column.ts +127 -0
- package/src/sqlite/datatypes/index.ts +80 -0
- package/src/sqlite/datatypes/spec.ts +98 -0
- package/src/sqlite/errors/catalog.ts +103 -0
- package/src/sqlite/errors/fields.ts +19 -0
- package/src/sqlite/errors/index.ts +19 -0
- package/src/sqlite/errors/normalize.ts +229 -0
- package/src/sqlite/errors/requirements.ts +71 -0
- package/src/sqlite/errors/types.ts +29 -0
- package/src/sqlite/executor.ts +229 -0
- package/src/sqlite/function/aggregate.ts +2 -0
- package/src/sqlite/function/core.ts +2 -0
- package/src/sqlite/function/index.ts +19 -0
- package/src/sqlite/function/string.ts +2 -0
- package/src/sqlite/function/temporal.ts +100 -0
- package/src/sqlite/function/window.ts +2 -0
- package/src/sqlite/internal/dialect.ts +42 -0
- package/src/sqlite/internal/dsl.ts +6979 -0
- package/src/sqlite/internal/renderer.ts +51 -0
- package/src/sqlite/json.ts +39 -0
- package/src/sqlite/query-extension.ts +2 -0
- package/src/sqlite/query.ts +196 -0
- package/src/sqlite/renderer.ts +51 -0
- package/src/sqlite.ts +14 -0
- package/src/standard/column.ts +163 -0
- package/src/standard/datatypes/index.ts +83 -0
- package/src/standard/datatypes/spec.ts +98 -0
- package/src/standard/dialect.ts +40 -0
- package/src/standard/function/aggregate.ts +2 -0
- package/src/standard/function/core.ts +2 -0
- package/src/standard/function/index.ts +18 -0
- package/src/standard/function/string.ts +2 -0
- package/src/standard/function/temporal.ts +78 -0
- package/src/standard/function/window.ts +2 -0
- package/src/standard/internal/renderer.ts +45 -0
- package/src/standard/query.ts +152 -0
- package/src/standard/renderer.ts +21 -0
- package/src/standard/table.ts +147 -0
- package/src/standard.ts +18 -0
- package/src/internal/aggregation-validation.ts +0 -57
- package/src/mysql/table.ts +0 -157
package/src/internal/query.ts
CHANGED
|
@@ -6,6 +6,7 @@ import * as RowSet from "./row-set.js"
|
|
|
6
6
|
import * as Table from "./table.js"
|
|
7
7
|
import * as ExpressionAst from "./expression-ast.js"
|
|
8
8
|
import * as QueryAst from "./query-ast.js"
|
|
9
|
+
import type * as ProjectionAlias from "./projection-alias.js"
|
|
9
10
|
import type { JsonNode } from "./json/ast.js"
|
|
10
11
|
import type * as JsonPath from "./json/path.js"
|
|
11
12
|
import type { QueryCapability } from "./query-requirements.js"
|
|
@@ -24,7 +25,7 @@ import type {
|
|
|
24
25
|
} from "./predicate/analysis.js"
|
|
25
26
|
import type { AssumeFactsFalse, AssumeFactsTrue, PredicateContext } from "./predicate/context.js"
|
|
26
27
|
import type { FormulaOfPredicate } from "./predicate/normalize.js"
|
|
27
|
-
import type { ColumnKeyOfAst, ColumnKeyOfExpression, PredicateKeyOfAst } from "./predicate/key.js"
|
|
28
|
+
import type { ColumnKey, ColumnKeyOfAst, ColumnKeyOfExpression, PredicateKeyOfAst } from "./predicate/key.js"
|
|
28
29
|
import type { PredicateFormula, TrueFormula } from "./predicate/formula.js"
|
|
29
30
|
import { trueFormula } from "./predicate/runtime.js"
|
|
30
31
|
|
|
@@ -95,7 +96,7 @@ export { union_query_capabilities } from "./query-requirements.js"
|
|
|
95
96
|
* `Pipeable.pipe(...)` plus the metadata stored under `Expression.TypeId`.
|
|
96
97
|
*/
|
|
97
98
|
const ExpressionProto = {
|
|
98
|
-
pipe(this:
|
|
99
|
+
pipe(this: Pipeable) {
|
|
99
100
|
return pipeArguments(this, arguments)
|
|
100
101
|
}
|
|
101
102
|
}
|
|
@@ -107,7 +108,7 @@ const ExpressionProto = {
|
|
|
107
108
|
* chained through `.pipe(...)`.
|
|
108
109
|
*/
|
|
109
110
|
const PlanProto = {
|
|
110
|
-
pipe(this:
|
|
111
|
+
pipe(this: Pipeable) {
|
|
111
112
|
return pipeArguments(this, arguments)
|
|
112
113
|
}
|
|
113
114
|
}
|
|
@@ -142,6 +143,34 @@ export interface QueryState<
|
|
|
142
143
|
|
|
143
144
|
/** Effective SQL dialect carried by an expression. */
|
|
144
145
|
export type DialectOf<Value extends Expression.Any> = Value[typeof Expression.TypeId]["dialect"]
|
|
146
|
+
type ConcreteDialect<D> = D extends "standard" ? never : D
|
|
147
|
+
type IsDialectUnion<Union, All = Union> = Union extends any
|
|
148
|
+
? [All] extends [Union] ? false : true
|
|
149
|
+
: false
|
|
150
|
+
export type DialectConflictError<A extends string, B extends string> = string & {
|
|
151
|
+
readonly _tag: "DialectConflict"
|
|
152
|
+
readonly left: A
|
|
153
|
+
readonly right: B
|
|
154
|
+
}
|
|
155
|
+
export type MergeDialect<A extends string, B extends string> =
|
|
156
|
+
[ConcreteDialect<A>, ConcreteDialect<B>] extends [never, never] ? "standard"
|
|
157
|
+
: [ConcreteDialect<A>, ConcreteDialect<B>] extends [never, infer C extends string] ? C
|
|
158
|
+
: [ConcreteDialect<A>, ConcreteDialect<B>] extends [infer C extends string, never] ? C
|
|
159
|
+
: ConcreteDialect<A> extends ConcreteDialect<B>
|
|
160
|
+
? ConcreteDialect<B> extends ConcreteDialect<A>
|
|
161
|
+
? A
|
|
162
|
+
: DialectConflictError<A, B>
|
|
163
|
+
: DialectConflictError<A, B>
|
|
164
|
+
|
|
165
|
+
/** Collapses the portable standard tag out of a dialect union. */
|
|
166
|
+
export type NormalizeDialect<Dialect extends string> =
|
|
167
|
+
[Dialect] extends [never]
|
|
168
|
+
? never
|
|
169
|
+
: [Exclude<Dialect, "standard">] extends [never]
|
|
170
|
+
? "standard"
|
|
171
|
+
: IsDialectUnion<Exclude<Dialect, "standard">> extends true
|
|
172
|
+
? DialectConflictError<Exclude<Dialect, "standard">, Exclude<Dialect, "standard">>
|
|
173
|
+
: Exclude<Dialect, "standard">
|
|
145
174
|
/** Source dependency union carried by an expression. */
|
|
146
175
|
export type DependenciesOf<Value extends Expression.Any> = Expression.DependenciesOf<Value>
|
|
147
176
|
/** Aggregation kind carried by an expression. */
|
|
@@ -281,8 +310,57 @@ type JoinGroupingKeys<Keys extends readonly string[]> = Keys extends readonly []
|
|
|
281
310
|
? `${Head},${JoinGroupingKeys<Tail>}`
|
|
282
311
|
: string
|
|
283
312
|
|
|
313
|
+
type EscapeGroupingBackslashes<Value extends string> = string extends Value
|
|
314
|
+
? string
|
|
315
|
+
: Value extends `${infer Head}\\${infer Tail}`
|
|
316
|
+
? `${Head}\\\\${EscapeGroupingBackslashes<Tail>}`
|
|
317
|
+
: Value
|
|
318
|
+
|
|
319
|
+
type EscapeGroupingCommas<Value extends string> = string extends Value
|
|
320
|
+
? string
|
|
321
|
+
: Value extends `${infer Head},${infer Tail}`
|
|
322
|
+
? `${Head}\\,${EscapeGroupingCommas<Tail>}`
|
|
323
|
+
: Value
|
|
324
|
+
|
|
325
|
+
type EscapeGroupingPipes<Value extends string> = string extends Value
|
|
326
|
+
? string
|
|
327
|
+
: Value extends `${infer Head}|${infer Tail}`
|
|
328
|
+
? `${Head}\\|${EscapeGroupingPipes<Tail>}`
|
|
329
|
+
: Value
|
|
330
|
+
|
|
331
|
+
type EscapeGroupingEquals<Value extends string> = string extends Value
|
|
332
|
+
? string
|
|
333
|
+
: Value extends `${infer Head}=${infer Tail}`
|
|
334
|
+
? `${Head}\\=${EscapeGroupingEquals<Tail>}`
|
|
335
|
+
: Value
|
|
336
|
+
|
|
337
|
+
type EscapeGroupingGreaterThan<Value extends string> = string extends Value
|
|
338
|
+
? string
|
|
339
|
+
: Value extends `${infer Head}>${infer Tail}`
|
|
340
|
+
? `${Head}\\>${EscapeGroupingGreaterThan<Tail>}`
|
|
341
|
+
: Value
|
|
342
|
+
|
|
343
|
+
type EscapeGroupingText<Value extends string> =
|
|
344
|
+
EscapeGroupingGreaterThan<
|
|
345
|
+
EscapeGroupingEquals<
|
|
346
|
+
EscapeGroupingPipes<
|
|
347
|
+
EscapeGroupingCommas<
|
|
348
|
+
EscapeGroupingBackslashes<Value>
|
|
349
|
+
>
|
|
350
|
+
>
|
|
351
|
+
>
|
|
352
|
+
>
|
|
353
|
+
|
|
354
|
+
type JsonStringKeysGroupingKey<Keys extends readonly string[]> = Keys extends readonly []
|
|
355
|
+
? ""
|
|
356
|
+
: Keys extends readonly [infer Head extends string]
|
|
357
|
+
? EscapeGroupingText<Head>
|
|
358
|
+
: Keys extends readonly [infer Head extends string, ...infer Tail extends readonly string[]]
|
|
359
|
+
? `${EscapeGroupingText<Head>},${JsonStringKeysGroupingKey<Tail>}`
|
|
360
|
+
: string
|
|
361
|
+
|
|
284
362
|
type JsonSegmentGroupingKey<Segment> =
|
|
285
|
-
Segment extends JsonPath.KeySegment<infer Key extends string> ? `key:${Key}` :
|
|
363
|
+
Segment extends JsonPath.KeySegment<infer Key extends string> ? `key:${EscapeGroupingText<Key>}` :
|
|
286
364
|
Segment extends JsonPath.IndexSegment<infer Index extends number> ? `index:${Index}` :
|
|
287
365
|
Segment extends JsonPath.WildcardSegment ? "wildcard" :
|
|
288
366
|
Segment extends JsonPath.SliceSegment<infer Start extends number | undefined, infer End extends number | undefined>
|
|
@@ -290,7 +368,7 @@ type JsonSegmentGroupingKey<Segment> =
|
|
|
290
368
|
: Segment extends JsonPath.DescendSegment
|
|
291
369
|
? "descend"
|
|
292
370
|
: Segment extends string
|
|
293
|
-
? `key:${Segment}`
|
|
371
|
+
? `key:${EscapeGroupingText<Segment>}`
|
|
294
372
|
: Segment extends number
|
|
295
373
|
? `index:${Segment}`
|
|
296
374
|
: "unknown"
|
|
@@ -306,13 +384,13 @@ type JsonPathGroupingKey<Segments extends readonly any[]> = Segments extends rea
|
|
|
306
384
|
type JsonOpaquePathGroupingKey<Value> =
|
|
307
385
|
Value extends JsonPath.Path<infer Segments extends readonly JsonPath.CanonicalSegment[]>
|
|
308
386
|
? `jsonpath:${JsonPathGroupingKey<Segments>}` :
|
|
309
|
-
Value extends string ? `jsonpath:${Value}` :
|
|
387
|
+
Value extends string ? `jsonpath:${EscapeGroupingText<Value>}` :
|
|
310
388
|
Value extends Expression.Any ? `jsonpath:${GroupingKeyOfExpression<Value>}` :
|
|
311
389
|
"jsonpath:unknown"
|
|
312
390
|
|
|
313
391
|
type JsonEntryGroupingKey<Entry> =
|
|
314
392
|
Entry extends { readonly key: infer Key extends string; readonly value: infer Value extends Expression.Any }
|
|
315
|
-
? `${Key}=>${GroupingKeyOfExpression<Value>}`
|
|
393
|
+
? `${EscapeGroupingText<Key>}=>${GroupingKeyOfExpression<Value>}`
|
|
316
394
|
: "entry:unknown"
|
|
317
395
|
|
|
318
396
|
type JsonEntriesGroupingKey<Entries extends readonly { readonly key: string; readonly value: Expression.Any }[]> = Entries extends readonly []
|
|
@@ -338,13 +416,19 @@ type BranchGroupingKeys<
|
|
|
338
416
|
|
|
339
417
|
type GroupingKeyOfAst<Ast extends ExpressionAst.Any> =
|
|
340
418
|
Ast extends ExpressionAst.ColumnNode<infer TableName extends string, infer ColumnName extends string>
|
|
341
|
-
? `column:${TableName
|
|
419
|
+
? `column:${ColumnKey<TableName, ColumnName>}`
|
|
342
420
|
: Ast extends ExpressionAst.LiteralNode<infer Value>
|
|
343
421
|
? `literal:${LiteralGroupingKey<Value>}`
|
|
344
422
|
: Ast extends ExpressionAst.ExcludedNode<infer ColumnName extends string>
|
|
345
423
|
? `excluded:${ColumnName}`
|
|
346
424
|
: Ast extends ExpressionAst.CastNode<infer Value extends Expression.Any, infer Target extends Expression.DbType.Any>
|
|
347
425
|
? `cast(${GroupingKeyOfExpression<Value>} as ${Target["dialect"]}:${Target["kind"]})`
|
|
426
|
+
: Ast extends ExpressionAst.CollateNode<infer Value extends Expression.Any, infer Collation extends readonly [string, ...string[]]>
|
|
427
|
+
? `collate(${GroupingKeyOfExpression<Value>},${JsonStringKeysGroupingKey<Collation>})`
|
|
428
|
+
: Ast extends ExpressionAst.FunctionCallNode<infer Name extends string, infer Args extends readonly Expression.Any[]>
|
|
429
|
+
? `function(${EscapeGroupingText<Name>},${JoinGroupingKeys<{
|
|
430
|
+
readonly [K in keyof Args]: Args[K] extends Expression.Any ? GroupingKeyOfExpression<Args[K]> : never
|
|
431
|
+
} & readonly string[]>})`
|
|
348
432
|
: Ast extends ExpressionAst.UnaryNode<infer Kind extends ExpressionAst.UnaryKind, infer Value extends Expression.Any>
|
|
349
433
|
? `${Kind}(${GroupingKeyOfExpression<Value>})`
|
|
350
434
|
: Ast extends ExpressionAst.BinaryNode<infer Kind extends ExpressionAst.BinaryKind, infer Left extends Expression.Any, infer Right extends Expression.Any>
|
|
@@ -357,7 +441,7 @@ type GroupingKeyOfAst<Ast extends ExpressionAst.Any> =
|
|
|
357
441
|
? Kind extends "jsonGet" | "jsonPath" | "jsonAccess" | "jsonTraverse" | "jsonGetText" | "jsonPathText" | "jsonAccessText" | "jsonTraverseText"
|
|
358
442
|
? `json(${Kind},${GroupingKeyOfExpression<Extract<Ast["value"] | Ast["base"] | Ast["left"], Expression.Any>>},${JsonPathGroupingKey<Extract<Ast["segments"] | Ast["path"], readonly any[]>>})`
|
|
359
443
|
: Kind extends "jsonHasKey" | "jsonKeyExists" | "jsonHasAnyKeys" | "jsonHasAllKeys"
|
|
360
|
-
? `json(${Kind},${GroupingKeyOfExpression<Extract<Ast["value"] | Ast["base"] | Ast["left"], Expression.Any>>},${
|
|
444
|
+
? `json(${Kind},${GroupingKeyOfExpression<Extract<Ast["value"] | Ast["base"] | Ast["left"], Expression.Any>>},${JsonStringKeysGroupingKey<Extract<Ast["keys"], readonly string[]> & readonly string[]>})`
|
|
361
445
|
: Kind extends "jsonConcat" | "jsonMerge" | "jsonDelete" | "jsonDeletePath" | "jsonRemove" | "jsonSet" | "jsonInsert"
|
|
362
446
|
? `json(${Kind},${GroupingKeyOfExpression<Extract<Ast["left"] | Ast["base"] | Ast["value"], Expression.Any>>},${GroupingKeyOfExpression<Extract<Ast["right"] | Ast["newValue"] | Ast["insert"], Expression.Any>>},${JsonPathGroupingKey<Extract<Ast["segments"] | Ast["path"], readonly any[]>>})`
|
|
363
447
|
: Kind extends "jsonPathExists" | "jsonPathMatch"
|
|
@@ -408,15 +492,17 @@ export type ExtractRequired<Selection> = Selection extends Expression.Any
|
|
|
408
492
|
}[keyof Selection]
|
|
409
493
|
: never
|
|
410
494
|
|
|
411
|
-
|
|
412
|
-
export type ExtractDialect<Selection> = Selection extends Expression.Any
|
|
495
|
+
type ExtractDialectRaw<Selection> = Selection extends Expression.Any
|
|
413
496
|
? DialectOf<Selection>
|
|
414
497
|
: Selection extends Record<string, any>
|
|
415
498
|
? {
|
|
416
|
-
[K in keyof Selection]:
|
|
499
|
+
[K in keyof Selection]: ExtractDialectRaw<Selection[K]>
|
|
417
500
|
}[keyof Selection]
|
|
418
501
|
: never
|
|
419
502
|
|
|
503
|
+
/** Walks a selection tree and extracts the effective dialects referenced by its leaves. */
|
|
504
|
+
export type ExtractDialect<Selection> = NormalizeDialect<Extract<ExtractDialectRaw<Selection>, string>>
|
|
505
|
+
|
|
420
506
|
/**
|
|
421
507
|
* Minimal table-like shape required by `from(...)` and joins.
|
|
422
508
|
*
|
|
@@ -949,8 +1035,7 @@ export type UpdateInputOfTarget<Target extends MutationTargetInput> =
|
|
|
949
1035
|
}>
|
|
950
1036
|
: never
|
|
951
1037
|
|
|
952
|
-
|
|
953
|
-
export type SourceDialectOf<Source extends SourceLike> =
|
|
1038
|
+
type SourceDialectOfRaw<Source extends SourceLike> =
|
|
954
1039
|
Source extends TableLike<any, infer Dialect> ? Dialect :
|
|
955
1040
|
Source extends { readonly kind: "derived"; readonly plan: infer PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any> } ? PlanDialectOf<PlanValue> :
|
|
956
1041
|
Source extends { readonly kind: "cte"; readonly plan: infer PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any> } ? PlanDialectOf<PlanValue> :
|
|
@@ -958,6 +1043,9 @@ export type SourceDialectOf<Source extends SourceLike> =
|
|
|
958
1043
|
Source extends { readonly dialect: infer Dialect extends string } ? Dialect :
|
|
959
1044
|
never
|
|
960
1045
|
|
|
1046
|
+
/** Extracts the effective dialect from a source. */
|
|
1047
|
+
export type SourceDialectOf<Source extends SourceLike> = NormalizeDialect<SourceDialectOfRaw<Source>>
|
|
1048
|
+
|
|
961
1049
|
/** Extracts the base table name from a source. */
|
|
962
1050
|
export type SourceBaseNameOf<Source extends SourceLike> =
|
|
963
1051
|
Source extends TableLike<any, any> ? Source[typeof Table.TypeId]["baseName"] :
|
|
@@ -1000,6 +1088,153 @@ type JoinPath<Segments extends readonly string[]> = Segments extends readonly []
|
|
|
1000
1088
|
? `${Head}__${JoinPath<Tail>}`
|
|
1001
1089
|
: string
|
|
1002
1090
|
|
|
1091
|
+
type ProjectionAliasOf<
|
|
1092
|
+
Value extends Expression.Any,
|
|
1093
|
+
Path extends readonly string[]
|
|
1094
|
+
> = Value extends {
|
|
1095
|
+
readonly [ProjectionAlias.TypeId]: ProjectionAlias.State<infer Alias extends string>
|
|
1096
|
+
} ? Alias : JoinPath<Path>
|
|
1097
|
+
|
|
1098
|
+
type SelectionProjectionEntry<
|
|
1099
|
+
Alias extends string,
|
|
1100
|
+
Path extends readonly string[],
|
|
1101
|
+
ExpectedAlias extends string
|
|
1102
|
+
> = {
|
|
1103
|
+
readonly alias: Alias
|
|
1104
|
+
readonly path: Path
|
|
1105
|
+
readonly expectedAlias: ExpectedAlias
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
type SelectionProjectionEntries<
|
|
1109
|
+
Selection,
|
|
1110
|
+
Path extends readonly string[] = []
|
|
1111
|
+
> = Selection extends Expression.Any
|
|
1112
|
+
? SelectionProjectionEntry<ProjectionAliasOf<Selection, Path>, Path, JoinPath<Path>>
|
|
1113
|
+
: Selection extends Record<string, any>
|
|
1114
|
+
? {
|
|
1115
|
+
readonly [K in Extract<keyof Selection, string>]:
|
|
1116
|
+
SelectionProjectionEntries<Selection[K], [...Path, K]>
|
|
1117
|
+
}[Extract<keyof Selection, string>]
|
|
1118
|
+
: never
|
|
1119
|
+
|
|
1120
|
+
type SameProjectionPath<
|
|
1121
|
+
Left extends readonly string[],
|
|
1122
|
+
Right extends readonly string[]
|
|
1123
|
+
> = Left extends readonly []
|
|
1124
|
+
? Right extends readonly [] ? true : false
|
|
1125
|
+
: Left extends readonly [infer LeftHead extends string, ...infer LeftTail extends readonly string[]]
|
|
1126
|
+
? Right extends readonly [infer RightHead extends string, ...infer RightTail extends readonly string[]]
|
|
1127
|
+
? [LeftHead] extends [RightHead]
|
|
1128
|
+
? [RightHead] extends [LeftHead]
|
|
1129
|
+
? SameProjectionPath<LeftTail, RightTail>
|
|
1130
|
+
: false
|
|
1131
|
+
: false
|
|
1132
|
+
: false
|
|
1133
|
+
: false
|
|
1134
|
+
|
|
1135
|
+
type SameProjectionAlias<
|
|
1136
|
+
Left extends string,
|
|
1137
|
+
Right extends string
|
|
1138
|
+
> = [Left] extends [Right] ? [Right] extends [Left] ? true : false : false
|
|
1139
|
+
|
|
1140
|
+
type HasDifferentPathForAlias<
|
|
1141
|
+
Entries,
|
|
1142
|
+
Alias extends string,
|
|
1143
|
+
Path extends readonly string[]
|
|
1144
|
+
> = Entries extends SelectionProjectionEntry<infer EntryAlias, infer EntryPath, any>
|
|
1145
|
+
? SameProjectionAlias<EntryAlias, Alias> extends true
|
|
1146
|
+
? SameProjectionPath<EntryPath, Path> extends true ? never : true
|
|
1147
|
+
: never
|
|
1148
|
+
: never
|
|
1149
|
+
|
|
1150
|
+
type DuplicateProjectionAliases<
|
|
1151
|
+
Entries,
|
|
1152
|
+
AllEntries = Entries
|
|
1153
|
+
> = Entries extends SelectionProjectionEntry<infer Alias, infer Path, any>
|
|
1154
|
+
? string extends Alias ? never
|
|
1155
|
+
: true extends HasDifferentPathForAlias<AllEntries, Alias, Path> ? Alias : never
|
|
1156
|
+
: never
|
|
1157
|
+
|
|
1158
|
+
type ProjectionAliasMismatches<Entries> =
|
|
1159
|
+
Entries extends SelectionProjectionEntry<infer Alias, any, infer ExpectedAlias>
|
|
1160
|
+
? string extends Alias ? never
|
|
1161
|
+
: string extends ExpectedAlias ? never
|
|
1162
|
+
: SameProjectionAlias<Alias, ExpectedAlias> extends true ? never : Alias
|
|
1163
|
+
: never
|
|
1164
|
+
|
|
1165
|
+
type DerivedProjectionDuplicateAliases<Selection> =
|
|
1166
|
+
DuplicateProjectionAliases<SelectionProjectionEntries<Selection>>
|
|
1167
|
+
|
|
1168
|
+
type DerivedProjectionAliasMismatches<Selection> =
|
|
1169
|
+
ProjectionAliasMismatches<SelectionProjectionEntries<Selection>>
|
|
1170
|
+
|
|
1171
|
+
type IsAny<Value> = 0 extends (1 & Value) ? true : false
|
|
1172
|
+
|
|
1173
|
+
type IsBroadSelection<Selection> =
|
|
1174
|
+
IsAny<Selection> extends true ? true :
|
|
1175
|
+
unknown extends Selection ? true : false
|
|
1176
|
+
|
|
1177
|
+
type DerivedProjectionIssues<Selection> =
|
|
1178
|
+
IsBroadSelection<Selection> extends true
|
|
1179
|
+
? never
|
|
1180
|
+
: | DerivedProjectionDuplicateAliases<Selection>
|
|
1181
|
+
| DerivedProjectionAliasMismatches<Selection>
|
|
1182
|
+
|
|
1183
|
+
export type SelectionProjectionDuplicateAliases<Selection> =
|
|
1184
|
+
IsBroadSelection<Selection> extends true
|
|
1185
|
+
? never
|
|
1186
|
+
: DuplicateProjectionAliases<SelectionProjectionEntries<Selection>>
|
|
1187
|
+
|
|
1188
|
+
export type SelectionProjectionAliasCollisionError<Selection> = Selection & {
|
|
1189
|
+
readonly __effect_qb_error__: "effect-qb: projection aliases must be unique"
|
|
1190
|
+
readonly __effect_qb_duplicate_projection_aliases__: SelectionProjectionDuplicateAliases<Selection>
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
export type SelectionProjectionAliasCollisionConstraint<Selection> =
|
|
1194
|
+
[SelectionProjectionDuplicateAliases<Selection>] extends [never]
|
|
1195
|
+
? unknown
|
|
1196
|
+
: SelectionProjectionAliasCollisionError<Selection>
|
|
1197
|
+
|
|
1198
|
+
export type DerivedSourceProjectionCompatibilityError<
|
|
1199
|
+
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>
|
|
1200
|
+
> = PlanValue & {
|
|
1201
|
+
readonly __effect_qb_error__: "effect-qb: derived subqueries require unique path-based projection aliases"
|
|
1202
|
+
readonly __effect_qb_duplicate_projection_aliases__: DerivedProjectionDuplicateAliases<SelectionOfPlan<PlanValue>>
|
|
1203
|
+
readonly __effect_qb_alias_mismatches__: DerivedProjectionAliasMismatches<SelectionOfPlan<PlanValue>>
|
|
1204
|
+
readonly __effect_qb_hint__: "Use unique nested selection paths and do not override projection aliases inside derived subqueries"
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
export type DerivedProjectionCompatiblePlan<
|
|
1208
|
+
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>,
|
|
1209
|
+
ValidPlan = PlanValue
|
|
1210
|
+
> = [DerivedProjectionIssues<SelectionOfPlan<PlanValue>>] extends [never]
|
|
1211
|
+
? ValidPlan
|
|
1212
|
+
: ValidPlan & DerivedSourceProjectionCompatibilityError<PlanValue>
|
|
1213
|
+
|
|
1214
|
+
export type DerivedSourceCompatiblePlan<
|
|
1215
|
+
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>
|
|
1216
|
+
> = DerivedProjectionCompatiblePlan<PlanValue, CompletePlan<PlanValue>>
|
|
1217
|
+
|
|
1218
|
+
type InlineSourceStatementError<
|
|
1219
|
+
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>
|
|
1220
|
+
> = PlanValue & {
|
|
1221
|
+
readonly __effect_qb_error__: "effect-qb: inline derived sources only accept select-like query plans"
|
|
1222
|
+
readonly __effect_qb_statement__: StatementOfPlan<PlanValue>
|
|
1223
|
+
readonly __effect_qb_hint__: "Use select(...) or a set operator for as(...) and lateral(...); use with(...) for data-modifying CTEs where supported"
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
export type DerivedTableCompatiblePlan<
|
|
1227
|
+
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>
|
|
1228
|
+
> = StatementOfPlan<PlanValue> extends SelectLikeStatement
|
|
1229
|
+
? DerivedSourceCompatiblePlan<PlanValue>
|
|
1230
|
+
: InlineSourceStatementError<PlanValue>
|
|
1231
|
+
|
|
1232
|
+
export type LateralSourceCompatiblePlan<
|
|
1233
|
+
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>
|
|
1234
|
+
> = StatementOfPlan<PlanValue> extends SelectLikeStatement
|
|
1235
|
+
? DerivedProjectionCompatiblePlan<PlanValue>
|
|
1236
|
+
: InlineSourceStatementError<PlanValue>
|
|
1237
|
+
|
|
1003
1238
|
type DerivedLeafExpression<
|
|
1004
1239
|
Value extends Expression.Any,
|
|
1005
1240
|
Alias extends string,
|
|
@@ -1066,7 +1301,7 @@ export type AvailableOfPlan<
|
|
|
1066
1301
|
/** Extracts the effective dialect carried by a query plan. */
|
|
1067
1302
|
export type PlanDialectOf<
|
|
1068
1303
|
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any, any, any>
|
|
1069
|
-
> = QueryPlanParts<PlanValue>["dialect"]
|
|
1304
|
+
> = NormalizeDialect<QueryPlanParts<PlanValue>["dialect"]>
|
|
1070
1305
|
/** Extracts the grouped-source phantom carried by a query plan. */
|
|
1071
1306
|
export type GroupedOfPlan<
|
|
1072
1307
|
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any, any, any>
|
|
@@ -1085,6 +1320,12 @@ export type AssumptionsOfPlan<
|
|
|
1085
1320
|
export type FactsOfPlan<
|
|
1086
1321
|
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any, any, any>
|
|
1087
1322
|
> = QueryPlanState<PlanValue>["facts"]
|
|
1323
|
+
export type CommonSetFacts<
|
|
1324
|
+
Left extends QueryPlan<any, any, any, any, any, any, any, any, any, any>,
|
|
1325
|
+
Right extends QueryPlan<any, any, any, any, any, any, any, any, any, any>
|
|
1326
|
+
> = [FactsOfPlan<Left>] extends [FactsOfPlan<Right>]
|
|
1327
|
+
? [FactsOfPlan<Right>] extends [FactsOfPlan<Left>] ? FactsOfPlan<Left> : EmptyFacts
|
|
1328
|
+
: EmptyFacts
|
|
1088
1329
|
export type PredicateStateOfPlan<
|
|
1089
1330
|
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any, any, any>
|
|
1090
1331
|
> = PredicateState<AssumptionsOfPlan<PlanValue>, FactsOfPlan<PlanValue>>
|
|
@@ -1217,9 +1458,10 @@ export type AddJoinRequired<
|
|
|
1217
1458
|
Available extends Record<string, RowSet.AnySource>,
|
|
1218
1459
|
JoinedName extends string,
|
|
1219
1460
|
Predicate extends PredicateInput | never,
|
|
1220
|
-
Kind extends QueryAst.JoinKind = "inner"
|
|
1461
|
+
Kind extends QueryAst.JoinKind = "inner",
|
|
1462
|
+
SourceRequired extends string = never
|
|
1221
1463
|
> = Exclude<
|
|
1222
|
-
Required | (Predicate extends never ? never : RequiredFromInput<Predicate>),
|
|
1464
|
+
Required | SourceRequired | (Predicate extends never ? never : RequiredFromInput<Predicate>),
|
|
1223
1465
|
AvailableNames<AvailableAfterJoin<Available, JoinedName, Kind>>
|
|
1224
1466
|
>
|
|
1225
1467
|
|
|
@@ -1270,7 +1512,7 @@ export type MergeNullabilityTuple<
|
|
|
1270
1512
|
/** Dialect union across a tuple of expressions. */
|
|
1271
1513
|
export type TupleDialect<
|
|
1272
1514
|
Values extends readonly Expression.Any[]
|
|
1273
|
-
> = Values[number] extends never ? never : DialectOf<Values[number]
|
|
1515
|
+
> = Values[number] extends never ? never : NormalizeDialect<DialectOf<Values[number]>>
|
|
1274
1516
|
|
|
1275
1517
|
/** Converts a union into an intersection. */
|
|
1276
1518
|
type UnionToIntersection<Union> = (
|
|
@@ -1744,30 +1986,45 @@ type JsonLiteralSetsForColumn<
|
|
|
1744
1986
|
? Paths
|
|
1745
1987
|
: {}
|
|
1746
1988
|
|
|
1989
|
+
type JsonPathHead<
|
|
1990
|
+
Path extends string,
|
|
1991
|
+
Current extends string = ""
|
|
1992
|
+
> = Path extends `\\.${infer Rest}`
|
|
1993
|
+
? JsonPathHead<Rest, `${Current}.`>
|
|
1994
|
+
: Path extends `\\\\${infer Rest}`
|
|
1995
|
+
? JsonPathHead<Rest, `${Current}\\`>
|
|
1996
|
+
: Path extends `.${infer Tail}`
|
|
1997
|
+
? readonly [Current, Tail]
|
|
1998
|
+
: Path extends `${infer Character}${infer Rest}`
|
|
1999
|
+
? JsonPathHead<Rest, `${Current}${Character}`>
|
|
2000
|
+
: readonly [Current, ""]
|
|
2001
|
+
|
|
1747
2002
|
type RefineJsonRuntimeAtPath<
|
|
1748
2003
|
Runtime,
|
|
1749
2004
|
Path extends string,
|
|
1750
2005
|
Values
|
|
1751
2006
|
> = Runtime extends unknown
|
|
1752
|
-
? Path extends
|
|
1753
|
-
?
|
|
1754
|
-
?
|
|
1755
|
-
?
|
|
1756
|
-
? [
|
|
1757
|
-
? never
|
|
1758
|
-
|
|
2007
|
+
? JsonPathHead<Path> extends readonly [infer Head extends string, infer Tail extends string]
|
|
2008
|
+
? Tail extends ""
|
|
2009
|
+
? Runtime extends object
|
|
2010
|
+
? Head extends keyof Runtime
|
|
2011
|
+
? RefineRuntimeByAllowedLiterals<NonNullable<Runtime[Head]>, Values> extends infer Refined
|
|
2012
|
+
? [Refined] extends [never]
|
|
2013
|
+
? never
|
|
2014
|
+
: Omit<Runtime, Head> & { readonly [K in Head]: Refined }
|
|
2015
|
+
: never
|
|
1759
2016
|
: never
|
|
1760
|
-
:
|
|
1761
|
-
:
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
:
|
|
2017
|
+
: RefineRuntimeByAllowedLiterals<NonNullable<Runtime>, Values>
|
|
2018
|
+
: Runtime extends object
|
|
2019
|
+
? Head extends keyof Runtime
|
|
2020
|
+
? RefineJsonRuntimeAtPath<NonNullable<Runtime[Head]>, Tail, Values> extends infer Refined
|
|
2021
|
+
? [Refined] extends [never]
|
|
2022
|
+
? never
|
|
2023
|
+
: Omit<Runtime, Head> & { readonly [K in Head]: Refined }
|
|
2024
|
+
: never
|
|
1768
2025
|
: never
|
|
1769
2026
|
: never
|
|
1770
|
-
|
|
2027
|
+
: never
|
|
1771
2028
|
: never
|
|
1772
2029
|
|
|
1773
2030
|
type RefineJsonRuntimeWithConstraints<
|
|
@@ -1914,7 +2171,7 @@ type DialectCompatibilityError<
|
|
|
1914
2171
|
EngineDialect extends string
|
|
1915
2172
|
> = PlanValue & {
|
|
1916
2173
|
readonly __effect_qb_error__: "effect-qb: plan dialect is not compatible with the target renderer or executor"
|
|
1917
|
-
readonly __effect_qb_plan_dialect__: PlanValue
|
|
2174
|
+
readonly __effect_qb_plan_dialect__: PlanDialectOf<PlanValue>
|
|
1918
2175
|
readonly __effect_qb_target_dialect__: EngineDialect
|
|
1919
2176
|
readonly __effect_qb_hint__: "Use the matching dialect module or renderer/executor"
|
|
1920
2177
|
}
|
|
@@ -1965,27 +2222,39 @@ type IsDialectCompatible<
|
|
|
1965
2222
|
EngineDialect extends string
|
|
1966
2223
|
> = [PlanDialect] extends [never]
|
|
1967
2224
|
? true
|
|
1968
|
-
:
|
|
1969
|
-
?
|
|
1970
|
-
:
|
|
2225
|
+
: Exclude<PlanDialect, EngineDialect | "standard"> extends never
|
|
2226
|
+
? true
|
|
2227
|
+
: false
|
|
1971
2228
|
|
|
1972
2229
|
/** Narrows a complete plan to those compatible with a target engine dialect. */
|
|
1973
2230
|
export type DialectCompatiblePlan<
|
|
1974
2231
|
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>,
|
|
1975
2232
|
EngineDialect extends string
|
|
1976
|
-
> = IsDialectCompatible<PlanValue
|
|
2233
|
+
> = IsDialectCompatible<PlanDialectOf<PlanValue>, EngineDialect> extends true
|
|
1977
2234
|
? CompletePlan<PlanValue>
|
|
1978
2235
|
: DialectCompatibilityError<PlanValue, EngineDialect>
|
|
1979
2236
|
|
|
2237
|
+
type SelectLikeStatement = "select" | "set"
|
|
2238
|
+
|
|
2239
|
+
type NestedPlanStatementError<
|
|
2240
|
+
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>
|
|
2241
|
+
> = PlanValue & {
|
|
2242
|
+
readonly __effect_qb_error__: "effect-qb: subquery expressions only accept select-like query plans"
|
|
2243
|
+
readonly __effect_qb_statement__: StatementOfPlan<PlanValue>
|
|
2244
|
+
readonly __effect_qb_hint__: "Use select(...) or a set operator as the nested subquery expression"
|
|
2245
|
+
}
|
|
2246
|
+
|
|
1980
2247
|
/** Nested-plan compatibility used by subquery expressions such as `exists(...)`. */
|
|
1981
2248
|
export type DialectCompatibleNestedPlan<
|
|
1982
2249
|
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>,
|
|
1983
2250
|
EngineDialect extends string
|
|
1984
|
-
> = IsDialectCompatible<PlanValue
|
|
1985
|
-
?
|
|
2251
|
+
> = IsDialectCompatible<PlanDialectOf<PlanValue>, EngineDialect> extends true
|
|
2252
|
+
? StatementOfPlan<PlanValue> extends SelectLikeStatement
|
|
2253
|
+
? AggregationCompatiblePlan<PlanValue>
|
|
2254
|
+
: NestedPlanStatementError<PlanValue>
|
|
1986
2255
|
: DialectCompatibilityError<PlanValue, EngineDialect>
|
|
1987
2256
|
|
|
1988
|
-
type SetOperandStatement =
|
|
2257
|
+
type SetOperandStatement = SelectLikeStatement
|
|
1989
2258
|
type IsUnion<Value, All = Value> = Value extends any ? ([All] extends [Value] ? false : true) : never
|
|
1990
2259
|
|
|
1991
2260
|
type SingleSelectedExpressionError<
|
|
@@ -2205,7 +2474,7 @@ export const makeExpression = <
|
|
|
2205
2474
|
Object.defineProperty(expression, "pipe", {
|
|
2206
2475
|
configurable: true,
|
|
2207
2476
|
writable: true,
|
|
2208
|
-
value: function(this:
|
|
2477
|
+
value: function(this: Pipeable) {
|
|
2209
2478
|
return pipeArguments(expression, arguments)
|
|
2210
2479
|
}
|
|
2211
2480
|
})
|
|
@@ -2255,7 +2524,7 @@ export const makePlan = <
|
|
|
2255
2524
|
Object.defineProperty(plan, "pipe", {
|
|
2256
2525
|
configurable: true,
|
|
2257
2526
|
writable: true,
|
|
2258
|
-
value: function(this:
|
|
2527
|
+
value: function(this: Pipeable) {
|
|
2259
2528
|
return pipeArguments(plan, arguments)
|
|
2260
2529
|
}
|
|
2261
2530
|
})
|
|
@@ -2314,16 +2583,8 @@ export const extractRequiredRuntime = (selection: SelectionShape): readonly stri
|
|
|
2314
2583
|
|
|
2315
2584
|
/** Extracts the single top-level expression from a scalar subquery selection. */
|
|
2316
2585
|
export const extractSingleSelectedExpressionRuntime = (selection: SelectionShape): Expression.Any => {
|
|
2317
|
-
const
|
|
2318
|
-
|
|
2319
|
-
throw new Error("scalar subqueries must select exactly one top-level expression")
|
|
2320
|
-
}
|
|
2321
|
-
const record = selection as Record<string, unknown>
|
|
2322
|
-
const value = record[keys[0]!]
|
|
2323
|
-
if (value === null || typeof value !== "object" || !(Expression.TypeId in (value as object))) {
|
|
2324
|
-
throw new Error("scalar subqueries must select a scalar expression")
|
|
2325
|
-
}
|
|
2326
|
-
return value as unknown as Expression.Any
|
|
2586
|
+
const record = selection as Record<string, Expression.Any>
|
|
2587
|
+
return record[Object.keys(record)[0]!]!
|
|
2327
2588
|
}
|
|
2328
2589
|
|
|
2329
2590
|
/** Converts the plan's runtime `required` metadata into a mutable string list. */
|
package/src/internal/renderer.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as Query from "./query.js"
|
|
2
|
-
import
|
|
3
|
-
import { type Projection, validateProjections } from "./projections.js"
|
|
2
|
+
import * as Expression from "./scalar.js"
|
|
3
|
+
import { flattenSelection, type Projection, validateProjections } from "./projections.js"
|
|
4
4
|
|
|
5
5
|
/** Symbol used to attach rendered-query phantom row metadata. */
|
|
6
6
|
export const TypeId: unique symbol = Symbol.for("effect-qb/Renderer")
|
|
@@ -37,14 +37,13 @@ export type RowOf<Value extends RenderedQuery<any, any>> = Value[typeof TypeId][
|
|
|
37
37
|
*
|
|
38
38
|
* Renderers only accept complete, dialect-compatible plans. The returned
|
|
39
39
|
* `RenderedQuery` keeps the canonical `Query.ResultRow<...>` type attached for
|
|
40
|
-
* downstream executor layers
|
|
41
|
-
* matching runtime aggregate-shape validation.
|
|
40
|
+
* downstream executor layers.
|
|
42
41
|
*/
|
|
43
42
|
export interface Renderer<Dialect extends string = string> {
|
|
44
43
|
readonly dialect: Dialect
|
|
45
44
|
render<PlanValue extends Query.Plan.Any>(
|
|
46
45
|
plan: Query.DialectCompatiblePlan<PlanValue, Dialect>
|
|
47
|
-
): RenderedQuery<
|
|
46
|
+
): RenderedQuery<Query.ResultRow<PlanValue>, Dialect>
|
|
48
47
|
}
|
|
49
48
|
|
|
50
49
|
type CustomRender<Dialect extends string> = <PlanValue extends Query.Plan.Any>(
|
|
@@ -56,6 +55,29 @@ type CustomRender<Dialect extends string> = <PlanValue extends Query.Plan.Any>(
|
|
|
56
55
|
readonly valueMappings?: Expression.DriverValueMappings
|
|
57
56
|
}
|
|
58
57
|
|
|
58
|
+
const projectionPathKey = (path: readonly string[]): string => JSON.stringify(path)
|
|
59
|
+
|
|
60
|
+
const formatProjectionPath = (path: readonly string[]): string => path.join(".")
|
|
61
|
+
|
|
62
|
+
const validateProjectionPathsMatchSelection = (
|
|
63
|
+
plan: Query.Plan.Any,
|
|
64
|
+
projections: readonly Projection[]
|
|
65
|
+
): void => {
|
|
66
|
+
const expected = flattenSelection(Query.getAst(plan).select as Record<string, unknown>)
|
|
67
|
+
const expectedPaths = new Set(expected.map((projection) => projectionPathKey(projection.path)))
|
|
68
|
+
const actualPaths = new Set(projections.map((projection) => projectionPathKey(projection.path)))
|
|
69
|
+
for (const projection of projections) {
|
|
70
|
+
if (!expectedPaths.has(projectionPathKey(projection.path))) {
|
|
71
|
+
throw new Error(`Projection path ${formatProjectionPath(projection.path)} does not exist in the query selection`)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
for (const projection of expected) {
|
|
75
|
+
if (!actualPaths.has(projectionPathKey(projection.path))) {
|
|
76
|
+
throw new Error(`Projection path ${formatProjectionPath(projection.path)} is missing from rendered projections`)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
59
81
|
/**
|
|
60
82
|
* Constructs a renderer from a dialect and implementation callback.
|
|
61
83
|
*/
|
|
@@ -67,6 +89,26 @@ export function make<Dialect extends string>(
|
|
|
67
89
|
dialect: Dialect,
|
|
68
90
|
render: CustomRender<Dialect>
|
|
69
91
|
): Renderer<Dialect> {
|
|
92
|
+
return makeRenderer(dialect, render, true)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Internal renderer factory for built-in renderers that derive projections from typed plans. */
|
|
96
|
+
export function makeTrusted<Dialect extends string>(
|
|
97
|
+
dialect: Dialect,
|
|
98
|
+
render: CustomRender<Dialect>
|
|
99
|
+
): Renderer<Dialect>
|
|
100
|
+
export function makeTrusted<Dialect extends string>(
|
|
101
|
+
dialect: Dialect,
|
|
102
|
+
render: CustomRender<Dialect>
|
|
103
|
+
): Renderer<Dialect> {
|
|
104
|
+
return makeRenderer(dialect, render, false)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const makeRenderer = <Dialect extends string>(
|
|
108
|
+
dialect: Dialect,
|
|
109
|
+
render: CustomRender<Dialect>,
|
|
110
|
+
validate: boolean
|
|
111
|
+
): Renderer<Dialect> => {
|
|
70
112
|
if (typeof render !== "function") {
|
|
71
113
|
throw new Error(`Renderer.make requires an explicit render implementation for dialect: ${dialect}`)
|
|
72
114
|
}
|
|
@@ -75,7 +117,10 @@ export function make<Dialect extends string>(
|
|
|
75
117
|
render(plan) {
|
|
76
118
|
const rendered = render(plan)
|
|
77
119
|
const projections = rendered.projections ?? []
|
|
78
|
-
|
|
120
|
+
if (validate) {
|
|
121
|
+
validateProjections(projections)
|
|
122
|
+
validateProjectionPathsMatchSelection(plan as Query.Plan.Any, projections)
|
|
123
|
+
}
|
|
79
124
|
return {
|
|
80
125
|
sql: rendered.sql,
|
|
81
126
|
params: rendered.params ?? [],
|