effect-qb 0.20.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 +2 -3
  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
@@ -4,6 +4,7 @@ import * as Schema from "effect/Schema"
4
4
  import { mysqlDatatypes } 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 {
@@ -133,6 +134,7 @@ import type { FormulaOfPredicate } from "../../internal/predicate/normalize.js"
133
134
  import type { TrueFormula } from "../../internal/predicate/formula.js"
134
135
  import { assumeFormulaTrue, formulaOfExpression as formulaOfExpressionRuntime, trueFormula } from "../../internal/predicate/runtime.js"
135
136
  import { dedupeGroupedExpressions } from "../../internal/grouping-key.js"
137
+ import { validateWindowFrame } from "../../internal/window-frame.js"
136
138
  import { makeDslMutationRuntime } from "../../internal/dsl-mutation-runtime.js"
137
139
  import { makeDslPlanRuntime } from "../../internal/dsl-plan-runtime.js"
138
140
  import { makeDslQueryRuntime } from "../../internal/dsl-query-runtime.js"
@@ -1440,6 +1442,7 @@ type WindowSpecInput<
1440
1442
  > = {
1441
1443
  readonly partitionBy?: PartitionBy
1442
1444
  readonly orderBy?: OrderBy
1445
+ readonly frame?: ExpressionAst.WindowFrameNode<"rows" | "range">
1443
1446
  }
1444
1447
 
1445
1448
  type OrderedWindowSpecInput<
@@ -1448,6 +1451,7 @@ type OrderedWindowSpecInput<
1448
1451
  > = {
1449
1452
  readonly partitionBy?: PartitionBy
1450
1453
  readonly orderBy: OrderBy
1454
+ readonly frame?: never
1451
1455
  }
1452
1456
 
1453
1457
  type WindowOrderExpressionTuple<
@@ -1520,8 +1524,8 @@ type NumberWindowExpression<
1520
1524
  PartitionBy extends readonly WindowPartitionInput[],
1521
1525
  OrderBy extends NonEmptyWindowOrderTerms
1522
1526
  > = AstBackedExpression<
1523
- number,
1524
- Expression.DbType.Any,
1527
+ RuntimeOfDbType<ReturnType<typeof mysqlDatatypes.bigint>>,
1528
+ ReturnType<typeof mysqlDatatypes.bigint>,
1525
1529
  "never",
1526
1530
  NumberWindowDialectOf<PartitionBy, OrderBy>,
1527
1531
  "window",
@@ -1812,6 +1816,7 @@ const profile: QueryDialectProfile<Dialect, TextDb, NumericDb, BoolDb, Timestamp
1812
1816
  >(
1813
1817
  spec: WindowSpecInput<PartitionBy, OrderBy> | OrderedWindowSpecInput<PartitionBy, Extract<OrderBy, NonEmptyWindowOrderTerms>> | undefined
1814
1818
  ) => {
1819
+ validateWindowFrame(spec?.frame)
1815
1820
  const partitionBy = [...(spec?.partitionBy ?? [])] as unknown as PartitionBy
1816
1821
  const orderBy = (spec?.orderBy ?? []).map((term) => {
1817
1822
  const direction = term.direction ?? "asc"
@@ -1826,7 +1831,8 @@ const profile: QueryDialectProfile<Dialect, TextDb, NumericDb, BoolDb, Timestamp
1826
1831
  } & readonly { readonly value: WindowOrderInput; readonly direction: OrderDirection }[]
1827
1832
  return {
1828
1833
  partitionBy,
1829
- orderBy
1834
+ orderBy,
1835
+ frame: spec?.frame
1830
1836
  }
1831
1837
  }
1832
1838
 
@@ -2156,12 +2162,17 @@ type BinaryPredicateExpression<
2156
2162
  }
2157
2163
 
2158
2164
  const upper = <Value extends ExpressionInput>(
2159
- value: Value
2165
+ value: Value & FunctionConstraint.CaseConversionInput<
2166
+ NoInfer<Value>,
2167
+ DialectDbTypeOfInput<NoInfer<Value>, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
2168
+ Dialect,
2169
+ "upper"
2170
+ >
2160
2171
  ): AstBackedExpression<
2161
2172
  string,
2162
2173
  TextDb,
2163
2174
  NullabilityOfDialectStringInput<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
2164
- DialectOfDialectStringInput<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
2175
+ Dialect,
2165
2176
  KindOf<DialectAsStringExpression<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>>,
2166
2177
  DependenciesOfDialectStringInput<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
2167
2178
  ExpressionAst.UnaryNode<"upper", DialectAsStringExpression<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>>
@@ -2171,7 +2182,7 @@ type BinaryPredicateExpression<
2171
2182
  runtime: "" as string,
2172
2183
  dbType: profile.textDb as TextDb,
2173
2184
  nullability: expression[Expression.TypeId].nullability as NullabilityOfDialectStringInput<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
2174
- dialect: expression[Expression.TypeId].dialect,
2185
+ dialect: profile.dialect as Dialect,
2175
2186
  kind: expression[Expression.TypeId].kind as KindOf<DialectAsStringExpression<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>>,
2176
2187
 
2177
2188
  dependencies: expression[Expression.TypeId].dependencies
@@ -2182,12 +2193,17 @@ type BinaryPredicateExpression<
2182
2193
  }
