effect-qb 0.16.0 → 0.17.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/dist/mysql.js +1661 -591
- package/dist/postgres/metadata.js +1930 -135
- package/dist/postgres.js +7808 -6718
- package/dist/sqlite.js +8360 -0
- package/package.json +6 -1
- package/src/internal/derived-table.ts +29 -3
- package/src/internal/dialect.ts +2 -0
- package/src/internal/dsl-mutation-runtime.ts +173 -4
- package/src/internal/dsl-plan-runtime.ts +165 -20
- package/src/internal/dsl-query-runtime.ts +60 -6
- package/src/internal/dsl-transaction-ddl-runtime.ts +72 -2
- package/src/internal/executor.ts +47 -9
- package/src/internal/expression-ast.ts +3 -2
- package/src/internal/grouping-key.ts +141 -1
- package/src/internal/implication-runtime.ts +2 -1
- 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 +27 -3
- package/src/internal/query.ts +252 -30
- package/src/internal/renderer.ts +35 -2
- package/src/internal/runtime/driver-value-mapping.ts +58 -0
- package/src/internal/runtime/normalize.ts +62 -38
- package/src/internal/runtime/schema.ts +5 -3
- package/src/internal/runtime/value.ts +153 -30
- package/src/internal/table-options.ts +108 -1
- package/src/internal/table.ts +87 -29
- package/src/mysql/column.ts +18 -2
- package/src/mysql/datatypes/index.ts +21 -0
- package/src/mysql/errors/catalog.ts +5 -5
- package/src/mysql/errors/normalize.ts +2 -2
- package/src/mysql/internal/dsl.ts +736 -218
- package/src/mysql/internal/renderer.ts +2 -1
- package/src/mysql/internal/sql-expression-renderer.ts +486 -130
- package/src/mysql/query.ts +9 -2
- package/src/mysql/table.ts +38 -12
- package/src/postgres/column.ts +4 -2
- package/src/postgres/errors/normalize.ts +2 -2
- package/src/postgres/executor.ts +48 -5
- package/src/postgres/function/core.ts +19 -1
- package/src/postgres/internal/dsl.ts +683 -240
- package/src/postgres/internal/renderer.ts +2 -1
- package/src/postgres/internal/schema-ddl.ts +2 -1
- package/src/postgres/internal/schema-model.ts +6 -3
- package/src/postgres/internal/sql-expression-renderer.ts +420 -91
- package/src/postgres/json.ts +57 -17
- package/src/postgres/query.ts +9 -2
- package/src/postgres/schema-management.ts +91 -4
- package/src/postgres/schema.ts +1 -1
- package/src/postgres/table.ts +189 -53
- package/src/sqlite/column.ts +128 -0
- package/src/sqlite/datatypes/index.ts +79 -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 +227 -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 +37 -0
- package/src/sqlite/internal/dsl.ts +6926 -0
- package/src/sqlite/internal/renderer.ts +47 -0
- package/src/sqlite/internal/sql-expression-renderer.ts +1821 -0
- package/src/sqlite/json.ts +2 -0
- package/src/sqlite/query.ts +196 -0
- package/src/sqlite/renderer.ts +24 -0
- package/src/sqlite/table.ts +183 -0
- package/src/sqlite.ts +22 -0
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
|
|
|
@@ -281,8 +282,57 @@ type JoinGroupingKeys<Keys extends readonly string[]> = Keys extends readonly []
|
|
|
281
282
|
? `${Head},${JoinGroupingKeys<Tail>}`
|
|
282
283
|
: string
|
|
283
284
|
|
|
285
|
+
type EscapeGroupingBackslashes<Value extends string> = string extends Value
|
|
286
|
+
? string
|
|
287
|
+
: Value extends `${infer Head}\\${infer Tail}`
|
|
288
|
+
? `${Head}\\\\${EscapeGroupingBackslashes<Tail>}`
|
|
289
|
+
: Value
|
|
290
|
+
|
|
291
|
+
type EscapeGroupingCommas<Value extends string> = string extends Value
|
|
292
|
+
? string
|
|
293
|
+
: Value extends `${infer Head},${infer Tail}`
|
|
294
|
+
? `${Head}\\,${EscapeGroupingCommas<Tail>}`
|
|
295
|
+
: Value
|
|
296
|
+
|
|
297
|
+
type EscapeGroupingPipes<Value extends string> = string extends Value
|
|
298
|
+
? string
|
|
299
|
+
: Value extends `${infer Head}|${infer Tail}`
|
|
300
|
+
? `${Head}\\|${EscapeGroupingPipes<Tail>}`
|
|
301
|
+
: Value
|
|
302
|
+
|
|
303
|
+
type EscapeGroupingEquals<Value extends string> = string extends Value
|
|
304
|
+
? string
|
|
305
|
+
: Value extends `${infer Head}=${infer Tail}`
|
|
306
|
+
? `${Head}\\=${EscapeGroupingEquals<Tail>}`
|
|
307
|
+
: Value
|
|
308
|
+
|
|
309
|
+
type EscapeGroupingGreaterThan<Value extends string> = string extends Value
|
|
310
|
+
? string
|
|
311
|
+
: Value extends `${infer Head}>${infer Tail}`
|
|
312
|
+
? `${Head}\\>${EscapeGroupingGreaterThan<Tail>}`
|
|
313
|
+
: Value
|
|
314
|
+
|
|
315
|
+
type EscapeGroupingText<Value extends string> =
|
|
316
|
+
EscapeGroupingGreaterThan<
|
|
317
|
+
EscapeGroupingEquals<
|
|
318
|
+
EscapeGroupingPipes<
|
|
319
|
+
EscapeGroupingCommas<
|
|
320
|
+
EscapeGroupingBackslashes<Value>
|
|
321
|
+
>
|
|
322
|
+
>
|
|
323
|
+
>
|
|
324
|
+
>
|
|
325
|
+
|
|
326
|
+
type JsonStringKeysGroupingKey<Keys extends readonly string[]> = Keys extends readonly []
|
|
327
|
+
? ""
|
|
328
|
+
: Keys extends readonly [infer Head extends string]
|
|
329
|
+
? EscapeGroupingText<Head>
|
|
330
|
+
: Keys extends readonly [infer Head extends string, ...infer Tail extends readonly string[]]
|
|
331
|
+
? `${EscapeGroupingText<Head>},${JsonStringKeysGroupingKey<Tail>}`
|
|
332
|
+
: string
|
|
333
|
+
|
|
284
334
|
type JsonSegmentGroupingKey<Segment> =
|
|
285
|
-
Segment extends JsonPath.KeySegment<infer Key extends string> ? `key:${Key}` :
|
|
335
|
+
Segment extends JsonPath.KeySegment<infer Key extends string> ? `key:${EscapeGroupingText<Key>}` :
|
|
286
336
|
Segment extends JsonPath.IndexSegment<infer Index extends number> ? `index:${Index}` :
|
|
287
337
|
Segment extends JsonPath.WildcardSegment ? "wildcard" :
|
|
288
338
|
Segment extends JsonPath.SliceSegment<infer Start extends number | undefined, infer End extends number | undefined>
|
|
@@ -290,7 +340,7 @@ type JsonSegmentGroupingKey<Segment> =
|
|
|
290
340
|
: Segment extends JsonPath.DescendSegment
|
|
291
341
|
? "descend"
|
|
292
342
|
: Segment extends string
|
|
293
|
-
? `key:${Segment}`
|
|
343
|
+
? `key:${EscapeGroupingText<Segment>}`
|
|
294
344
|
: Segment extends number
|
|
295
345
|
? `index:${Segment}`
|
|
296
346
|
: "unknown"
|
|
@@ -306,13 +356,13 @@ type JsonPathGroupingKey<Segments extends readonly any[]> = Segments extends rea
|
|
|
306
356
|
type JsonOpaquePathGroupingKey<Value> =
|
|
307
357
|
Value extends JsonPath.Path<infer Segments extends readonly JsonPath.CanonicalSegment[]>
|
|
308
358
|
? `jsonpath:${JsonPathGroupingKey<Segments>}` :
|
|
309
|
-
Value extends string ? `jsonpath:${Value}` :
|
|
359
|
+
Value extends string ? `jsonpath:${EscapeGroupingText<Value>}` :
|
|
310
360
|
Value extends Expression.Any ? `jsonpath:${GroupingKeyOfExpression<Value>}` :
|
|
311
361
|
"jsonpath:unknown"
|
|
312
362
|
|
|
313
363
|
type JsonEntryGroupingKey<Entry> =
|
|
314
364
|
Entry extends { readonly key: infer Key extends string; readonly value: infer Value extends Expression.Any }
|
|
315
|
-
? `${Key}=>${GroupingKeyOfExpression<Value>}`
|
|
365
|
+
? `${EscapeGroupingText<Key>}=>${GroupingKeyOfExpression<Value>}`
|
|
316
366
|
: "entry:unknown"
|
|
317
367
|
|
|
318
368
|
type JsonEntriesGroupingKey<Entries extends readonly { readonly key: string; readonly value: Expression.Any }[]> = Entries extends readonly []
|
|
@@ -338,13 +388,19 @@ type BranchGroupingKeys<
|
|
|
338
388
|
|
|
339
389
|
type GroupingKeyOfAst<Ast extends ExpressionAst.Any> =
|
|
340
390
|
Ast extends ExpressionAst.ColumnNode<infer TableName extends string, infer ColumnName extends string>
|
|
341
|
-
? `column:${TableName
|
|
391
|
+
? `column:${ColumnKey<TableName, ColumnName>}`
|
|
342
392
|
: Ast extends ExpressionAst.LiteralNode<infer Value>
|
|
343
393
|
? `literal:${LiteralGroupingKey<Value>}`
|
|
344
394
|
: Ast extends ExpressionAst.ExcludedNode<infer ColumnName extends string>
|
|
345
395
|
? `excluded:${ColumnName}`
|
|
346
396
|
: Ast extends ExpressionAst.CastNode<infer Value extends Expression.Any, infer Target extends Expression.DbType.Any>
|
|
347
397
|
? `cast(${GroupingKeyOfExpression<Value>} as ${Target["dialect"]}:${Target["kind"]})`
|
|
398
|
+
: Ast extends ExpressionAst.CollateNode<infer Value extends Expression.Any, infer Collation extends readonly [string, ...string[]]>
|
|
399
|
+
? `collate(${GroupingKeyOfExpression<Value>},${JsonStringKeysGroupingKey<Collation>})`
|
|
400
|
+
: Ast extends ExpressionAst.FunctionCallNode<infer Name extends string, infer Args extends readonly Expression.Any[]>
|
|
401
|
+
? `function(${EscapeGroupingText<Name>},${JoinGroupingKeys<{
|
|
402
|
+
readonly [K in keyof Args]: Args[K] extends Expression.Any ? GroupingKeyOfExpression<Args[K]> : never
|
|
403
|
+
} & readonly string[]>})`
|
|
348
404
|
: Ast extends ExpressionAst.UnaryNode<infer Kind extends ExpressionAst.UnaryKind, infer Value extends Expression.Any>
|
|
349
405
|
? `${Kind}(${GroupingKeyOfExpression<Value>})`
|
|
350
406
|
: Ast extends ExpressionAst.BinaryNode<infer Kind extends ExpressionAst.BinaryKind, infer Left extends Expression.Any, infer Right extends Expression.Any>
|
|
@@ -357,7 +413,7 @@ type GroupingKeyOfAst<Ast extends ExpressionAst.Any> =
|
|
|
357
413
|
? Kind extends "jsonGet" | "jsonPath" | "jsonAccess" | "jsonTraverse" | "jsonGetText" | "jsonPathText" | "jsonAccessText" | "jsonTraverseText"
|
|
358
414
|
? `json(${Kind},${GroupingKeyOfExpression<Extract<Ast["value"] | Ast["base"] | Ast["left"], Expression.Any>>},${JsonPathGroupingKey<Extract<Ast["segments"] | Ast["path"], readonly any[]>>})`
|
|
359
415
|
: Kind extends "jsonHasKey" | "jsonKeyExists" | "jsonHasAnyKeys" | "jsonHasAllKeys"
|
|
360
|
-
? `json(${Kind},${GroupingKeyOfExpression<Extract<Ast["value"] | Ast["base"] | Ast["left"], Expression.Any>>},${
|
|
416
|
+
? `json(${Kind},${GroupingKeyOfExpression<Extract<Ast["value"] | Ast["base"] | Ast["left"], Expression.Any>>},${JsonStringKeysGroupingKey<Extract<Ast["keys"], readonly string[]> & readonly string[]>})`
|
|
361
417
|
: Kind extends "jsonConcat" | "jsonMerge" | "jsonDelete" | "jsonDeletePath" | "jsonRemove" | "jsonSet" | "jsonInsert"
|
|
362
418
|
? `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
419
|
: Kind extends "jsonPathExists" | "jsonPathMatch"
|
|
@@ -1000,6 +1056,138 @@ type JoinPath<Segments extends readonly string[]> = Segments extends readonly []
|
|
|
1000
1056
|
? `${Head}__${JoinPath<Tail>}`
|
|
1001
1057
|
: string
|
|
1002
1058
|
|
|
1059
|
+
type ProjectionAliasOf<
|
|
1060
|
+
Value extends Expression.Any,
|
|
1061
|
+
Path extends readonly string[]
|
|
1062
|
+
> = Value extends {
|
|
1063
|
+
readonly [ProjectionAlias.TypeId]: ProjectionAlias.State<infer Alias extends string>
|
|
1064
|
+
} ? Alias : JoinPath<Path>
|
|
1065
|
+
|
|
1066
|
+
type SelectionProjectionEntry<
|
|
1067
|
+
Alias extends string,
|
|
1068
|
+
Path extends readonly string[],
|
|
1069
|
+
ExpectedAlias extends string
|
|
1070
|
+
> = {
|
|
1071
|
+
readonly alias: Alias
|
|
1072
|
+
readonly path: Path
|
|
1073
|
+
readonly expectedAlias: ExpectedAlias
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
type SelectionProjectionEntries<
|
|
1077
|
+
Selection,
|
|
1078
|
+
Path extends readonly string[] = []
|
|
1079
|
+
> = Selection extends Expression.Any
|
|
1080
|
+
? SelectionProjectionEntry<ProjectionAliasOf<Selection, Path>, Path, JoinPath<Path>>
|
|
1081
|
+
: Selection extends Record<string, any>
|
|
1082
|
+
? {
|
|
1083
|
+
readonly [K in Extract<keyof Selection, string>]:
|
|
1084
|
+
SelectionProjectionEntries<Selection[K], [...Path, K]>
|
|
1085
|
+
}[Extract<keyof Selection, string>]
|
|
1086
|
+
: never
|
|
1087
|
+
|
|
1088
|
+
type SameProjectionPath<
|
|
1089
|
+
Left extends readonly string[],
|
|
1090
|
+
Right extends readonly string[]
|
|
1091
|
+
> = Left extends readonly []
|
|
1092
|
+
? Right extends readonly [] ? true : false
|
|
1093
|
+
: Left extends readonly [infer LeftHead extends string, ...infer LeftTail extends readonly string[]]
|
|
1094
|
+
? Right extends readonly [infer RightHead extends string, ...infer RightTail extends readonly string[]]
|
|
1095
|
+
? [LeftHead] extends [RightHead]
|
|
1096
|
+
? [RightHead] extends [LeftHead]
|
|
1097
|
+
? SameProjectionPath<LeftTail, RightTail>
|
|
1098
|
+
: false
|
|
1099
|
+
: false
|
|
1100
|
+
: false
|
|
1101
|
+
: false
|
|
1102
|
+
|
|
1103
|
+
type SameProjectionAlias<
|
|
1104
|
+
Left extends string,
|
|
1105
|
+
Right extends string
|
|
1106
|
+
> = [Left] extends [Right] ? [Right] extends [Left] ? true : false : false
|
|
1107
|
+
|
|
1108
|
+
type HasDifferentPathForAlias<
|
|
1109
|
+
Entries,
|
|
1110
|
+
Alias extends string,
|
|
1111
|
+
Path extends readonly string[]
|
|
1112
|
+
> = Entries extends SelectionProjectionEntry<infer EntryAlias, infer EntryPath, any>
|
|
1113
|
+
? SameProjectionAlias<EntryAlias, Alias> extends true
|
|
1114
|
+
? SameProjectionPath<EntryPath, Path> extends true ? never : true
|
|
1115
|
+
: never
|
|
1116
|
+
: never
|
|
1117
|
+
|
|
1118
|
+
type DuplicateProjectionAliases<
|
|
1119
|
+
Entries,
|
|
1120
|
+
AllEntries = Entries
|
|
1121
|
+
> = Entries extends SelectionProjectionEntry<infer Alias, infer Path, any>
|
|
1122
|
+
? string extends Alias ? never
|
|
1123
|
+
: true extends HasDifferentPathForAlias<AllEntries, Alias, Path> ? Alias : never
|
|
1124
|
+
: never
|
|
1125
|
+
|
|
1126
|
+
type ProjectionAliasMismatches<Entries> =
|
|
1127
|
+
Entries extends SelectionProjectionEntry<infer Alias, any, infer ExpectedAlias>
|
|
1128
|
+
? string extends Alias ? never
|
|
1129
|
+
: string extends ExpectedAlias ? never
|
|
1130
|
+
: SameProjectionAlias<Alias, ExpectedAlias> extends true ? never : Alias
|
|
1131
|
+
: never
|
|
1132
|
+
|
|
1133
|
+
type DerivedProjectionDuplicateAliases<Selection> =
|
|
1134
|
+
DuplicateProjectionAliases<SelectionProjectionEntries<Selection>>
|
|
1135
|
+
|
|
1136
|
+
type DerivedProjectionAliasMismatches<Selection> =
|
|
1137
|
+
ProjectionAliasMismatches<SelectionProjectionEntries<Selection>>
|
|
1138
|
+
|
|
1139
|
+
type IsAny<Value> = 0 extends (1 & Value) ? true : false
|
|
1140
|
+
|
|
1141
|
+
type IsBroadSelection<Selection> =
|
|
1142
|
+
IsAny<Selection> extends true ? true :
|
|
1143
|
+
unknown extends Selection ? true : false
|
|
1144
|
+
|
|
1145
|
+
type DerivedProjectionIssues<Selection> =
|
|
1146
|
+
IsBroadSelection<Selection> extends true
|
|
1147
|
+
? never
|
|
1148
|
+
: | DerivedProjectionDuplicateAliases<Selection>
|
|
1149
|
+
| DerivedProjectionAliasMismatches<Selection>
|
|
1150
|
+
|
|
1151
|
+
export type DerivedSourceProjectionCompatibilityError<
|
|
1152
|
+
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>
|
|
1153
|
+
> = PlanValue & {
|
|
1154
|
+
readonly __effect_qb_error__: "effect-qb: derived subqueries require unique path-based projection aliases"
|
|
1155
|
+
readonly __effect_qb_duplicate_projection_aliases__: DerivedProjectionDuplicateAliases<SelectionOfPlan<PlanValue>>
|
|
1156
|
+
readonly __effect_qb_alias_mismatches__: DerivedProjectionAliasMismatches<SelectionOfPlan<PlanValue>>
|
|
1157
|
+
readonly __effect_qb_hint__: "Use unique nested selection paths and do not override projection aliases inside derived subqueries"
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
export type DerivedProjectionCompatiblePlan<
|
|
1161
|
+
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>,
|
|
1162
|
+
ValidPlan = PlanValue
|
|
1163
|
+
> = [DerivedProjectionIssues<SelectionOfPlan<PlanValue>>] extends [never]
|
|
1164
|
+
? ValidPlan
|
|
1165
|
+
: ValidPlan & DerivedSourceProjectionCompatibilityError<PlanValue>
|
|
1166
|
+
|
|
1167
|
+
export type DerivedSourceCompatiblePlan<
|
|
1168
|
+
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>
|
|
1169
|
+
> = DerivedProjectionCompatiblePlan<PlanValue, CompletePlan<PlanValue>>
|
|
1170
|
+
|
|
1171
|
+
type InlineSourceStatementError<
|
|
1172
|
+
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>
|
|
1173
|
+
> = PlanValue & {
|
|
1174
|
+
readonly __effect_qb_error__: "effect-qb: inline derived sources only accept select-like query plans"
|
|
1175
|
+
readonly __effect_qb_statement__: StatementOfPlan<PlanValue>
|
|
1176
|
+
readonly __effect_qb_hint__: "Use select(...) or a set operator for as(...) and lateral(...); use with(...) for data-modifying CTEs where supported"
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
export type DerivedTableCompatiblePlan<
|
|
1180
|
+
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>
|
|
1181
|
+
> = StatementOfPlan<PlanValue> extends SelectLikeStatement
|
|
1182
|
+
? DerivedSourceCompatiblePlan<PlanValue>
|
|
1183
|
+
: InlineSourceStatementError<PlanValue>
|
|
1184
|
+
|
|
1185
|
+
export type LateralSourceCompatiblePlan<
|
|
1186
|
+
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>
|
|
1187
|
+
> = StatementOfPlan<PlanValue> extends SelectLikeStatement
|
|
1188
|
+
? DerivedProjectionCompatiblePlan<PlanValue>
|
|
1189
|
+
: InlineSourceStatementError<PlanValue>
|
|
1190
|
+
|
|
1003
1191
|
type DerivedLeafExpression<
|
|
1004
1192
|
Value extends Expression.Any,
|
|
1005
1193
|
Alias extends string,
|
|
@@ -1085,6 +1273,12 @@ export type AssumptionsOfPlan<
|
|
|
1085
1273
|
export type FactsOfPlan<
|
|
1086
1274
|
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any, any, any>
|
|
1087
1275
|
> = QueryPlanState<PlanValue>["facts"]
|
|
1276
|
+
export type CommonSetFacts<
|
|
1277
|
+
Left extends QueryPlan<any, any, any, any, any, any, any, any, any, any>,
|
|
1278
|
+
Right extends QueryPlan<any, any, any, any, any, any, any, any, any, any>
|
|
1279
|
+
> = [FactsOfPlan<Left>] extends [FactsOfPlan<Right>]
|
|
1280
|
+
? [FactsOfPlan<Right>] extends [FactsOfPlan<Left>] ? FactsOfPlan<Left> : EmptyFacts
|
|
1281
|
+
: EmptyFacts
|
|
1088
1282
|
export type PredicateStateOfPlan<
|
|
1089
1283
|
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any, any, any>
|
|
1090
1284
|
> = PredicateState<AssumptionsOfPlan<PlanValue>, FactsOfPlan<PlanValue>>
|
|
@@ -1217,9 +1411,10 @@ export type AddJoinRequired<
|
|
|
1217
1411
|
Available extends Record<string, RowSet.AnySource>,
|
|
1218
1412
|
JoinedName extends string,
|
|
1219
1413
|
Predicate extends PredicateInput | never,
|
|
1220
|
-
Kind extends QueryAst.JoinKind = "inner"
|
|
1414
|
+
Kind extends QueryAst.JoinKind = "inner",
|
|
1415
|
+
SourceRequired extends string = never
|
|
1221
1416
|
> = Exclude<
|
|
1222
|
-
Required | (Predicate extends never ? never : RequiredFromInput<Predicate>),
|
|
1417
|
+
Required | SourceRequired | (Predicate extends never ? never : RequiredFromInput<Predicate>),
|
|
1223
1418
|
AvailableNames<AvailableAfterJoin<Available, JoinedName, Kind>>
|
|
1224
1419
|
>
|
|
1225
1420
|
|
|
@@ -1744,30 +1939,45 @@ type JsonLiteralSetsForColumn<
|
|
|
1744
1939
|
? Paths
|
|
1745
1940
|
: {}
|
|
1746
1941
|
|
|
1942
|
+
type JsonPathHead<
|
|
1943
|
+
Path extends string,
|
|
1944
|
+
Current extends string = ""
|
|
1945
|
+
> = Path extends `\\.${infer Rest}`
|
|
1946
|
+
? JsonPathHead<Rest, `${Current}.`>
|
|
1947
|
+
: Path extends `\\\\${infer Rest}`
|
|
1948
|
+
? JsonPathHead<Rest, `${Current}\\`>
|
|
1949
|
+
: Path extends `.${infer Tail}`
|
|
1950
|
+
? readonly [Current, Tail]
|
|
1951
|
+
: Path extends `${infer Character}${infer Rest}`
|
|
1952
|
+
? JsonPathHead<Rest, `${Current}${Character}`>
|
|
1953
|
+
: readonly [Current, ""]
|
|
1954
|
+
|
|
1747
1955
|
type RefineJsonRuntimeAtPath<
|
|
1748
1956
|
Runtime,
|
|
1749
1957
|
Path extends string,
|
|
1750
1958
|
Values
|
|
1751
1959
|
> = Runtime extends unknown
|
|
1752
|
-
? Path extends
|
|
1753
|
-
?
|
|
1754
|
-
?
|
|
1755
|
-
?
|
|
1756
|
-
? [
|
|
1757
|
-
? never
|
|
1758
|
-
|
|
1960
|
+
? JsonPathHead<Path> extends readonly [infer Head extends string, infer Tail extends string]
|
|
1961
|
+
? Tail extends ""
|
|
1962
|
+
? Runtime extends object
|
|
1963
|
+
? Head extends keyof Runtime
|
|
1964
|
+
? RefineRuntimeByAllowedLiterals<NonNullable<Runtime[Head]>, Values> extends infer Refined
|
|
1965
|
+
? [Refined] extends [never]
|
|
1966
|
+
? never
|
|
1967
|
+
: Omit<Runtime, Head> & { readonly [K in Head]: Refined }
|
|
1968
|
+
: never
|
|
1759
1969
|
: never
|
|
1760
|
-
:
|
|
1761
|
-
:
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
:
|
|
1970
|
+
: RefineRuntimeByAllowedLiterals<NonNullable<Runtime>, Values>
|
|
1971
|
+
: Runtime extends object
|
|
1972
|
+
? Head extends keyof Runtime
|
|
1973
|
+
? RefineJsonRuntimeAtPath<NonNullable<Runtime[Head]>, Tail, Values> extends infer Refined
|
|
1974
|
+
? [Refined] extends [never]
|
|
1975
|
+
? never
|
|
1976
|
+
: Omit<Runtime, Head> & { readonly [K in Head]: Refined }
|
|
1977
|
+
: never
|
|
1768
1978
|
: never
|
|
1769
1979
|
: never
|
|
1770
|
-
|
|
1980
|
+
: never
|
|
1771
1981
|
: never
|
|
1772
1982
|
|
|
1773
1983
|
type RefineJsonRuntimeWithConstraints<
|
|
@@ -1965,9 +2175,9 @@ type IsDialectCompatible<
|
|
|
1965
2175
|
EngineDialect extends string
|
|
1966
2176
|
> = [PlanDialect] extends [never]
|
|
1967
2177
|
? true
|
|
1968
|
-
:
|
|
1969
|
-
?
|
|
1970
|
-
:
|
|
2178
|
+
: Exclude<PlanDialect, EngineDialect> extends never
|
|
2179
|
+
? true
|
|
2180
|
+
: false
|
|
1971
2181
|
|
|
1972
2182
|
/** Narrows a complete plan to those compatible with a target engine dialect. */
|
|
1973
2183
|
export type DialectCompatiblePlan<
|
|
@@ -1977,15 +2187,27 @@ export type DialectCompatiblePlan<
|
|
|
1977
2187
|
? CompletePlan<PlanValue>
|
|
1978
2188
|
: DialectCompatibilityError<PlanValue, EngineDialect>
|
|
1979
2189
|
|
|
2190
|
+
type SelectLikeStatement = "select" | "set"
|
|
2191
|
+
|
|
2192
|
+
type NestedPlanStatementError<
|
|
2193
|
+
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>
|
|
2194
|
+
> = PlanValue & {
|
|
2195
|
+
readonly __effect_qb_error__: "effect-qb: subquery expressions only accept select-like query plans"
|
|
2196
|
+
readonly __effect_qb_statement__: StatementOfPlan<PlanValue>
|
|
2197
|
+
readonly __effect_qb_hint__: "Use select(...) or a set operator as the nested subquery expression"
|
|
2198
|
+
}
|
|
2199
|
+
|
|
1980
2200
|
/** Nested-plan compatibility used by subquery expressions such as `exists(...)`. */
|
|
1981
2201
|
export type DialectCompatibleNestedPlan<
|
|
1982
2202
|
PlanValue extends QueryPlan<any, any, any, any, any, any, any, any, any, any>,
|
|
1983
2203
|
EngineDialect extends string
|
|
1984
2204
|
> = IsDialectCompatible<PlanValue[typeof RowSet.TypeId]["dialect"], EngineDialect> extends true
|
|
1985
|
-
?
|
|
2205
|
+
? StatementOfPlan<PlanValue> extends SelectLikeStatement
|
|
2206
|
+
? AggregationCompatiblePlan<PlanValue>
|
|
2207
|
+
: NestedPlanStatementError<PlanValue>
|
|
1986
2208
|
: DialectCompatibilityError<PlanValue, EngineDialect>
|
|
1987
2209
|
|
|
1988
|
-
type SetOperandStatement =
|
|
2210
|
+
type SetOperandStatement = SelectLikeStatement
|
|
1989
2211
|
type IsUnion<Value, All = Value> = Value extends any ? ([All] extends [Value] ? false : true) : never
|
|
1990
2212
|
|
|
1991
2213
|
type SingleSelectedExpressionError<
|
package/src/internal/renderer.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as Query from "./query.js"
|
|
2
2
|
import type * as Expression from "./scalar.js"
|
|
3
|
-
import { type Projection, validateProjections } from "./projections.js"
|
|
3
|
+
import { flattenSelection, type Projection, validateProjections } from "./projections.js"
|
|
4
|
+
import * as Plan from "./row-set.js"
|
|
4
5
|
|
|
5
6
|
/** Symbol used to attach rendered-query phantom row metadata. */
|
|
6
7
|
export const TypeId: unique symbol = Symbol.for("effect-qb/Renderer")
|
|
@@ -44,7 +45,7 @@ export interface Renderer<Dialect extends string = string> {
|
|
|
44
45
|
readonly dialect: Dialect
|
|
45
46
|
render<PlanValue extends Query.Plan.Any>(
|
|
46
47
|
plan: Query.DialectCompatiblePlan<PlanValue, Dialect>
|
|
47
|
-
): RenderedQuery<
|
|
48
|
+
): RenderedQuery<Query.ResultRow<PlanValue>, Dialect>
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
type CustomRender<Dialect extends string> = <PlanValue extends Query.Plan.Any>(
|
|
@@ -56,6 +57,29 @@ type CustomRender<Dialect extends string> = <PlanValue extends Query.Plan.Any>(
|
|
|
56
57
|
readonly valueMappings?: Expression.DriverValueMappings
|
|
57
58
|
}
|
|
58
59
|
|
|
60
|
+
const projectionPathKey = (path: readonly string[]): string => JSON.stringify(path)
|
|
61
|
+
|
|
62
|
+
const formatProjectionPath = (path: readonly string[]): string => path.join(".")
|
|
63
|
+
|
|
64
|
+
const validateProjectionPathsMatchSelection = (
|
|
65
|
+
plan: Query.Plan.Any,
|
|
66
|
+
projections: readonly Projection[]
|
|
67
|
+
): void => {
|
|
68
|
+
const expected = flattenSelection(Query.getAst(plan).select as Record<string, unknown>)
|
|
69
|
+
const expectedPaths = new Set(expected.map((projection) => projectionPathKey(projection.path)))
|
|
70
|
+
const actualPaths = new Set(projections.map((projection) => projectionPathKey(projection.path)))
|
|
71
|
+
for (const projection of projections) {
|
|
72
|
+
if (!expectedPaths.has(projectionPathKey(projection.path))) {
|
|
73
|
+
throw new Error(`Projection path ${formatProjectionPath(projection.path)} does not exist in the query selection`)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
for (const projection of expected) {
|
|
77
|
+
if (!actualPaths.has(projectionPathKey(projection.path))) {
|
|
78
|
+
throw new Error(`Projection path ${formatProjectionPath(projection.path)} is missing from rendered projections`)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
59
83
|
/**
|
|
60
84
|
* Constructs a renderer from a dialect and implementation callback.
|
|
61
85
|
*/
|
|
@@ -73,9 +97,18 @@ export function make<Dialect extends string>(
|
|
|
73
97
|
return {
|
|
74
98
|
dialect,
|
|
75
99
|
render(plan) {
|
|
100
|
+
const required = Query.currentRequiredList(plan[Plan.TypeId].required)
|
|
101
|
+
if (required.length > 0) {
|
|
102
|
+
throw new Error(`query references sources that are not yet in scope: ${required.join(", ")}`)
|
|
103
|
+
}
|
|
104
|
+
const planDialect = plan[Plan.TypeId].dialect
|
|
105
|
+
if (planDialect !== dialect) {
|
|
106
|
+
throw new Error("effect-qb: plan dialect is not compatible with the target renderer or executor")
|
|
107
|
+
}
|
|
76
108
|
const rendered = render(plan)
|
|
77
109
|
const projections = rendered.projections ?? []
|
|
78
110
|
validateProjections(projections)
|
|
111
|
+
validateProjectionPathsMatchSelection(plan as Query.Plan.Any, projections)
|
|
79
112
|
return {
|
|
80
113
|
sql: rendered.sql,
|
|
81
114
|
params: rendered.params ?? [],
|
|
@@ -84,6 +84,26 @@ const findMapping = <Key extends MappingKey>(
|
|
|
84
84
|
return undefined
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
const isJsonDbType = (dbType: Expression.DbType.Any | undefined): boolean => {
|
|
88
|
+
if (dbType === undefined) {
|
|
89
|
+
return false
|
|
90
|
+
}
|
|
91
|
+
if ("base" in dbType) {
|
|
92
|
+
return isJsonDbType(dbType.base)
|
|
93
|
+
}
|
|
94
|
+
if (!("variant" in dbType)) {
|
|
95
|
+
return false
|
|
96
|
+
}
|
|
97
|
+
const variant = dbType.variant as string
|
|
98
|
+
return variant === "json" || variant === "jsonb"
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const schemaAccepts = (
|
|
102
|
+
schema: Schema.Schema.Any | undefined,
|
|
103
|
+
value: unknown
|
|
104
|
+
): boolean =>
|
|
105
|
+
schema !== undefined && (Schema.is(schema) as (candidate: unknown) => boolean)(value)
|
|
106
|
+
|
|
87
107
|
const encodeWithSchema = (
|
|
88
108
|
schema: Schema.Schema.Any | undefined,
|
|
89
109
|
value: unknown
|
|
@@ -100,6 +120,32 @@ const encodeWithSchema = (
|
|
|
100
120
|
}
|
|
101
121
|
}
|
|
102
122
|
|
|
123
|
+
const normalizeJsonDriverString = (
|
|
124
|
+
value: string,
|
|
125
|
+
context: DriverValueContext
|
|
126
|
+
): unknown | undefined => {
|
|
127
|
+
if (!isJsonDbType(context.dbType) || context.runtimeSchema === undefined) {
|
|
128
|
+
return undefined
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
const parsed = JSON.parse(value)
|
|
132
|
+
if (value.trimStart().startsWith("\"") && schemaAccepts(context.runtimeSchema, parsed)) {
|
|
133
|
+
return parsed
|
|
134
|
+
}
|
|
135
|
+
if (schemaAccepts(context.runtimeSchema, value) && !schemaAccepts(context.runtimeSchema, parsed)) {
|
|
136
|
+
return value
|
|
137
|
+
}
|
|
138
|
+
} catch (error) {
|
|
139
|
+
if (error instanceof SyntaxError && schemaAccepts(context.runtimeSchema, value)) {
|
|
140
|
+
return value
|
|
141
|
+
}
|
|
142
|
+
if (!(error instanceof SyntaxError)) {
|
|
143
|
+
throw error
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return undefined
|
|
147
|
+
}
|
|
148
|
+
|
|
103
149
|
export const toDriverValue = (
|
|
104
150
|
value: unknown,
|
|
105
151
|
context: DriverValueContext
|
|
@@ -107,6 +153,9 @@ export const toDriverValue = (
|
|
|
107
153
|
if (value === null) {
|
|
108
154
|
return null
|
|
109
155
|
}
|
|
156
|
+
if (value instanceof Date && Number.isNaN(value.getTime())) {
|
|
157
|
+
throw new Error("Expected a valid Date value")
|
|
158
|
+
}
|
|
110
159
|
const dbType = context.dbType
|
|
111
160
|
const encoded = encodeWithSchema(context.runtimeSchema, value)
|
|
112
161
|
let current = encoded.value
|
|
@@ -114,6 +163,9 @@ export const toDriverValue = (
|
|
|
114
163
|
if (custom !== undefined && dbType !== undefined) {
|
|
115
164
|
return custom(current, dbType)
|
|
116
165
|
}
|
|
166
|
+
if (encoded.encoded && typeof current === "string" && isJsonDbType(dbType)) {
|
|
167
|
+
return current
|
|
168
|
+
}
|
|
117
169
|
return dbType === undefined || !encoded.encoded
|
|
118
170
|
? current
|
|
119
171
|
: normalizeDbValue(dbType, current)
|
|
@@ -131,6 +183,12 @@ export const fromDriverValue = (
|
|
|
131
183
|
if (custom !== undefined && dbType !== undefined) {
|
|
132
184
|
return custom(value, dbType)
|
|
133
185
|
}
|
|
186
|
+
if (typeof value === "string") {
|
|
187
|
+
const normalizedJsonString = normalizeJsonDriverString(value, context)
|
|
188
|
+
if (normalizedJsonString !== undefined) {
|
|
189
|
+
return normalizedJsonString
|
|
190
|
+
}
|
|
191
|
+
}
|
|
134
192
|
return dbType === undefined
|
|
135
193
|
? value
|
|
136
194
|
: normalizeDbValue(dbType, value)
|