effect-qb 0.21.0 → 0.22.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/index.js +663 -261
- package/dist/mysql.js +2840 -492
- package/dist/postgres/metadata.js +113 -14
- package/dist/postgres.js +2596 -285
- package/dist/sqlite.js +2783 -450
- package/dist/standard.js +663 -261
- package/package.json +1 -1
- package/src/internal/analytics.ts +264 -0
- package/src/internal/coercion/errors.d.ts +1 -1
- package/src/internal/coercion/errors.ts +1 -1
- package/src/internal/custom-sql-renderer.ts +20 -0
- package/src/internal/dialect-numeric.ts +332 -0
- package/src/internal/dialect-renderers/mysql.ts +68 -13
- package/src/internal/dialect-renderers/postgres.ts +83 -13
- package/src/internal/dialect-renderers/sqlite.ts +57 -13
- package/src/internal/dynamic.ts +131 -0
- package/src/internal/executor.ts +198 -7
- package/src/internal/expression-ast.ts +62 -1
- package/src/internal/fragment.ts +120 -0
- package/src/internal/function-constraints.ts +105 -0
- package/src/internal/grouping-key.ts +26 -0
- package/src/internal/numeric.ts +204 -0
- package/src/internal/query.ts +13 -1
- package/src/internal/runtime/normalize.ts +5 -2
- package/src/internal/runtime/schema.ts +2 -1
- package/src/internal/standard-dsl.ts +43 -20
- package/src/internal/window-frame.ts +30 -0
- package/src/internal/window-renderer.ts +25 -0
- package/src/mysql/executor.ts +126 -45
- package/src/mysql/function/aggregate.ts +87 -1
- package/src/mysql/function/index.ts +5 -2
- package/src/mysql/function/numeric.ts +185 -0
- package/src/mysql/function/temporal.ts +6 -6
- package/src/mysql/function/window.ts +12 -0
- package/src/mysql/internal/dsl.ts +38 -20
- package/src/mysql.ts +2 -0
- package/src/postgres/executor.ts +111 -45
- package/src/postgres/function/aggregate.ts +124 -1
- package/src/postgres/function/core.ts +2 -1
- package/src/postgres/function/index.ts +19 -1
- package/src/postgres/function/numeric.ts +246 -0
- package/src/postgres/function/window.ts +12 -0
- package/src/postgres/internal/dsl.ts +38 -20
- package/src/sqlite/executor.ts +96 -45
- package/src/sqlite/function/aggregate.ts +90 -1
- package/src/sqlite/function/index.ts +5 -2
- package/src/sqlite/function/numeric.ts +139 -0
- package/src/sqlite/function/window.ts +12 -0
- package/src/sqlite/internal/dsl.ts +38 -20
- package/src/sqlite.ts +2 -0
- package/src/standard/function/core.ts +8 -1
- package/src/standard/function/index.ts +18 -11
- package/src/standard/function/string.ts +1 -1
- package/src/standard/function/window.ts +10 -1
- package/src/standard/query.ts +19 -7
- package/src/standard/type.ts +16 -0
- package/src/standard.ts +4 -0
- package/src/standard/function/temporal.ts +0 -78
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import * as Numeric from "../../internal/dialect-numeric.js"
|
|
2
|
+
import * as Expression from "../../internal/scalar.js"
|
|
3
|
+
import { postgresDatatypes, postgresDatatypeFamilies } from "../datatypes/index.js"
|
|
4
|
+
|
|
5
|
+
type PgNumericDb<
|
|
6
|
+
Kind extends "int2" | "int4" | "int8" | "numeric" | "float8",
|
|
7
|
+
Runtime extends "number" | "bigintString" | "decimalString"
|
|
8
|
+
> = Expression.DbType.Base<"postgres", Kind> & {
|
|
9
|
+
readonly family: "numeric"
|
|
10
|
+
readonly runtime: Runtime
|
|
11
|
+
readonly compareGroup: "numeric"
|
|
12
|
+
readonly castTargets: typeof postgresDatatypeFamilies.numeric.castTargets
|
|
13
|
+
readonly implicitTargets: readonly []
|
|
14
|
+
readonly traits: { readonly ordered: true }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
type PgInt2 = PgNumericDb<"int2", "number">
|
|
18
|
+
type PgInt4 = PgNumericDb<"int4", "number">
|
|
19
|
+
type PgInt8 = PgNumericDb<"int8", "bigintString">
|
|
20
|
+
type PgNumeric = PgNumericDb<"numeric", "decimalString">
|
|
21
|
+
type PgFloat8 = PgNumericDb<"float8", "number">
|
|
22
|
+
|
|
23
|
+
type IsAny<Value> = 0 extends (1 & Value) ? true : false
|
|
24
|
+
|
|
25
|
+
type BaseDb<Db extends Expression.DbType.Any> =
|
|
26
|
+
Db extends Expression.DbType.Domain<any, infer Base extends Expression.DbType.Any, any>
|
|
27
|
+
? BaseDb<Base>
|
|
28
|
+
: Db
|
|
29
|
+
|
|
30
|
+
type PgModuloCategory<Db extends Expression.DbType.Any> =
|
|
31
|
+
BaseDb<Db> extends infer Base extends Expression.DbType.Any
|
|
32
|
+
? Base["dialect"] extends "standard"
|
|
33
|
+
? Base["kind"] extends "int" | "integer" ? "int4"
|
|
34
|
+
: Base["kind"] extends "bigint" ? "int8"
|
|
35
|
+
: Base["kind"] extends "numeric" | "decimal" ? "numeric"
|
|
36
|
+
: never
|
|
37
|
+
: Base["dialect"] extends "postgres"
|
|
38
|
+
? Base["kind"] extends "int2" | "int4" | "int8" | "numeric"
|
|
39
|
+
? Base["kind"]
|
|
40
|
+
: never
|
|
41
|
+
: never
|
|
42
|
+
: never
|
|
43
|
+
|
|
44
|
+
type PgRoundCategory<Db extends Expression.DbType.Any> =
|
|
45
|
+
BaseDb<Db> extends infer Base extends Expression.DbType.Any
|
|
46
|
+
? Base["dialect"] extends "standard"
|
|
47
|
+
? Base["kind"] extends "numeric" | "decimal" ? "exact"
|
|
48
|
+
: Base["kind"] extends "int" | "integer" | "bigint" ? "integer"
|
|
49
|
+
: Base["kind"] extends "real" ? "approximate"
|
|
50
|
+
: never
|
|
51
|
+
: Base["dialect"] extends "postgres"
|
|
52
|
+
? Base["kind"] extends "numeric" ? "exact"
|
|
53
|
+
: Base["kind"] extends "int2" | "int4" | "int8" ? "integer"
|
|
54
|
+
: Base["kind"] extends "float4" | "float8" ? "approximate"
|
|
55
|
+
: never
|
|
56
|
+
: never
|
|
57
|
+
: never
|
|
58
|
+
|
|
59
|
+
type NumericInputError<
|
|
60
|
+
Operation extends "modulo" | "round",
|
|
61
|
+
Db extends Expression.DbType.Any,
|
|
62
|
+
Expected extends string
|
|
63
|
+
> = {
|
|
64
|
+
readonly __effect_qb_error__: "effect-qb: unsupported postgres numeric input"
|
|
65
|
+
readonly __effect_qb_operation__: Operation
|
|
66
|
+
readonly __effect_qb_db_type__: Db
|
|
67
|
+
readonly __effect_qb_expected__: Expected
|
|
68
|
+
readonly __effect_qb_hint__: "Use Cast.to(...) with a supported Type or Pg.Type witness"
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
type IntegerLiteralError<Value extends number> = {
|
|
72
|
+
readonly __effect_qb_error__: "effect-qb: postgres modulo number literals must be integers"
|
|
73
|
+
readonly __effect_qb_value__: Value
|
|
74
|
+
readonly __effect_qb_hint__: "Use Cast.to(value, Type.numeric()) for exact fractional modulo"
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
type PgModuloConstraint<Value extends Numeric.Input> =
|
|
78
|
+
IsAny<Value> extends true ? unknown
|
|
79
|
+
: Numeric.DialectConstraint<Value, PgInt4, "postgres"> & (
|
|
80
|
+
Value extends number
|
|
81
|
+
? number extends Value
|
|
82
|
+
? IntegerLiteralError<Value>
|
|
83
|
+
: `${Value}` extends `${bigint}` ? unknown : IntegerLiteralError<Value>
|
|
84
|
+
: PgModuloCategory<Numeric.DbTypeOfInput<Value, PgInt4, "postgres">> extends never
|
|
85
|
+
? NumericInputError<
|
|
86
|
+
"modulo",
|
|
87
|
+
Numeric.DbTypeOfInput<Value, PgInt4, "postgres">,
|
|
88
|
+
"smallint, integer, bigint, or numeric"
|
|
89
|
+
>
|
|
90
|
+
: unknown
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
type PgRoundConstraint<Value extends Numeric.Input> =
|
|
94
|
+
IsAny<Value> extends true ? unknown
|
|
95
|
+
: Numeric.DialectConstraint<Value, PgFloat8, "postgres"> & (
|
|
96
|
+
PgRoundCategory<Numeric.DbTypeOfInput<Value, PgFloat8, "postgres">> extends never
|
|
97
|
+
? NumericInputError<
|
|
98
|
+
"round",
|
|
99
|
+
Numeric.DbTypeOfInput<Value, PgFloat8, "postgres">,
|
|
100
|
+
"smallint, integer, bigint, numeric, real, or double precision"
|
|
101
|
+
>
|
|
102
|
+
: unknown
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
type PgScaledRoundConstraint<Value extends Numeric.Input> =
|
|
106
|
+
PgRoundConstraint<Value> &
|
|
107
|
+
(PgRoundCategory<Numeric.DbTypeOfInput<Value, PgFloat8, "postgres">> extends "approximate"
|
|
108
|
+
? NumericInputError<
|
|
109
|
+
"round",
|
|
110
|
+
Numeric.DbTypeOfInput<Value, PgFloat8, "postgres">,
|
|
111
|
+
"numeric or integer when a scale is provided"
|
|
112
|
+
>
|
|
113
|
+
: unknown)
|
|
114
|
+
|
|
115
|
+
type PgModuloResultCategory<
|
|
116
|
+
Left extends Numeric.Input,
|
|
117
|
+
Right extends Numeric.Input
|
|
118
|
+
> =
|
|
119
|
+
PgModuloCategory<Numeric.DbTypeOfInput<Left, PgInt4, "postgres">> extends infer LeftCategory
|
|
120
|
+
? PgModuloCategory<Numeric.DbTypeOfInput<Right, PgInt4, "postgres">> extends infer RightCategory
|
|
121
|
+
? "numeric" extends LeftCategory | RightCategory ? "numeric"
|
|
122
|
+
: "int8" extends LeftCategory | RightCategory ? "int8"
|
|
123
|
+
: "int4" extends LeftCategory | RightCategory ? "int4"
|
|
124
|
+
: "int2"
|
|
125
|
+
: never
|
|
126
|
+
: never
|
|
127
|
+
|
|
128
|
+
type PgModuloResultDb<
|
|
129
|
+
Left extends Numeric.Input,
|
|
130
|
+
Right extends Numeric.Input
|
|
131
|
+
> = PgModuloResultCategory<Left, Right> extends "numeric" ? PgNumeric
|
|
132
|
+
: PgModuloResultCategory<Left, Right> extends "int8" ? PgInt8
|
|
133
|
+
: PgModuloResultCategory<Left, Right> extends "int4" ? PgInt4
|
|
134
|
+
: PgInt2
|
|
135
|
+
|
|
136
|
+
type PgRoundResultDb<Value extends Numeric.Input> =
|
|
137
|
+
PgRoundCategory<Numeric.DbTypeOfInput<Value, PgFloat8, "postgres">> extends "exact"
|
|
138
|
+
? PgNumeric
|
|
139
|
+
: PgFloat8
|
|
140
|
+
|
|
141
|
+
type PgModuloResult<
|
|
142
|
+
Left extends Numeric.Input,
|
|
143
|
+
Right extends Numeric.Input
|
|
144
|
+
> = Numeric.BinaryResult<
|
|
145
|
+
Left,
|
|
146
|
+
Right,
|
|
147
|
+
PgInt4,
|
|
148
|
+
PgModuloResultDb<Left, Right>,
|
|
149
|
+
Numeric.BinaryNullability<Left, Right, PgInt4, "postgres">,
|
|
150
|
+
"postgres"
|
|
151
|
+
>
|
|
152
|
+
|
|
153
|
+
type PgRoundResult<
|
|
154
|
+
Value extends Numeric.Input,
|
|
155
|
+
ResultDb extends Expression.DbType.Any,
|
|
156
|
+
WithScale extends boolean
|
|
157
|
+
> = Numeric.RoundResult<
|
|
158
|
+
Value,
|
|
159
|
+
PgFloat8,
|
|
160
|
+
ResultDb,
|
|
161
|
+
Value extends Expression.Any ? Expression.NullabilityOf<Value> : "never",
|
|
162
|
+
"postgres",
|
|
163
|
+
WithScale
|
|
164
|
+
>
|
|
165
|
+
|
|
166
|
+
const baseDb = (db: Expression.DbType.Any): Expression.DbType.Any =>
|
|
167
|
+
"base" in db ? baseDb(db.base) : db
|
|
168
|
+
|
|
169
|
+
const moduloCategory = (value: Numeric.Input): "int2" | "int4" | "int8" | "numeric" => {
|
|
170
|
+
if (typeof value === "number") {
|
|
171
|
+
if (!Number.isSafeInteger(value) || value < -2_147_483_648 || value > 2_147_483_647) {
|
|
172
|
+
throw new Error("postgres modulo number literals must fit a signed int4")
|
|
173
|
+
}
|
|
174
|
+
return "int4"
|
|
175
|
+
}
|
|
176
|
+
const db = baseDb(value[Expression.TypeId].dbType)
|
|
177
|
+
if (db.dialect === "standard") {
|
|
178
|
+
if (db.kind === "bigint") return "int8"
|
|
179
|
+
if (db.kind === "numeric" || db.kind === "decimal") return "numeric"
|
|
180
|
+
return "int4"
|
|
181
|
+
}
|
|
182
|
+
return db.kind as "int2" | "int4" | "int8" | "numeric"
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const moduloResultDb = (left: Numeric.Input, right: Numeric.Input) => {
|
|
186
|
+
const categories = [moduloCategory(left), moduloCategory(right)]
|
|
187
|
+
if (categories.includes("numeric")) return postgresDatatypes.numeric()
|
|
188
|
+
if (categories.includes("int8")) return postgresDatatypes.int8()
|
|
189
|
+
if (categories.includes("int4")) return postgresDatatypes.int4()
|
|
190
|
+
return postgresDatatypes.int2()
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const roundCategory = (value: Numeric.Input): "exact" | "integer" | "approximate" => {
|
|
194
|
+
if (typeof value === "number") return "approximate"
|
|
195
|
+
const db = baseDb(value[Expression.TypeId].dbType)
|
|
196
|
+
if (db.kind === "numeric" || db.kind === "decimal") return "exact"
|
|
197
|
+
if (db.kind === "int" || db.kind === "integer" || db.kind === "bigint" ||
|
|
198
|
+
db.kind === "int2" || db.kind === "int4" || db.kind === "int8") {
|
|
199
|
+
return "integer"
|
|
200
|
+
}
|
|
201
|
+
return "approximate"
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export const modulo = <
|
|
205
|
+
Left extends Numeric.Input,
|
|
206
|
+
Right extends Numeric.Input
|
|
207
|
+
>(
|
|
208
|
+
left: Left & PgModuloConstraint<NoInfer<Left>>,
|
|
209
|
+
right: Right & PgModuloConstraint<NoInfer<Right>>
|
|
210
|
+
): PgModuloResult<Left, Right> =>
|
|
211
|
+
(Numeric.modulo as any)(left, right, {
|
|
212
|
+
dialect: "postgres",
|
|
213
|
+
literalDb: postgresDatatypes.int4(),
|
|
214
|
+
resultDb: moduloResultDb(left, right) as PgModuloResultDb<Left, Right>,
|
|
215
|
+
nullability: Numeric.binaryInputNullability(left, right) as Numeric.BinaryNullability<Left, Right, PgInt4, "postgres">
|
|
216
|
+
}) as PgModuloResult<Left, Right>
|
|
217
|
+
|
|
218
|
+
const roundRuntime = (
|
|
219
|
+
value: Numeric.Input,
|
|
220
|
+
scale?: number
|
|
221
|
+
): Expression.Any => {
|
|
222
|
+
const category = roundCategory(value)
|
|
223
|
+
if (scale !== undefined && category === "approximate") {
|
|
224
|
+
throw new Error("postgres round with a scale requires a numeric or integer input")
|
|
225
|
+
}
|
|
226
|
+
const resultDb = scale !== undefined || category === "exact"
|
|
227
|
+
? postgresDatatypes.numeric()
|
|
228
|
+
: postgresDatatypes.float8()
|
|
229
|
+
return Numeric.round(value, scale, {
|
|
230
|
+
dialect: "postgres",
|
|
231
|
+
literalDb: postgresDatatypes.float8(),
|
|
232
|
+
scaleDb: postgresDatatypes.int4(),
|
|
233
|
+
resultDb,
|
|
234
|
+
nullability: typeof value === "number" ? "never" : value[Expression.TypeId].nullability
|
|
235
|
+
}) as Expression.Any
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export const round: {
|
|
239
|
+
<Value extends Numeric.Input>(
|
|
240
|
+
value: Value & PgRoundConstraint<NoInfer<Value>>
|
|
241
|
+
): PgRoundResult<Value, PgRoundResultDb<Value>, false>
|
|
242
|
+
<Value extends Numeric.Input>(
|
|
243
|
+
value: Value & PgScaledRoundConstraint<NoInfer<Value>>,
|
|
244
|
+
scale: number
|
|
245
|
+
): PgRoundResult<Value, PgNumeric, true>
|
|
246
|
+
} = roundRuntime as any
|
|
@@ -1,2 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
makeDialectFirstValue,
|
|
3
|
+
makeDialectLastValue
|
|
4
|
+
} from "../../internal/analytics.js"
|
|
5
|
+
export type { WindowSpec } from "../../internal/analytics.js"
|
|
6
|
+
|
|
1
7
|
/** Postgres window functions. */
|
|
2
8
|
export { over, rowNumber, rank, denseRank } from "../internal/dsl.js"
|
|
9
|
+
|
|
10
|
+
/** First value in a PostgreSQL window frame. */
|
|
11
|
+
export const firstValue = makeDialectFirstValue("postgres")
|
|
12
|
+
|
|
13
|
+
/** Last value in a PostgreSQL window frame. */
|
|
14
|
+
export const lastValue = makeDialectLastValue("postgres")
|
|
@@ -4,6 +4,7 @@ import * as Schema from "effect/Schema"
|
|
|
4
4
|
import { postgresDatatypes } from "../datatypes/index.js"
|
|
5
5
|
|
|
6
6
|
import * as Expression from "../../internal/scalar.js"
|
|
7
|
+
import type * as FunctionConstraint from "../../internal/function-constraints.js"
|
|
7
8
|
import * as Plan from "../../internal/row-set.js"
|
|
8
9
|
import * as Table from "../../internal/table.js"
|
|
9
10
|
import type {
|
|
@@ -135,6 +136,7 @@ import type { FormulaOfPredicate } from "../../internal/predicate/normalize.js"
|
|
|
135
136
|
import type { TrueFormula } from "../../internal/predicate/formula.js"
|
|
136
137
|
import { assumeFormulaTrue, formulaOfExpression as formulaOfExpressionRuntime, trueFormula } from "../../internal/predicate/runtime.js"
|
|
137
138
|
import { dedupeGroupedExpressions } from "../../internal/grouping-key.js"
|
|
139
|
+
import { validateWindowFrame } from "../../internal/window-frame.js"
|
|
138
140
|
import { makeDslMutationRuntime } from "../../internal/dsl-mutation-runtime.js"
|
|
139
141
|
import { makeDslPlanRuntime } from "../../internal/dsl-plan-runtime.js"
|
|
140
142
|
import { makeDslQueryRuntime } from "../../internal/dsl-query-runtime.js"
|
|
@@ -1421,6 +1423,7 @@ type WindowSpecInput<
|
|
|
1421
1423
|
> = {
|
|
1422
1424
|
readonly partitionBy?: PartitionBy
|
|
1423
1425
|
readonly orderBy?: OrderBy
|
|
1426
|
+
readonly frame?: ExpressionAst.WindowFrameNode
|
|
1424
1427
|
}
|
|
1425
1428
|
|
|
1426
1429
|
type OrderedWindowSpecInput<
|
|
@@ -1429,6 +1432,7 @@ type OrderedWindowSpecInput<
|
|
|
1429
1432
|
> = {
|
|
1430
1433
|
readonly partitionBy?: PartitionBy
|
|
1431
1434
|
readonly orderBy: OrderBy
|
|
1435
|
+
readonly frame?: never
|
|
1432
1436
|
}
|
|
1433
1437
|
|
|
1434
1438
|
type WindowOrderExpressionTuple<
|
|
@@ -1501,8 +1505,8 @@ type NumberWindowExpression<
|
|
|
1501
1505
|
PartitionBy extends readonly WindowPartitionInput[],
|
|
1502
1506
|
OrderBy extends NonEmptyWindowOrderTerms
|
|
1503
1507
|
> = AstBackedExpression<
|
|
1504
|
-
|
|
1505
|
-
|
|
1508
|
+
RuntimeOfDbType<ReturnType<typeof postgresDatatypes.int8>>,
|
|
1509
|
+
ReturnType<typeof postgresDatatypes.int8>,
|
|
1506
1510
|
"never",
|
|
1507
1511
|
NumberWindowDialectOf<PartitionBy, OrderBy>,
|
|
1508
1512
|
"window",
|
|
@@ -1793,6 +1797,7 @@ const profile: QueryDialectProfile<Dialect, TextDb, NumericDb, BoolDb, Timestamp
|
|
|
1793
1797
|
>(
|
|
1794
1798
|
spec: WindowSpecInput<PartitionBy, OrderBy> | OrderedWindowSpecInput<PartitionBy, Extract<OrderBy, NonEmptyWindowOrderTerms>> | undefined
|
|
1795
1799
|
) => {
|
|
1800
|
+
validateWindowFrame(spec?.frame)
|
|
1796
1801
|
const partitionBy = [...(spec?.partitionBy ?? [])] as unknown as PartitionBy
|
|
1797
1802
|
const orderBy = (spec?.orderBy ?? []).map((term) => {
|
|
1798
1803
|
const direction = term.direction ?? "asc"
|
|
@@ -1807,7 +1812,8 @@ const profile: QueryDialectProfile<Dialect, TextDb, NumericDb, BoolDb, Timestamp
|
|
|
1807
1812
|
} & readonly { readonly value: WindowOrderInput; readonly direction: OrderDirection }[]
|
|
1808
1813
|
return {
|
|
1809
1814
|
partitionBy,
|
|
1810
|
-
orderBy
|
|
1815
|
+
orderBy,
|
|
1816
|
+
frame: spec?.frame
|
|
1811
1817
|
}
|
|
1812
1818
|
}
|
|
1813
1819
|
|
|
@@ -2137,12 +2143,17 @@ type BinaryPredicateExpression<
|
|
|
2137
2143
|
}
|
|
2138
2144
|
|
|
2139
2145
|
const upper = <Value extends ExpressionInput>(
|
|
2140
|
-
value: Value
|
|
2146
|
+
value: Value & FunctionConstraint.CaseConversionInput<
|
|
2147
|
+
NoInfer<Value>,
|
|
2148
|
+
DialectDbTypeOfInput<NoInfer<Value>, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
|
|
2149
|
+
Dialect,
|
|
2150
|
+
"upper"
|
|
2151
|
+
>
|
|
2141
2152
|
): AstBackedExpression<
|
|
2142
2153
|
string,
|
|
2143
2154
|
TextDb,
|
|
2144
2155
|
NullabilityOfDialectStringInput<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
|
|
2145
|
-
|
|
2156
|
+
Dialect,
|
|
2146
2157
|
KindOf<DialectAsStringExpression<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>>,
|
|
2147
2158
|
DependenciesOfDialectStringInput<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
|
|
2148
2159
|
ExpressionAst.UnaryNode<"upper", DialectAsStringExpression<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>>
|
|
@@ -2152,7 +2163,7 @@ type BinaryPredicateExpression<
|
|
|
2152
2163
|
runtime: "" as string,
|
|
2153
2164
|
dbType: profile.textDb as TextDb,
|
|
2154
2165
|
nullability: expression[Expression.TypeId].nullability as NullabilityOfDialectStringInput<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
|
|
2155
|
-
dialect:
|
|
2166
|
+
dialect: profile.dialect as Dialect,
|
|
2156
2167
|
kind: expression[Expression.TypeId].kind as KindOf<DialectAsStringExpression<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>>,
|
|
2157
2168
|
|
|
2158
2169
|
dependencies: expression[Expression.TypeId].dependencies
|
|
@@ -2163,12 +2174,17 @@ type BinaryPredicateExpression<
|
|
|
2163
2174
|
}
|
|
2164
2175
|
|
|
2165
2176
|
const lower = <Value extends ExpressionInput>(
|
|
2166
|
-
value: Value
|
|
2177
|
+
value: Value & FunctionConstraint.CaseConversionInput<
|
|
2178
|
+
NoInfer<Value>,
|
|
2179
|
+
DialectDbTypeOfInput<NoInfer<Value>, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
|
|
2180
|
+
Dialect,
|
|
2181
|
+
"lower"
|
|
2182
|
+
>
|
|
2167
2183
|
): AstBackedExpression<
|
|
2168
2184
|
string,
|
|
2169
2185
|
TextDb,
|
|
2170
2186
|
NullabilityOfDialectStringInput<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
|
|
2171
|
-
|
|
2187
|
+
Dialect,
|
|
2172
2188
|
KindOf<DialectAsStringExpression<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>>,
|
|
2173
2189
|
DependenciesOfDialectStringInput<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
|
|
2174
2190
|
ExpressionAst.UnaryNode<"lower", DialectAsStringExpression<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>>
|
|
@@ -2178,7 +2194,7 @@ type BinaryPredicateExpression<
|
|
|
2178
2194
|
runtime: "" as string,
|
|
2179
2195
|
dbType: profile.textDb as TextDb,
|
|
2180
2196
|
nullability: expression[Expression.TypeId].nullability as NullabilityOfDialectStringInput<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
|
|
2181
|
-
dialect:
|
|
2197
|
+
dialect: profile.dialect as Dialect,
|
|
2182
2198
|
kind: expression[Expression.TypeId].kind as KindOf<DialectAsStringExpression<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>>,
|
|
2183
2199
|
|
|
2184
2200
|
dependencies: expression[Expression.TypeId].dependencies
|
|
@@ -3496,8 +3512,8 @@ type BinaryPredicateExpression<
|
|
|
3496
3512
|
const count = <Value extends ExpressionInput>(
|
|
3497
3513
|
value: Value
|
|
3498
3514
|
): AstBackedExpression<
|
|
3499
|
-
|
|
3500
|
-
|
|
3515
|
+
RuntimeOfDbType<ReturnType<typeof postgresDatatypes.int8>>,
|
|
3516
|
+
ReturnType<typeof postgresDatatypes.int8>,
|
|
3501
3517
|
"never",
|
|
3502
3518
|
DialectOfDialectInput<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
|
|
3503
3519
|
"aggregate",
|
|
@@ -3506,8 +3522,8 @@ type BinaryPredicateExpression<
|
|
|
3506
3522
|
> => {
|
|
3507
3523
|
const expression = toDialectExpression(value)
|
|
3508
3524
|
return makeExpression({
|
|
3509
|
-
runtime:
|
|
3510
|
-
dbType:
|
|
3525
|
+
runtime: undefined as unknown as RuntimeOfDbType<ReturnType<typeof postgresDatatypes.int8>>,
|
|
3526
|
+
dbType: postgresDatatypes.int8(),
|
|
3511
3527
|
nullability: "never",
|
|
3512
3528
|
dialect: expression[Expression.TypeId].dialect,
|
|
3513
3529
|
kind: "aggregate",
|
|
@@ -3719,7 +3735,8 @@ type BinaryPredicateExpression<
|
|
|
3719
3735
|
function: "over",
|
|
3720
3736
|
value,
|
|
3721
3737
|
partitionBy: normalized.partitionBy,
|
|
3722
|
-
orderBy: normalized.orderBy
|
|
3738
|
+
orderBy: normalized.orderBy,
|
|
3739
|
+
frame: normalized.frame
|
|
3723
3740
|
})
|
|
3724
3741
|
}
|
|
3725
3742
|
|
|
@@ -3734,8 +3751,8 @@ type BinaryPredicateExpression<
|
|
|
3734
3751
|
const normalized = normalizeWindowSpec(spec)
|
|
3735
3752
|
const expressions = mergeWindowExpressions(undefined, normalized.partitionBy, normalized.orderBy)
|
|
3736
3753
|
return makeExpression({
|
|
3737
|
-
runtime:
|
|
3738
|
-
dbType:
|
|
3754
|
+
runtime: undefined as unknown as RuntimeOfDbType<ReturnType<typeof postgresDatatypes.int8>>,
|
|
3755
|
+
dbType: postgresDatatypes.int8(),
|
|
3739
3756
|
nullability: "never",
|
|
3740
3757
|
dialect: (expressions.find((expression) => expression[Expression.TypeId].dialect !== undefined)?.[Expression.TypeId].dialect ?? profile.dialect) as NumberWindowDialectOf<PartitionBy, OrderBy>,
|
|
3741
3758
|
kind: "window",
|
|
@@ -3745,7 +3762,8 @@ type BinaryPredicateExpression<
|
|
|
3745
3762
|
kind: "window",
|
|
3746
3763
|
function: kind,
|
|
3747
3764
|
partitionBy: normalized.partitionBy,
|
|
3748
|
-
orderBy: normalized.orderBy
|
|
3765
|
+
orderBy: normalized.orderBy,
|
|
3766
|
+
frame: normalized.frame
|
|
3749
3767
|
})
|
|
3750
3768
|
}
|
|
3751
3769
|
|
|
@@ -3774,7 +3792,7 @@ type BinaryPredicateExpression<
|
|
|
3774
3792
|
buildNumberWindow("denseRank", spec)
|
|
3775
3793
|
|
|
3776
3794
|
const max = <Value extends Expression.Any>(
|
|
3777
|
-
value: Value
|
|
3795
|
+
value: Value & FunctionConstraint.OrderedInput<NoInfer<Value>, Dialect, "max">
|
|
3778
3796
|
): AstBackedExpression<
|
|
3779
3797
|
Expression.RuntimeOf<Value>,
|
|
3780
3798
|
Expression.DbTypeOf<Value>,
|
|
@@ -3798,7 +3816,7 @@ type BinaryPredicateExpression<
|
|
|
3798
3816
|
})
|
|
3799
3817
|
|
|
3800
3818
|
const min = <Value extends Expression.Any>(
|
|
3801
|
-
value: Value
|
|
3819
|
+
value: Value & FunctionConstraint.OrderedInput<NoInfer<Value>, Dialect, "min">
|
|
3802
3820
|
): AstBackedExpression<
|
|
3803
3821
|
Expression.RuntimeOf<Value>,
|
|
3804
3822
|
Expression.DbTypeOf<Value>,
|
|
@@ -3833,7 +3851,7 @@ type BinaryPredicateExpression<
|
|
|
3833
3851
|
const coalesce = <
|
|
3834
3852
|
Values extends readonly [ExpressionInput, ExpressionInput, ...ExpressionInput[]]
|
|
3835
3853
|
>(
|
|
3836
|
-
...values: Values
|
|
3854
|
+
...values: Values & FunctionConstraint.CoalesceConstraint<DialectExpressionTuple<Values, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>, Dialect>
|
|
3837
3855
|
): AstBackedExpression<
|
|
3838
3856
|
CoalesceRuntimeTuple<DialectExpressionTuple<Values, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>>,
|
|
3839
3857
|
Expression.DbTypeOf<DialectExpressionTuple<Values, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>[number]>,
|
package/src/sqlite/executor.ts
CHANGED
|
@@ -39,20 +39,36 @@ export type SqliteExecutorError = SqliteDriverError | RowDecodeError
|
|
|
39
39
|
export type SqliteQueryError<PlanValue extends CoreQuery.QueryPlan<any, any, any, any, any, any, any, any, any, any>> =
|
|
40
40
|
Exclude<CoreQuery.CapabilitiesOfPlan<PlanValue>, "read"> extends never ? SqliteReadQueryError : SqliteExecutorError
|
|
41
41
|
|
|
42
|
+
/** Pipeable execution cardinality helpers. */
|
|
43
|
+
export const atMostOne = CoreExecutor.atMostOne
|
|
44
|
+
export const exactlyOne = CoreExecutor.exactlyOne
|
|
45
|
+
export const nonEmpty = CoreExecutor.nonEmpty
|
|
46
|
+
|
|
42
47
|
/** Runs an effect within the ambient SQLite SQL transaction service. */
|
|
43
48
|
export const withTransaction = CoreExecutor.withTransaction
|
|
44
49
|
|
|
45
50
|
/** SQLite executor whose error channel narrows based on the query plan. */
|
|
46
|
-
export interface QueryExecutor<Context = never> {
|
|
51
|
+
export interface QueryExecutor<Context = never> extends CoreExecutor.Executor<"sqlite", SqliteQueryError<any>, Context> {
|
|
47
52
|
readonly dialect: "sqlite"
|
|
48
53
|
execute<PlanValue extends CoreQuery.QueryPlan<any, any, any, any, any, any, any, any, any, any>>(
|
|
49
54
|
plan: CoreQuery.DialectCompatiblePlan<PlanValue, "sqlite">
|
|
50
55
|
): Effect.Effect<CoreQuery.ResultRows<PlanValue>, SqliteQueryError<PlanValue>, Context>
|
|
56
|
+
executeResult<PlanValue extends CoreQuery.QueryPlan<any, any, any, any, any, any, any, any, any, any>>(
|
|
57
|
+
plan: CoreQuery.DialectCompatiblePlan<PlanValue, "sqlite">
|
|
58
|
+
): Effect.Effect<CoreExecutor.ExecutionResult<CoreQuery.ResultRow<PlanValue>>, SqliteQueryError<PlanValue>, Context>
|
|
59
|
+
prepare<PlanValue extends CoreQuery.QueryPlan<any, any, any, any, any, any, any, any, any, any>>(
|
|
60
|
+
plan: CoreQuery.DialectCompatiblePlan<PlanValue, "sqlite">
|
|
61
|
+
): CoreExecutor.PreparedQuery<CoreQuery.ResultRow<PlanValue>, SqliteQueryError<PlanValue>, Context>
|
|
51
62
|
stream<PlanValue extends CoreQuery.QueryPlan<any, any, any, any, any, any, any, any, any, any>>(
|
|
52
63
|
plan: Exclude<CoreQuery.CapabilitiesOfPlan<PlanValue>, "read" | "locking"> extends never
|
|
53
64
|
? CoreQuery.DialectCompatiblePlan<PlanValue, "sqlite">
|
|
54
65
|
: never
|
|
55
66
|
): Stream.Stream<CoreQuery.ResultRow<PlanValue>, SqliteQueryError<PlanValue>, Context>
|
|
67
|
+
explain<PlanValue extends CoreQuery.QueryPlan<any, any, any, any, any, any, any, any, any, any>>(
|
|
68
|
+
plan: Exclude<CoreQuery.CapabilitiesOfPlan<PlanValue>, "read" | "locking"> extends never
|
|
69
|
+
? CoreQuery.DialectCompatiblePlan<PlanValue, "sqlite">
|
|
70
|
+
: never
|
|
71
|
+
): Effect.Effect<ReadonlyArray<FlatRow>, SqliteQueryError<PlanValue>, Context>
|
|
56
72
|
}
|
|
57
73
|
|
|
58
74
|
type DriverExecute<Error, Context> = <Row>(
|
|
@@ -61,6 +77,9 @@ type DriverExecute<Error, Context> = <Row>(
|
|
|
61
77
|
|
|
62
78
|
type DriverHandlers<Error, Context> = {
|
|
63
79
|
readonly execute: DriverExecute<Error, Context>
|
|
80
|
+
readonly executeResult?: <Row>(
|
|
81
|
+
query: CoreRenderer.RenderedQuery<Row, "sqlite">
|
|
82
|
+
) => Effect.Effect<CoreExecutor.DriverResult, Error, Context>
|
|
64
83
|
readonly stream: <Row>(
|
|
65
84
|
query: CoreRenderer.RenderedQuery<Row, "sqlite">
|
|
66
85
|
) => Stream.Stream<FlatRow, Error, Context>
|
|
@@ -114,51 +133,83 @@ const fromDriver = <
|
|
|
114
133
|
sqlDriver: Driver<Error, Context>,
|
|
115
134
|
driverMode: CoreExecutor.DriverMode = "raw",
|
|
116
135
|
valueMappings?: Expression.DriverValueMappings
|
|
117
|
-
): QueryExecutor<Context> =>
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
stream(plan) {
|
|
141
|
-
const rendered = renderer.render(plan)
|
|
142
|
-
return Stream.mapError(
|
|
143
|
-
Stream.mapArrayEffect(
|
|
144
|
-
sqlDriver.stream(rendered),
|
|
145
|
-
(rows) => Effect.try({
|
|
146
|
-
try: () => CoreExecutor.decodeRows(rendered, plan, rows, { driverMode, valueMappings }) as never,
|
|
147
|
-
catch: (error) => error as RowDecodeError
|
|
148
|
-
})
|
|
149
|
-
),
|
|
150
|
-
(error) => {
|
|
151
|
-
if (typeof error === "object" && error !== null && "_tag" in error && error._tag === "RowDecodeError") {
|
|
152
|
-
return error as RowDecodeError
|
|
153
|
-
}
|
|
154
|
-
const normalized = normalizeSqliteDriverError(error, rendered)
|
|
155
|
-
return CoreExecutor.hasWriteCapability(plan)
|
|
156
|
-
? normalized
|
|
157
|
-
: narrowSqliteDriverErrorForReadQuery(normalized)
|
|
158
|
-
}
|
|
159
|
-
) as Stream.Stream<any, any, Context>
|
|
136
|
+
): QueryExecutor<Context> => {
|
|
137
|
+
const renderedCache = new WeakMap<object, CoreRenderer.RenderedQuery<any, "sqlite">>()
|
|
138
|
+
const render = (plan: CoreQuery.Plan.Any) => {
|
|
139
|
+
const cached = renderedCache.get(plan)
|
|
140
|
+
if (cached !== undefined) {
|
|
141
|
+
return cached
|
|
142
|
+
}
|
|
143
|
+
const rendered = renderer.render(plan as any)
|
|
144
|
+
renderedCache.set(plan, rendered)
|
|
145
|
+
return rendered
|
|
146
|
+
}
|
|
147
|
+
const mapExecutionError = (
|
|
148
|
+
error: unknown,
|
|
149
|
+
rendered: CoreRenderer.RenderedQuery<any, "sqlite">,
|
|
150
|
+
plan: CoreQuery.Plan.Any
|
|
151
|
+
) => {
|
|
152
|
+
if (typeof error === "object" && error !== null && "_tag" in error && error._tag === "RowDecodeError") {
|
|
153
|
+
return error as RowDecodeError
|
|
154
|
+
}
|
|
155
|
+
const normalized = normalizeSqliteDriverError(error, rendered)
|
|
156
|
+
return CoreExecutor.hasWriteCapability(plan)
|
|
157
|
+
? normalized
|
|
158
|
+
: narrowSqliteDriverErrorForReadQuery(normalized)
|
|
160
159
|
}
|
|
161
|
-
|
|
160
|
+
return CoreExecutor.withResultContracts({
|
|
161
|
+
dialect: "sqlite",
|
|
162
|
+
execute(plan) {
|
|
163
|
+
const rendered = render(plan)
|
|
164
|
+
return Effect.mapError(
|
|
165
|
+
Effect.flatMap(
|
|
166
|
+
sqlDriver.execute(rendered),
|
|
167
|
+
(rows) => Effect.try({
|
|
168
|
+
try: () => CoreExecutor.decodeRows(rendered, plan, rows, { driverMode, valueMappings }),
|
|
169
|
+
catch: (error) => error as RowDecodeError
|
|
170
|
+
})
|
|
171
|
+
),
|
|
172
|
+
(error) => mapExecutionError(error, rendered, plan)
|
|
173
|
+
) as Effect.Effect<any, any, Context>
|
|
174
|
+
},
|
|
175
|
+
executeResult(plan) {
|
|
176
|
+
const rendered = render(plan)
|
|
177
|
+
const result = sqlDriver.executeResult
|
|
178
|
+
? sqlDriver.executeResult(rendered)
|
|
179
|
+
: Effect.map(sqlDriver.execute(rendered), (rows) => ({ rows }))
|
|
180
|
+
return Effect.mapError(
|
|
181
|
+
Effect.flatMap(result, ({ rows, ...metadata }) => Effect.try({
|
|
182
|
+
try: () => ({
|
|
183
|
+
...metadata,
|
|
184
|
+
rows: CoreExecutor.decodeRows(rendered, plan, rows, { driverMode, valueMappings })
|
|
185
|
+
}),
|
|
186
|
+
catch: (error) => error as RowDecodeError
|
|
187
|
+
})),
|
|
188
|
+
(error) => mapExecutionError(error, rendered, plan)
|
|
189
|
+
) as Effect.Effect<any, any, Context>
|
|
190
|
+
},
|
|
191
|
+
stream(plan) {
|
|
192
|
+
const rendered = render(plan)
|
|
193
|
+
return Stream.mapError(
|
|
194
|
+
Stream.mapArrayEffect(
|
|
195
|
+
sqlDriver.stream(rendered),
|
|
196
|
+
(rows) => Effect.try({
|
|
197
|
+
try: () => CoreExecutor.decodeRows(rendered, plan, rows, { driverMode, valueMappings }) as never,
|
|
198
|
+
catch: (error) => error as RowDecodeError
|
|
199
|
+
})
|
|
200
|
+
),
|
|
201
|
+
(error) => mapExecutionError(error, rendered, plan)
|
|
202
|
+
) as Stream.Stream<any, any, Context>
|
|
203
|
+
},
|
|
204
|
+
explain(plan, options) {
|
|
205
|
+
const rendered = CoreExecutor.explainQuery(render(plan), options)
|
|
206
|
+
return Effect.mapError(
|
|
207
|
+
sqlDriver.execute(rendered),
|
|
208
|
+
(error) => mapExecutionError(error, rendered, plan)
|
|
209
|
+
) as Effect.Effect<any, any, Context>
|
|
210
|
+
}
|
|
211
|
+
}) as QueryExecutor<Context>
|
|
212
|
+
}
|
|
162
213
|
|
|
163
214
|
const sqlClientDriver = (): Driver<any, SqlClient.SqlClient> =>
|
|
164
215
|
driver({
|