2183
2194
 
2184
2195
  const lower = <Value extends ExpressionInput>(
2185
- value: Value
2196
+ value: Value & FunctionConstraint.CaseConversionInput<
2197
+ NoInfer<Value>,
2198
+ DialectDbTypeOfInput<NoInfer<Value>, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
2199
+ Dialect,
2200
+ "lower"
2201
+ >
2186
2202
  ): AstBackedExpression<
2187
2203
  string,
2188
2204
  TextDb,
2189
2205
  NullabilityOfDialectStringInput<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
2190
- DialectOfDialectStringInput<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
2206
+ Dialect,
2191
2207
  KindOf<DialectAsStringExpression<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>>,
2192
2208
  DependenciesOfDialectStringInput<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
2193
2209
  ExpressionAst.UnaryNode<"lower", DialectAsStringExpression<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>>
@@ -2197,7 +2213,7 @@ type BinaryPredicateExpression<
2197
2213
  runtime: "" as string,
2198
2214
  dbType: profile.textDb as TextDb,
2199
2215
  nullability: expression[Expression.TypeId].nullability as NullabilityOfDialectStringInput<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
2200
- dialect: expression[Expression.TypeId].dialect,
2216
+ dialect: profile.dialect as Dialect,
2201
2217
  kind: expression[Expression.TypeId].kind as KindOf<DialectAsStringExpression<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>>,
2202
2218
 
2203
2219
  dependencies: expression[Expression.TypeId].dependencies
@@ -3476,8 +3492,8 @@ type BinaryPredicateExpression<
3476
3492
  const count = <Value extends ExpressionInput>(
3477
3493
  value: Value
3478
3494
  ): AstBackedExpression<
3479
- number,
3480
- NumericDb,
3495
+ RuntimeOfDbType<ReturnType<typeof mysqlDatatypes.bigint>>,
3496
+ ReturnType<typeof mysqlDatatypes.bigint>,
3481
3497
  "never",
3482
3498
  DialectOfDialectInput<Value, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>,
3483
3499
  "aggregate",
