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.
Files changed (58) hide show
  1. package/dist/index.js +663 -261
  2. package/dist/mysql.js +2840 -492
  3. package/dist/postgres/metadata.js +113 -14
  4. package/dist/postgres.js +2596 -285
  5. package/dist/sqlite.js +2783 -450
  6. package/dist/standard.js +663 -261
  7. package/package.json +1 -1
  8. package/src/internal/analytics.ts +264 -0
  9. package/src/internal/coercion/errors.d.ts +1 -1
  10. package/src/internal/coercion/errors.ts +1 -1
  11. package/src/internal/custom-sql-renderer.ts +20 -0
  12. package/src/internal/dialect-numeric.ts +332 -0
  13. package/src/internal/dialect-renderers/mysql.ts +68 -13
  14. package/src/internal/dialect-renderers/postgres.ts +83 -13
  15. package/src/internal/dialect-renderers/sqlite.ts +57 -13
  16. package/src/internal/dynamic.ts +131 -0
  17. package/src/internal/executor.ts +198 -7
  18. package/src/internal/expression-ast.ts +62 -1
  19. package/src/internal/fragment.ts +120 -0
  20. package/src/internal/function-constraints.ts +105 -0
  21. package/src/internal/grouping-key.ts +26 -0
  22. package/src/internal/numeric.ts +204 -0
  23. package/src/internal/query.ts +13 -1
  24. package/src/internal/runtime/normalize.ts +5 -2
  25. package/src/internal/runtime/schema.ts +2 -1
  26. package/src/internal/standard-dsl.ts +43 -20
  27. package/src/internal/window-frame.ts +30 -0
  28. package/src/internal/window-renderer.ts +25 -0
  29. package/src/mysql/executor.ts +126 -45
  30. package/src/mysql/function/aggregate.ts +87 -1
  31. package/src/mysql/function/index.ts +5 -2
  32. package/src/mysql/function/numeric.ts +185 -0
  33. package/src/mysql/function/temporal.ts +6 -6
  34. package/src/mysql/function/window.ts +12 -0
  35. package/src/mysql/internal/dsl.ts +38 -20
  36. package/src/mysql.ts +2 -0
  37. package/src/postgres/executor.ts +111 -45
  38. package/src/postgres/function/aggregate.ts +124 -1
  39. package/src/postgres/function/core.ts +2 -1
  40. package/src/postgres/function/index.ts +19 -1
  41. package/src/postgres/function/numeric.ts +246 -0
  42. package/src/postgres/function/window.ts +12 -0
  43. package/src/postgres/internal/dsl.ts +38 -20
  44. package/src/sqlite/executor.ts +96 -45
  45. package/src/sqlite/function/aggregate.ts +90 -1
  46. package/src/sqlite/function/index.ts +5 -2
  47. package/src/sqlite/function/numeric.ts +139 -0
  48. package/src/sqlite/function/window.ts +12 -0
  49. package/src/sqlite/internal/dsl.ts +38 -20
  50. package/src/sqlite.ts +2 -0
  51. package/src/standard/function/core.ts +8 -1
  52. package/src/standard/function/index.ts +18 -11
  53. package/src/standard/function/string.ts +1 -1
  54. package/src/standard/function/window.ts +10 -1
  55. package/src/standard/query.ts +19 -7
  56. package/src/standard/type.ts +16 -0
  57. package/src/standard.ts +4 -0
  58. 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
- number,
1505
- Expression.DbType.Any,
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
- DialectOfDialectStringInput<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
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: expression[Expression.TypeId].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
- DialectOfDialectStringInput<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
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: expression[Expression.TypeId].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
- number,
3500
- NumericDb,
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: 0 as number,
3510
- dbType: profile.numericDb as NumericDb,
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: 0 as number,
3738
- dbType: profile.numericDb as Expression.DbType.Any,
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]>,
@@ -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
- dialect: "sqlite",
119
- execute(plan) {
120
- const rendered = renderer.render(plan)
121
- return Effect.mapError(
122
- Effect.flatMap(
123
- sqlDriver.execute(rendered),
124
- (rows) => Effect.try({
125
- try: () => CoreExecutor.decodeRows(rendered, plan, rows, { driverMode, valueMappings }),
126
- catch: (error) => error as RowDecodeError
127
- })
128
- ),
129
- (error) => {
130
- if (typeof error === "object" && error !== null && "_tag" in error && error._tag === "RowDecodeError") {
131
- return error as RowDecodeError
132
- }
133
- const normalized = normalizeSqliteDriverError(error, rendered)
134
- return CoreExecutor.hasWriteCapability(plan)
135
- ? normalized
136
- : narrowSqliteDriverErrorForReadQuery(normalized)
137
- }
138
- ) as Effect.Effect<any, any, Context>
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({