@@ -3486,8 +3502,8 @@ type BinaryPredicateExpression<
3486
3502
  > => {
3487
3503
  const expression = toDialectExpression(value)
3488
3504
  return makeExpression({
3489
- runtime: 0 as number,
3490
- dbType: profile.numericDb as NumericDb,
3505
+ runtime: undefined as unknown as RuntimeOfDbType<ReturnType<typeof mysqlDatatypes.bigint>>,
3506
+ dbType: mysqlDatatypes.bigint(),
3491
3507
  nullability: "never",
3492
3508
  dialect: expression[Expression.TypeId].dialect,
3493
3509
  kind: "aggregate",
@@ -3699,7 +3715,8 @@ type BinaryPredicateExpression<
3699
3715
  function: "over",
3700
3716
  value,
3701
3717
  partitionBy: normalized.partitionBy,
3702
- orderBy: normalized.orderBy
3718
+ orderBy: normalized.orderBy,
3719
+ frame: normalized.frame
3703
3720
  })
3704
3721
  }
3705
3722
 
@@ -3714,8 +3731,8 @@ type BinaryPredicateExpression<
3714
3731
  const normalized = normalizeWindowSpec(spec)
3715
3732
  const expressions = mergeWindowExpressions(undefined, normalized.partitionBy, normalized.orderBy)
3716
3733
  return makeExpression({
3717
- runtime: 0 as number,
3718
- dbType: profile.numericDb as Expression.DbType.Any,
3734
+ runtime: undefined as unknown as RuntimeOfDbType<ReturnType<typeof mysqlDatatypes.bigint>>,
3735
+ dbType: mysqlDatatypes.bigint(),
3719
3736
  nullability: "never",
3720
3737
  dialect: (expressions.find((expression) => expression[Expression.TypeId].dialect !== undefined)?.[Expression.TypeId].dialect ?? profile.dialect) as NumberWindowDialectOf<PartitionBy, OrderBy>,
3721
3738
  kind: "window",
@@ -3725,7 +3742,8 @@ type BinaryPredicateExpression<
3725
3742
  kind: "window",
3726
3743
  function: kind,
3727
3744
  partitionBy: normalized.partitionBy,
3728
- orderBy: normalized.orderBy
3745
+ orderBy: normalized.orderBy,
3746
+ frame: normalized.frame
3729
3747
  })
3730
3748
  }
3731
3749
 
@@ -3754,7 +3772,7 @@ type BinaryPredicateExpression<
3754
3772
  buildNumberWindow("denseRank", spec)
3755
3773
 
3756
3774
  const max = <Value extends Expression.Any>(
3757
- value: Value
3775
+ value: Value & FunctionConstraint.OrderedInput<NoInfer<Value>, Dialect, "max">
3758
3776
  ): AstBackedExpression<
3759
3777
  Expression.RuntimeOf<Value>,
3760
3778
  Expression.DbTypeOf<Value>,
@@ -3778,7 +3796,7 @@ type BinaryPredicateExpression<
3778
3796
  })
3779
3797
 
3780
3798
  const min = <Value extends Expression.Any>(
3781
- value: Value
3799
+ value: Value & FunctionConstraint.OrderedInput<NoInfer<Value>, Dialect, "min">
3782
3800
  ): AstBackedExpression<
3783
3801
  Expression.RuntimeOf<Value>,
3784
3802
  Expression.DbTypeOf<Value>,
@@ -3813,7 +3831,7 @@ type BinaryPredicateExpression<
3813
3831
  const coalesce = <
3814
3832
  Values extends readonly [ExpressionInput, ExpressionInput, ...ExpressionInput[]]
3815
3833
  >(
3816
- ...values: Values
3834
+ ...values: Values & FunctionConstraint.CoalesceConstraint<DialectExpressionTuple<Values, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>, Dialect>
3817
3835
  ): AstBackedExpression<
3818
3836
  CoalesceRuntimeTuple<DialectExpressionTuple<Values, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>>,
3819
3837
  Expression.DbTypeOf<DialectExpressionTuple<Values, Dialect, TextDb, NumericDb, BoolDb, TimestampDb, NullDb>[number]>,
package/src/mysql.ts CHANGED
@@ -4,6 +4,8 @@ export * as Column from "./mysql/column-extension.js"
4
4
  export * as Datatypes from "./mysql/datatypes/index.js"
5
5
  /** MySQL error catalog and error normalization helpers. */
6
6
  export * as Errors from "./mysql/errors/index.js"
7
+ /** MySQL-specific SQL function expressions. */
8
+ export * as Function from "./mysql/function/index.js"
7
9
  /** MySQL-specific JSON expression helpers. Portable JSON helpers are exported from the root package. */
8
10
  export * as Json from "./mysql/json.js"
9
11
  /** MySQL-specialized typed query execution contracts. */
@@ -26,6 +26,8 @@ export type Executor<Error = never, Context = never> = CoreExecutor.Executor<"po
26
26
  /** Postgres-specialized renderer contract. */
27
27
  export type Renderer = CoreRenderer.Renderer<"postgres">
28
28
  export type ValueMappings = Expression.DriverValueMappingsFor<PostgresDatatypeKind, PostgresDatatypeFamily>
29
+ /** PostgreSQL EXPLAIN options. ANALYZE executes the read query. */
30
+ export type ExplainOptions = CoreExecutor.ExplainOptions
29
31
  /** Optional renderer / driver overrides for the standard Postgres executor pipeline. */
30
32
  export interface MakeOptions<Error = never, Context = never> {
31
33
  readonly renderer?: Renderer
@@ -39,20 +41,37 @@ export type PostgresExecutorError = PostgresDriverError | RowDecodeError
39
41
  export type PostgresQueryError<PlanValue extends CoreQuery.QueryPlan<any, any, any, any, any, any, any, any, any, any>> =
40
42
  Exclude<CoreQuery.CapabilitiesOfPlan<PlanValue>, "read"> extends never ? PostgresReadQueryError : PostgresExecutorError
41
43
 
44
+ /** Pipeable execution cardinality helpers. */
45
+ export const atMostOne = CoreExecutor.atMostOne
46
+ export const exactlyOne = CoreExecutor.exactlyOne
47
+ export const nonEmpty = CoreExecutor.nonEmpty
48
+
42
49
  /** Runs an effect within the ambient Postgres SQL transaction service. */
43
50
  export const withTransaction = CoreExecutor.withTransaction
44
51
 
45
52
  /** Postgres executor whose error channel narrows based on the query plan. */
46
- export interface QueryExecutor<Context = never> {
53
+ export interface QueryExecutor<Context = never> extends CoreExecutor.Executor<"postgres", PostgresQueryError<any>, Context> {
47
54
  readonly dialect: "postgres"
48
55
  execute<PlanValue extends CoreQuery.QueryPlan<any, any, any, any, any, any, any, any, any, any>>(
49
56
  plan: CoreQuery.DialectCompatiblePlan<PlanValue, "postgres">
50
57
  ): Effect.Effect<CoreQuery.ResultRows<PlanValue>, PostgresQueryError<PlanValue>, Context>
58
+ executeResult<PlanValue extends CoreQuery.QueryPlan<any, any, any, any, any, any, any, any, any, any>>(
59
+ plan: CoreQuery.DialectCompatiblePlan<PlanValue, "postgres">
60
+ ): Effect.Effect<CoreExecutor.ExecutionResult<CoreQuery.ResultRow<PlanValue>>, PostgresQueryError<PlanValue>, Context>
61
+ prepare<PlanValue extends CoreQuery.QueryPlan<any, any, any, any, any, any, any, any, any, any>>(
62
+ plan: CoreQuery.DialectCompatiblePlan<PlanValue, "postgres">
63
+ ): CoreExecutor.PreparedQuery<CoreQuery.ResultRow<PlanValue>, PostgresQueryError<PlanValue>, Context>
51
64
  stream<PlanValue extends CoreQuery.QueryPlan<any, any, any, any, any, any, any, any, any, any>>(
52
65
  plan: Exclude<CoreQuery.CapabilitiesOfPlan<PlanValue>, "read" | "locking"> extends never
53
66
  ? CoreQuery.DialectCompatiblePlan<PlanValue, "postgres">
54
67
  : never
55
68
  ): Stream.Stream<CoreQuery.ResultRow<PlanValue>, PostgresQueryError<PlanValue>, Context>
69
+ explain<PlanValue extends CoreQuery.QueryPlan<any, any, any, any, any, any, any, any, any, any>>(
70
+ plan: Exclude<CoreQuery.CapabilitiesOfPlan<PlanValue>, "read" | "locking"> extends never
71
+ ? CoreQuery.DialectCompatiblePlan<PlanValue, "postgres">
72
+ : never,
73
+ options?: ExplainOptions
74
+ ): Effect.Effect<ReadonlyArray<FlatRow>, PostgresQueryError<PlanValue>, Context>
56
75
  }
57
76
 
58
77
  type DriverExecute<Error, Context> = <Row>(
@@ -61,6 +80,9 @@ type DriverExecute<Error, Context> = <Row>(
61
80
 
62
81
  type DriverHandlers<Error, Context> = {
63
82
  readonly execute: DriverExecute<Error, Context>
83
+ readonly executeResult?: <Row>(
84
+ query: CoreRenderer.RenderedQuery<Row, "postgres">
85
+ ) => Effect.Effect<CoreExecutor.DriverResult, Error, Context>
64
86
  readonly stream: <Row>(
65
87
  query: CoreRenderer.RenderedQuery<Row, "postgres">
66
88
  ) => Stream.Stream<FlatRow, Error, Context>
@@ -114,57 +136,101 @@ const fromDriver = <
114
136
  sqlDriver: Driver<Error, Context>,
115
137
  driverMode: CoreExecutor.DriverMode = "raw",
116
138
  valueMappings?: Expression.DriverValueMappings
117
- ): QueryExecutor<Context> => ({
118
- dialect: "postgres",
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 = normalizePostgresDriverError(error, rendered)
134
- return CoreExecutor.hasWriteCapability(plan)
135
- ? normalized
136
- : narrowPostgresDriverErrorForReadQuery(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 = normalizePostgresDriverError(error, rendered)
155
- return CoreExecutor.hasWriteCapability(plan)
156
- ? normalized
157
- : narrowPostgresDriverErrorForReadQuery(normalized)
158
- }
159
- ) as Stream.Stream<any, any, Context>
139
+ ): QueryExecutor<Context> => {
140
+ const renderedCache = new WeakMap<object, CoreRenderer.RenderedQuery<any, "postgres">>()
141
+ const render = (plan: CoreQuery.Plan.Any) => {
142
+ const cached = renderedCache.get(plan)
143
+ if (cached !== undefined) {
144
+ return cached
145
+ }
146
+ const rendered = renderer.render(plan as any)
147
+ renderedCache.set(plan, rendered)
148
+ return rendered
160
149
  }
161
- })
150
+ const mapExecutionError = (
151
+ error: unknown,
152
+ rendered: CoreRenderer.RenderedQuery<any, "postgres">,
153
+ plan: CoreQuery.Plan.Any
154
+ ) => {
155
+ if (typeof error === "object" && error !== null && "_tag" in error && error._tag === "RowDecodeError") {
156
+ return error as RowDecodeError
157
+ }
158
+ const normalized = normalizePostgresDriverError(error, rendered)
159
+ return CoreExecutor.hasWriteCapability(plan)
160
+ ? normalized
161
+ : narrowPostgresDriverErrorForReadQuery(normalized)
162
+ }
163
+ return CoreExecutor.withResultContracts({
164
+ dialect: "postgres",
165
+ execute(plan) {
166
+ const rendered = render(plan)
167
+ return Effect.mapError(
168
+ Effect.flatMap(
169
+ sqlDriver.execute(rendered),
170
+ (rows) => Effect.try({
171
+ try: () => CoreExecutor.decodeRows(rendered, plan, rows, { driverMode, valueMappings }),
172
+ catch: (error) => error as RowDecodeError
173
+ })
174
+ ),
175
+ (error) => mapExecutionError(error, rendered, plan)
176
+ ) as Effect.Effect<any, any, Context>
177
+ },
178
+ executeResult(plan) {
179
+ const rendered = render(plan)
180
+ const result = sqlDriver.executeResult
181
+ ? sqlDriver.executeResult(rendered)
182
+ : Effect.map(sqlDriver.execute(rendered), (rows) => ({ rows }))
183
+ return Effect.mapError(
184
+ Effect.flatMap(result, ({ rows, ...metadata }) => Effect.try({
185
+ try: () => ({
186
+ ...metadata,
187
+ rows: CoreExecutor.decodeRows(rendered, plan, rows, { driverMode, valueMappings })
188
+ }),
189
+ catch: (error) => error as RowDecodeError
190
+ })),
191
+ (error) => mapExecutionError(error, rendered, plan)
192
+ ) as Effect.Effect<any, any, Context>
193
+ },
194
+ stream(plan) {
195
+ const rendered = render(plan)
196
+ return Stream.mapError(
197
+ Stream.mapArrayEffect(
198
+ sqlDriver.stream(rendered),
199
+ (rows) => Effect.try({
200
+ try: () => CoreExecutor.decodeRows(rendered, plan, rows, { driverMode, valueMappings }) as never,
201
+ catch: (error) => error as RowDecodeError
202
+ })
203
+ ),
204
+ (error) => mapExecutionError(error, rendered, plan)
205
+ ) as Stream.Stream<any, any, Context>
206
+ },
207
+ explain(plan, options) {
208
+ const rendered = CoreExecutor.explainQuery(render(plan), options)
209
+ return Effect.mapError(
210
+ sqlDriver.execute(rendered),
211
+ (error) => mapExecutionError(error, rendered, plan)
212
+ ) as Effect.Effect<any, any, Context>
213
+ }
214
+ }) as QueryExecutor<Context>
215
+ }
162
216
 
163
217
  const sqlClientDriver = (): Driver<any, SqlClient.SqlClient> =>
164
218
  driver({
165
219
  execute: (query: CoreRenderer.RenderedQuery<any, "postgres">) =>
166
220
  Effect.flatMap(SqlClient.SqlClient, (sql) =>
167
221
  sql.unsafe<FlatRow>(query.sql, [...query.params])),
222
+ executeResult: (query: CoreRenderer.RenderedQuery<any, "postgres">) =>
223
+ Effect.flatMap(SqlClient.SqlClient, (sql) =>
224
+ Effect.map(sql.unsafe<FlatRow>(query.sql, [...query.params]).raw, (raw) => {
225
+ const result = raw as {
226
+ readonly rows?: ReadonlyArray<FlatRow>
227
+ readonly rowCount?: number | null
228
+ }
229
+ return {
230
+ rows: result.rows ?? [],
231
+ ...(typeof result.rowCount === "number" ? { affectedRows: result.rowCount } : {})
232
+ }
233
+ })),
168
234
  stream: (query: CoreRenderer.RenderedQuery<any, "postgres">) =>
169
235
  CoreExecutor.streamFromSqlClient(query)
170
236
  })
@@ -1,2 +1,125 @@
1
- /** Postgres aggregate functions. */
1
+ import * as Numeric from "../../internal/dialect-numeric.js"
2
+ import * as Expression from "../../internal/scalar.js"
3
+ import { postgresDatatypes } from "../datatypes/index.js"
4
+
2
5
  export { count, max, min } from "../internal/dsl.js"
6
+
7
+ type PgInt8 = ReturnType<typeof postgresDatatypes.int8>
8
+ type PgNumeric = ReturnType<typeof postgresDatatypes.numeric>
9
+ type PgFloat4 = ReturnType<typeof postgresDatatypes.float4>
10
+ type PgFloat8 = ReturnType<typeof postgresDatatypes.float8>
11
+
12
+ type IsAny<Value> = 0 extends (1 & Value) ? true : false
13
+
14
+ type BaseDb<Db extends Expression.DbType.Any> =
15
+ Db extends Expression.DbType.Domain<any, infer Base extends Expression.DbType.Any, any>
16
+ ? BaseDb<Base>
17
+ : Db
18
+
19
+ type PgAggregateCategory<Db extends Expression.DbType.Any> =
20
+ BaseDb<Db> extends infer Base extends Expression.DbType.Any
21
+ ? Base["dialect"] extends "standard"
22
+ ? Base["kind"] extends "int" | "integer" ? "int4"
23
+ : Base["kind"] extends "bigint" ? "int8"
24
+ : Base["kind"] extends "numeric" | "decimal" ? "numeric"
25
+ : Base["kind"] extends "real" ? "float4"
26
+ : never
27
+ : Base["dialect"] extends "postgres"
28
+ ? Base["kind"] extends "int2" | "int4" | "int8" | "numeric" | "float4" | "float8"
29
+ ? Base["kind"]
30
+ : never
31
+ : never
32
+ : never
33
+
34
+ type AggregateInputError<
35
+ Operation extends "sum" | "avg",
36
+ Db extends Expression.DbType.Any
37
+ > = {
38
+ readonly __effect_qb_error__: "effect-qb: unsupported postgres aggregate input"
39
+ readonly __effect_qb_operation__: Operation
40
+ readonly __effect_qb_db_type__: Db
41
+ readonly __effect_qb_expected__: "smallint, integer, bigint, numeric, real, or double precision"
42
+ readonly __effect_qb_hint__: "Use Cast.to(...) with a supported Type or Pg.Type witness"
43
+ }
44
+
45
+ type PgAggregateConstraint<
46
+ Value extends Numeric.Input,
47
+ Operation extends "sum" | "avg"
48
+ > = IsAny<Value> extends true ? unknown
49
+ : Numeric.DialectConstraint<Value, PgFloat8, "postgres"> & (
50
+ PgAggregateCategory<Numeric.DbTypeOfInput<Value, PgFloat8, "postgres">> extends never
51
+ ? AggregateInputError<
52
+ Operation,
53
+ Numeric.DbTypeOfInput<Value, PgFloat8, "postgres">
54
+ >
55
+ : unknown
56
+ )
57
+
58
+ type PgSumResultDb<Value extends Numeric.Input> =
59
+ PgAggregateCategory<Numeric.DbTypeOfInput<Value, PgFloat8, "postgres">> extends "int2" | "int4"
60
+ ? PgInt8
61
+ : PgAggregateCategory<Numeric.DbTypeOfInput<Value, PgFloat8, "postgres">> extends "int8" | "numeric"
62
+ ? PgNumeric
63
+ : PgAggregateCategory<Numeric.DbTypeOfInput<Value, PgFloat8, "postgres">> extends "float4"
64
+ ? PgFloat4
65
+ : PgFloat8
66
+
67
+ type PgAvgResultDb<Value extends Numeric.Input> =
68
+ PgAggregateCategory<Numeric.DbTypeOfInput<Value, PgFloat8, "postgres">> extends "float4" | "float8"
69
+ ? PgFloat8
70
+ : PgNumeric
71
+
72
+ const baseDb = (db: Expression.DbType.Any): Expression.DbType.Any =>
73
+ "base" in db ? baseDb(db.base) : db
74
+
75
+ const category = (
76
+ value: Numeric.Input
77
+ ): "int2" | "int4" | "int8" | "numeric" | "float4" | "float8" => {
78
+ if (typeof value === "number") return "float8"
79
+ const db = baseDb(value[Expression.TypeId].dbType)
80
+ if (db.dialect === "standard") {
81
+ if (db.kind === "int" || db.kind === "integer") return "int4"
82
+ if (db.kind === "bigint") return "int8"
83
+ if (db.kind === "numeric" || db.kind === "decimal") return "numeric"
84
+ return "float4"
85
+ }
86
+ return db.kind as "int2" | "int4" | "int8" | "numeric" | "float4" | "float8"
87
+ }
88
+
89
+ const sumResultDb = (value: Numeric.Input) => {
90
+ const valueCategory = category(value)
91
+ if (valueCategory === "int2" || valueCategory === "int4") {
92
+ return postgresDatatypes.int8()
93
+ }
94
+ if (valueCategory === "int8" || valueCategory === "numeric") {
95
+ return postgresDatatypes.numeric()
96
+ }
97
+ return valueCategory === "float4"
98
+ ? postgresDatatypes.float4()
99
+ : postgresDatatypes.float8()
100
+ }
101
+
102
+ const avgResultDb = (value: Numeric.Input) => {
103
+ const valueCategory = category(value)
104
+ return valueCategory === "float4" || valueCategory === "float8"
105
+ ? postgresDatatypes.float8()
106
+ : postgresDatatypes.numeric()
107
+ }
108
+
109
+ export const sum = <Value extends Numeric.Input>(
110
+ value: Value & PgAggregateConstraint<NoInfer<Value>, "sum">
111
+ ): Numeric.AggregateResult<"sum", Value, PgFloat8, PgSumResultDb<Value>, "postgres"> =>
112
+ (Numeric.aggregate as any)("sum", value, {
113
+ dialect: "postgres",
114
+ literalDb: postgresDatatypes.float8(),
115
+ resultDb: sumResultDb(value)
116
+ })
117
+
118
+ export const avg = <Value extends Numeric.Input>(
119
+ value: Value & PgAggregateConstraint<NoInfer<Value>, "avg">
120
+ ): Numeric.AggregateResult<"avg", Value, PgFloat8, PgAvgResultDb<Value>, "postgres"> =>
121
+ (Numeric.aggregate as any)("avg", value, {
122
+ dialect: "postgres",
123
+ literalDb: postgresDatatypes.float8(),
124
+ resultDb: avgResultDb(value)
125
+ })
@@ -1,5 +1,6 @@
1
1
  import type { ExpressionInput } from "../query.js"
2
2
  import {
3
+ call,
3
4
  cast,
4
5
  literal,
5
6
  nextVal as nextValInternal,
@@ -9,7 +10,7 @@ import {
9
10
  import { isSequenceDefinition, type SequenceDefinition } from "../schema-management.js"
10
11
 
11
12
  /** Postgres scalar core functions. */
12
- export { uuidGenerateV4 }
13
+ export { call, uuidGenerateV4 }
13
14
 
14
15
  const safeUnquotedIdentifier = /^[a-z_][a-z0-9_$]*$/
15
16
 
@@ -1,3 +1,21 @@
1
1
  export * as core from "./core.js"
2
+ export * as string from "./string.js"
3
+ export * as aggregate from "./aggregate.js"
4
+ export * as numeric from "./numeric.js"
5
+ export * as window from "./window.js"
6
+ export * as temporal from "./temporal.js"
2
7
 
3
- export { uuidGenerateV4, nextVal } from "./core.js"
8
+ export { call, uuidGenerateV4, nextVal } from "./core.js"
9
+ export { lower, upper, concat } from "./string.js"
10
+ export { avg, count, max, min, sum } from "./aggregate.js"
11
+ export { modulo, round } from "./numeric.js"
12
+ export { denseRank, firstValue, lastValue, over, rank, rowNumber } from "./window.js"
13
+ export type { WindowSpec } from "./window.js"
14
+ export {
15
+ currentDate,
16
+ currentTime,
17
+ currentTimestamp,
18
+ localTime,
19
+ localTimestamp,
20
+ now
21
+ } from "./temporal.js"