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
@@ -7,6 +7,8 @@ import * as QueryAst from "../query-ast.js"
7
7
  import { renderDbTypeName, type RenderState, type RenderValueContext, type SqlDialect } from "../dialect.js"
8
8
  import { renderPortableDatatypeCastType, renderPortableDatatypeDdlType } from "../datatypes/matrix.js"
9
9
  import * as ExpressionAst from "../expression-ast.js"
10
+ import { renderCustomSql } from "../custom-sql-renderer.js"
11
+ import { renderWindowFrame } from "../window-renderer.js"
10
12
  import * as JsonPath from "../json/path.js"
11
13
  import { renderMysqlMutationLockMode, renderSelectLockMode } from "../dsl-plan-runtime.js"
12
14
  import { expectConflictClause } from "../dsl-mutation-runtime.js"
@@ -1867,6 +1869,8 @@ export const renderExpression = (
1867
1869
  return `cast(${renderExpression(expectValueExpression("cast", ast.value), state, dialect)} as ${renderCastType(dialect, ast.target)})`
1868
1870
  case "function":
1869
1871
  return renderFunctionCall(ast.name, ast.args, state, dialect)
1872
+ case "customSql":
1873
+ return renderCustomSql(ast, state, dialect, renderExpression)
1870
1874
  case "eq":
1871
1875
  return renderBinaryExpression("eq", "=", ast.left, ast.right, state, dialect)
1872
1876
  case "neq":
@@ -1879,6 +1883,16 @@ export const renderExpression = (
1879
1883
  return renderBinaryExpression("gt", ">", ast.left, ast.right, state, dialect)
1880
1884
  case "gte":
1881
1885
  return renderBinaryExpression("gte", ">=", ast.left, ast.right, state, dialect)
1886
+ case "add":
1887
+ return renderBinaryExpression("add", "+", ast.left, ast.right, state, dialect)
1888
+ case "subtract":
1889
+ return renderBinaryExpression("subtract", "-", ast.left, ast.right, state, dialect)
1890
+ case "multiply":
1891
+ return renderBinaryExpression("multiply", "*", ast.left, ast.right, state, dialect)
1892
+ case "divide":
1893
+ return renderBinaryExpression("divide", "/", ast.left, ast.right, state, dialect)
1894
+ case "modulo":
1895
+ return renderBinaryExpression("modulo", "%", ast.left, ast.right, state, dialect)
1882
1896
  case "like":
1883
1897
  return renderBinaryExpression("like", "like", ast.left, ast.right, state, dialect)
1884
1898
  case "ilike": {
@@ -1983,6 +1997,16 @@ export const renderExpression = (
1983
1997
  return `lower(${renderExpression(expectValueExpression("lower", ast.value), state, dialect)})`
1984
1998
  case "count":
1985
1999
  return `count(${renderExpression(expectValueExpression("count", ast.value), state, dialect)})`
2000
+ case "sum":
2001
+ return `sum(${renderExpression(expectValueExpression("sum", ast.value), state, dialect)})`
2002
+ case "avg":
2003
+ return `avg(${renderExpression(expectValueExpression("avg", ast.value), state, dialect)})`
2004
+ case "abs":
2005
+ return `abs(${renderExpression(expectValueExpression("abs", ast.value), state, dialect)})`
2006
+ case "round":
2007
+ return `round(${renderExpression(expectValueExpression("round", ast.value), state, dialect)})`
2008
+ case "negate":
2009
+ return `(-${renderExpression(expectValueExpression("negate", ast.value), state, dialect)})`
1986
2010
  case "max":
1987
2011
  return `max(${renderExpression(expectValueExpression("max", ast.value), state, dialect)})`
1988
2012
  case "min":
@@ -2021,25 +2045,56 @@ export const renderExpression = (
2021
2045
  readonly value: Expression.Any
2022
2046
  readonly direction: string
2023
2047
  }[]
2024
- const clauses: string[] = []
2025
- if (partitionBy.length > 0) {
2026
- clauses.push(`partition by ${partitionBy.map((value) => renderExpression(value, state, dialect)).join(", ")}`)
2027
- }
2028
- if (orderBy.length > 0) {
2029
- clauses.push(`order by ${orderBy.map((entry) =>
2030
- `${renderExpression(entry.value, state, dialect)} ${entry.direction}`
2031
- ).join(", ")}`)
2048
+ const renderSpecification = (): string => {
2049
+ const clauses: string[] = []
2050
+ if (partitionBy.length > 0) {
2051
+ clauses.push(`partition by ${partitionBy.map((value) => renderExpression(value, state, dialect)).join(", ")}`)
2052
+ }
2053
+ if (orderBy.length > 0) {
2054
+ clauses.push(`order by ${orderBy.map((entry) =>
2055
+ `${renderExpression(entry.value, state, dialect)} ${entry.direction}`
2056
+ ).join(", ")}`)
2057
+ }
2058
+ if (ast.frame !== undefined) {
2059
+ clauses.push(renderWindowFrame(ast.frame))
2060
+ }
2061
+ return clauses.join(" ")
2032
2062
  }
2033
- const specification = clauses.join(" ")
2034
2063
  switch (ast.function) {
2035
2064
  case "rowNumber":
2036
- return `row_number() over (${specification})`
2065
+ return `row_number() over (${renderSpecification()})`
2037
2066
  case "rank":
2038
- return `rank() over (${specification})`
2067
+ return `rank() over (${renderSpecification()})`
2039
2068
  case "denseRank":
2040
- return `dense_rank() over (${specification})`
2069
+ return `dense_rank() over (${renderSpecification()})`
2041
2070
  case "over":
2042
- return `${renderExpression(ast.value as Expression.Any, state, dialect)} over (${specification})`
2071
+ return `${renderExpression(ast.value as Expression.Any, state, dialect)} over (${renderSpecification()})`
2072
+ case "lag":
2073
+ case "lead": {
2074
+ const args = [renderExpression(ast.value as Expression.Any, state, dialect)]
2075
+ if (ast.offset !== undefined) {
2076
+ const offsetAst = (ast.offset as Expression.Any & {
2077
+ readonly [ExpressionAst.TypeId]: ExpressionAst.Any
2078
+ })[ExpressionAst.TypeId]
2079
+ if (
2080
+ offsetAst.kind !== "literal" ||
2081
+ typeof offsetAst.value !== "number" ||
2082
+ !Number.isSafeInteger(offsetAst.value) ||
2083
+ offsetAst.value < 0
2084
+ ) {
2085
+ throw new Error(`${ast.function} offset must be a non-negative integer literal in MySQL`)
2086
+ }
2087
+ args.push(String(offsetAst.value))
2088
+ }
2089
+ if (ast.defaultValue !== undefined) {
2090
+ args.push(renderExpression(ast.defaultValue, state, dialect))
2091
+ }
2092
+ return `${ast.function}(${args.join(", ")}) over (${renderSpecification()})`
2093
+ }
2094
+ case "firstValue":
2095
+ return `first_value(${renderExpression(ast.value as Expression.Any, state, dialect)}) over (${renderSpecification()})`
2096
+ case "lastValue":
2097
+ return `last_value(${renderExpression(ast.value as Expression.Any, state, dialect)}) over (${renderSpecification()})`
2043
2098
  }
2044
2099
  break
2045
2100
  }
@@ -7,6 +7,8 @@ import * as QueryAst from "../query-ast.js"
7
7
  import { renderDbTypeName, type RenderState, type RenderValueContext, type SqlDialect } from "../dialect.js"
8
8
  import { renderPortableDatatypeCastType, renderPortableDatatypeDdlType } from "../datatypes/matrix.js"
9
9
  import * as ExpressionAst from "../expression-ast.js"
10
+ import { renderCustomSql } from "../custom-sql-renderer.js"
11
+ import { renderWindowFrame } from "../window-renderer.js"
10
12
  import * as JsonPath from "../json/path.js"
11
13
  import { renderSelectLockMode } from "../dsl-plan-runtime.js"
12
14
  import { expectConflictClause } from "../dsl-mutation-runtime.js"
@@ -458,6 +460,32 @@ const renderBinaryExpression = (
458
460
  return `(${renderExpression(leftExpression, state, dialect)} ${operator} ${renderExpression(rightExpression, state, dialect)})`
459
461
  }
460
462
 
463
+ const renderNumericExpression = (
464
+ expression: Expression.Any,
465
+ state: RenderState,
466
+ dialect: SqlDialect
467
+ ): string => {
468
+ const rendered = renderExpression(expression, state, dialect)
469
+ const ast = (expression as Expression.Any & {
470
+ readonly [ExpressionAst.TypeId]: ExpressionAst.Any
471
+ })[ExpressionAst.TypeId]
472
+ return dialect.name === "postgres" && ast.kind === "literal"
473
+ ? `cast(${rendered} as ${renderCastType(dialect, expression[Expression.TypeId].dbType)})`
474
+ : rendered
475
+ }
476
+
477
+ const renderNumericBinaryExpression = (
478
+ functionName: string,
479
+ operator: string,
480
+ left: unknown,
481
+ right: unknown,
482
+ state: RenderState,
483
+ dialect: SqlDialect
484
+ ): string => {
485
+ const [leftExpression, rightExpression] = expectBinaryExpressions(functionName, left, right)
486
+ return `(${renderNumericExpression(leftExpression, state, dialect)} ${operator} ${renderNumericExpression(rightExpression, state, dialect)})`
487
+ }
488
+
461
489
  const postgresRangeSubtypeByKind: Readonly<Record<string, string>> = {
462
490
  int4range: "int4",
463
491
  int8range: "int8",
@@ -1873,6 +1901,8 @@ export const renderExpression = (
1873
1901
  return `(${renderExpression(expectValueExpression("collate", ast.value), state, dialect)} collate ${renderCollation(ast.collation)})`
1874
1902
  case "function":
1875
1903
  return renderFunctionCall(ast.name, ast.args, state, dialect)
1904
+ case "customSql":
1905
+ return renderCustomSql(ast, state, dialect, renderExpression)
1876
1906
  case "eq":
1877
1907
  return renderBinaryExpression("eq", "=", ast.left, ast.right, state, dialect)
1878
1908
  case "neq":
@@ -1885,6 +1915,16 @@ export const renderExpression = (
1885
1915
  return renderBinaryExpression("gt", ">", ast.left, ast.right, state, dialect)
1886
1916
  case "gte":
1887
1917
  return renderBinaryExpression("gte", ">=", ast.left, ast.right, state, dialect)
1918
+ case "add":
1919
+ return renderNumericBinaryExpression("add", "+", ast.left, ast.right, state, dialect)
1920
+ case "subtract":
1921
+ return renderNumericBinaryExpression("subtract", "-", ast.left, ast.right, state, dialect)
1922
+ case "multiply":
1923
+ return renderNumericBinaryExpression("multiply", "*", ast.left, ast.right, state, dialect)
1924
+ case "divide":
1925
+ return renderNumericBinaryExpression("divide", "/", ast.left, ast.right, state, dialect)
1926
+ case "modulo":
1927
+ return renderNumericBinaryExpression("modulo", "%", ast.left, ast.right, state, dialect)
1888
1928
  case "like":
1889
1929
  return renderBinaryExpression("like", "like", ast.left, ast.right, state, dialect)
1890
1930
  case "ilike": {
@@ -2004,6 +2044,16 @@ export const renderExpression = (
2004
2044
  return `lower(${renderExpression(expectValueExpression("lower", ast.value), state, dialect)})`
2005
2045
  case "count":
2006
2046
  return `count(${renderExpression(expectValueExpression("count", ast.value), state, dialect)})`
2047
+ case "sum":
2048
+ return `sum(${renderNumericExpression(expectValueExpression("sum", ast.value), state, dialect)})`
2049
+ case "avg":
2050
+ return `avg(${renderNumericExpression(expectValueExpression("avg", ast.value), state, dialect)})`
2051
+ case "abs":
2052
+ return `abs(${renderNumericExpression(expectValueExpression("abs", ast.value), state, dialect)})`
2053
+ case "round":
2054
+ return `round(${renderNumericExpression(expectValueExpression("round", ast.value), state, dialect)})`
2055
+ case "negate":
2056
+ return `(-${renderNumericExpression(expectValueExpression("negate", ast.value), state, dialect)})`
2007
2057
  case "max":
2008
2058
  return `max(${renderExpression(expectValueExpression("max", ast.value), state, dialect)})`
2009
2059
  case "min":
@@ -2054,25 +2104,45 @@ export const renderExpression = (
2054
2104
  readonly value: Expression.Any
2055
2105
  readonly direction: string
2056
2106
  }[]
2057
- const clauses: string[] = []
2058
- if (partitionBy.length > 0) {
2059
- clauses.push(`partition by ${partitionBy.map((value) => renderExpression(value, state, dialect)).join(", ")}`)
2060
- }
2061
- if (orderBy.length > 0) {
2062
- clauses.push(`order by ${orderBy.map((entry) =>
2063
- `${renderExpression(entry.value, state, dialect)} ${entry.direction}`
2064
- ).join(", ")}`)
2107
+ const renderSpecification = (): string => {
2108
+ const clauses: string[] = []
2109
+ if (partitionBy.length > 0) {
2110
+ clauses.push(`partition by ${partitionBy.map((value) => renderExpression(value, state, dialect)).join(", ")}`)
2111
+ }
2112
+ if (orderBy.length > 0) {
2113
+ clauses.push(`order by ${orderBy.map((entry) =>
2114
+ `${renderExpression(entry.value, state, dialect)} ${entry.direction}`
2115
+ ).join(", ")}`)
2116
+ }
2117
+ if (ast.frame !== undefined) {
2118
+ clauses.push(renderWindowFrame(ast.frame))
2119
+ }
2120
+ return clauses.join(" ")
2065
2121
  }
2066
- const specification = clauses.join(" ")
2067
2122
  switch (ast.function) {
2068
2123
  case "rowNumber":
2069
- return `row_number() over (${specification})`
2124
+ return `row_number() over (${renderSpecification()})`
2070
2125
  case "rank":
2071
- return `rank() over (${specification})`
2126
+ return `rank() over (${renderSpecification()})`
2072
2127
  case "denseRank":
2073
- return `dense_rank() over (${specification})`
2128
+ return `dense_rank() over (${renderSpecification()})`
2074
2129
  case "over":
2075
- return `${renderExpression(ast.value as Expression.Any, state, dialect)} over (${specification})`
2130
+ return `${renderExpression(ast.value as Expression.Any, state, dialect)} over (${renderSpecification()})`
2131
+ case "lag":
2132
+ case "lead": {
2133
+ const args = [renderExpression(ast.value as Expression.Any, state, dialect)]
2134
+ if (ast.offset !== undefined) {
2135
+ args.push(renderExpression(ast.offset, state, dialect))
2136
+ }
2137
+ if (ast.defaultValue !== undefined) {
2138
+ args.push(renderExpression(ast.defaultValue, state, dialect))
2139
+ }
2140
+ return `${ast.function}(${args.join(", ")}) over (${renderSpecification()})`
2141
+ }
2142
+ case "firstValue":
2143
+ return `first_value(${renderExpression(ast.value as Expression.Any, state, dialect)}) over (${renderSpecification()})`
2144
+ case "lastValue":
2145
+ return `last_value(${renderExpression(ast.value as Expression.Any, state, dialect)}) over (${renderSpecification()})`
2076
2146
  }
2077
2147
  break
2078
2148
  }
@@ -7,6 +7,8 @@ import * as QueryAst from "../query-ast.js"
7
7
  import { renderDbTypeName, type RenderState, type RenderValueContext, type SqlDialect } from "../dialect.js"
8
8
  import { renderPortableDatatypeCastType, renderPortableDatatypeDdlType } from "../datatypes/matrix.js"
9
9
  import * as ExpressionAst from "../expression-ast.js"
10
+ import { renderCustomSql } from "../custom-sql-renderer.js"
11
+ import { renderWindowFrame } from "../window-renderer.js"
10
12
  import * as JsonPath from "../json/path.js"
11
13
  import { expectConflictClause } from "../dsl-mutation-runtime.js"
12
14
  import { expectDdlClauseKind, normalizeStatementFlag, normalizeStatementIdentifier } from "../dsl-transaction-ddl-runtime.js"
@@ -1795,6 +1797,8 @@ export const renderExpression = (
1795
1797
  return `cast(${renderExpression(expectValueExpression("cast", ast.value), state, dialect)} as ${renderCastType(dialect, ast.target)})`
1796
1798
  case "function":
1797
1799
  return renderFunctionCall(ast.name, ast.args, state, dialect)
1800
+ case "customSql":
1801
+ return renderCustomSql(ast, state, dialect, renderExpression)
1798
1802
  case "eq":
1799
1803
  return renderBinaryExpression("eq", "=", ast.left, ast.right, state, dialect)
1800
1804
  case "neq":
@@ -1807,6 +1811,16 @@ export const renderExpression = (
1807
1811
  return renderBinaryExpression("gt", ">", ast.left, ast.right, state, dialect)
1808
1812
  case "gte":
1809
1813
  return renderBinaryExpression("gte", ">=", ast.left, ast.right, state, dialect)
1814
+ case "add":
1815
+ return renderBinaryExpression("add", "+", ast.left, ast.right, state, dialect)
1816
+ case "subtract":
1817
+ return renderBinaryExpression("subtract", "-", ast.left, ast.right, state, dialect)
1818
+ case "multiply":
1819
+ return renderBinaryExpression("multiply", "*", ast.left, ast.right, state, dialect)
1820
+ case "divide":
1821
+ return renderBinaryExpression("divide", "/", ast.left, ast.right, state, dialect)
1822
+ case "modulo":
1823
+ return renderBinaryExpression("modulo", "%", ast.left, ast.right, state, dialect)
1810
1824
  case "like":
1811
1825
  return renderBinaryExpression("like", "like", ast.left, ast.right, state, dialect)
1812
1826
  case "ilike": {
@@ -1902,6 +1916,16 @@ export const renderExpression = (
1902
1916
  return `lower(${renderExpression(expectValueExpression("lower", ast.value), state, dialect)})`
1903
1917
  case "count":
1904
1918
  return `count(${renderExpression(expectValueExpression("count", ast.value), state, dialect)})`
1919
+ case "sum":
1920
+ return `sum(${renderExpression(expectValueExpression("sum", ast.value), state, dialect)})`
1921
+ case "avg":
1922
+ return `avg(${renderExpression(expectValueExpression("avg", ast.value), state, dialect)})`
1923
+ case "abs":
1924
+ return `abs(${renderExpression(expectValueExpression("abs", ast.value), state, dialect)})`
1925
+ case "round":
1926
+ return `round(${renderExpression(expectValueExpression("round", ast.value), state, dialect)})`
1927
+ case "negate":
1928
+ return `(-${renderExpression(expectValueExpression("negate", ast.value), state, dialect)})`
1905
1929
  case "max":
1906
1930
  return `max(${renderExpression(expectValueExpression("max", ast.value), state, dialect)})`
1907
1931
  case "min":
@@ -1952,25 +1976,45 @@ export const renderExpression = (
1952
1976
  readonly value: Expression.Any
1953
1977
  readonly direction: string
1954
1978
  }[]
1955
- const clauses: string[] = []
1956
- if (partitionBy.length > 0) {
1957
- clauses.push(`partition by ${partitionBy.map((value) => renderExpression(value, state, dialect)).join(", ")}`)
1958
- }
1959
- if (orderBy.length > 0) {
1960
- clauses.push(`order by ${orderBy.map((entry) =>
1961
- `${renderExpression(entry.value, state, dialect)} ${entry.direction}`
1962
- ).join(", ")}`)
1979
+ const renderSpecification = (): string => {
1980
+ const clauses: string[] = []
1981
+ if (partitionBy.length > 0) {
1982
+ clauses.push(`partition by ${partitionBy.map((value) => renderExpression(value, state, dialect)).join(", ")}`)
1983
+ }
1984
+ if (orderBy.length > 0) {
1985
+ clauses.push(`order by ${orderBy.map((entry) =>
1986
+ `${renderExpression(entry.value, state, dialect)} ${entry.direction}`
1987
+ ).join(", ")}`)
1988
+ }
1989
+ if (ast.frame !== undefined) {
1990
+ clauses.push(renderWindowFrame(ast.frame))
1991
+ }
1992
+ return clauses.join(" ")
1963
1993
  }
1964
- const specification = clauses.join(" ")
1965
1994
  switch (ast.function) {
1966
1995
  case "rowNumber":
1967
- return `row_number() over (${specification})`
1996
+ return `row_number() over (${renderSpecification()})`
1968
1997
  case "rank":
1969
- return `rank() over (${specification})`
1998
+ return `rank() over (${renderSpecification()})`
1970
1999
  case "denseRank":
1971
- return `dense_rank() over (${specification})`
2000
+ return `dense_rank() over (${renderSpecification()})`
1972
2001
  case "over":
1973
- return `${renderExpression(ast.value as Expression.Any, state, dialect)} over (${specification})`
2002
+ return `${renderExpression(ast.value as Expression.Any, state, dialect)} over (${renderSpecification()})`
2003
+ case "lag":
2004
+ case "lead": {
2005
+ const args = [renderExpression(ast.value as Expression.Any, state, dialect)]
2006
+ if (ast.offset !== undefined) {
2007
+ args.push(renderExpression(ast.offset, state, dialect))
2008
+ }
2009
+ if (ast.defaultValue !== undefined) {
2010
+ args.push(renderExpression(ast.defaultValue, state, dialect))
2011
+ }
2012
+ return `${ast.function}(${args.join(", ")}) over (${renderSpecification()})`
2013
+ }
2014
+ case "firstValue":
2015
+ return `first_value(${renderExpression(ast.value as Expression.Any, state, dialect)}) over (${renderSpecification()})`
2016
+ case "lastValue":
2017
+ return `last_value(${renderExpression(ast.value as Expression.Any, state, dialect)}) over (${renderSpecification()})`
1974
2018
  }
1975
2019
  break
1976
2020
  }
@@ -0,0 +1,131 @@
1
+ import * as Schema from "effect/Schema"
2
+
3
+ import { standardDatatypes } from "../standard/datatypes/index.js"
4
+ import * as ExpressionAst from "./expression-ast.js"
5
+ import * as Expression from "./scalar.js"
6
+ import {
7
+ makeExpression,
8
+ mergeManyDependencies,
9
+ mergeNullabilityManyRuntime,
10
+ type MergeNullabilityTuple,
11
+ type PredicateInput,
12
+ type TupleDependencies,
13
+ type TupleDialect
14
+ } from "./query.js"
15
+
16
+ type BooleanLiteral<Value extends boolean> = Expression.Scalar<
17
+ Value,
18
+ ReturnType<typeof standardDatatypes.boolean>,
19
+ "never",
20
+ "standard",
21
+ "scalar",
22
+ never
23
+ > & {
24
+ readonly [ExpressionAst.TypeId]: ExpressionAst.LiteralNode<Value>
25
+ }
26
+
27
+ type AsPredicate<Value extends PredicateInput> =
28
+ Value extends Expression.Any ? Value : BooleanLiteral<Extract<Value, boolean>>
29
+
30
+ type PredicateTuple<Values extends readonly PredicateInput[]> = {
31
+ readonly [Key in keyof Values]: AsPredicate<Values[Key]>
32
+ }
33
+
34
+ type CombinedPredicate<
35
+ Kind extends "and" | "or",
36
+ Values extends readonly PredicateInput[]
37
+ > = Values extends readonly [] ? BooleanLiteral<Kind extends "and" ? true : false> : Expression.Scalar<
38
+ boolean,
39
+ ReturnType<typeof standardDatatypes.boolean>,
40
+ MergeNullabilityTuple<PredicateTuple<Values>>,
41
+ TupleDialect<PredicateTuple<Values>>,
42
+ "scalar",
43
+ TupleDependencies<PredicateTuple<Values>>
44
+ > & {
45
+ readonly [ExpressionAst.TypeId]: ExpressionAst.VariadicNode<Kind, PredicateTuple<Values>>
46
+ }
47
+
48
+ const booleanLiteral = <const Value extends boolean>(
49
+ value: Value
50
+ ): BooleanLiteral<Value> => makeExpression({
51
+ runtime: value,
52
+ dbType: standardDatatypes.boolean(),
53
+ runtimeSchema: Schema.Boolean,
54
+ nullability: "never",
55
+ dialect: "standard",
56
+ kind: "scalar",
57
+ dependencies: {}
58
+ }, {
59
+ kind: "literal",
60
+ value
61
+ })
62
+
63
+ const combine = <
64
+ Kind extends "and" | "or",
65
+ const Values extends readonly PredicateInput[]
66
+ >(
67
+ kind: Kind,
68
+ values: Values,
69
+ identity: boolean
70
+ ): CombinedPredicate<Kind, Values> => {
71
+ if (values.length === 0) {
72
+ return booleanLiteral(identity) as unknown as CombinedPredicate<Kind, Values>
73
+ }
74
+ const expressions = values.map((value) =>
75
+ typeof value === "boolean" ? booleanLiteral(value) : value) as PredicateTuple<Values>
76
+ return makeExpression({
77
+ runtime: identity,
78
+ dbType: standardDatatypes.boolean(),
79
+ runtimeSchema: Schema.Boolean,
80
+ nullability: mergeNullabilityManyRuntime(expressions),
81
+ dialect: expressions.find((value) => value[Expression.TypeId].dialect !== "standard")?.[Expression.TypeId].dialect ??
82
+ expressions[0]![Expression.TypeId].dialect,
83
+ kind: "scalar",
84
+ dependencies: mergeManyDependencies(expressions)
85
+ }, {
86
+ kind,
87
+ values: expressions
88
+ }) as CombinedPredicate<Kind, Values>
89
+ }
90
+
91
+ /** Combines a runtime list of predicates; an empty list is SQL true. */
92
+ export const andAll = <
93
+ const Values extends readonly PredicateInput[]
94
+ >(values: Values): CombinedPredicate<"and", Values> =>
95
+ combine("and", values, true)
96
+
97
+ /** Combines a runtime list of predicates; an empty list is SQL false. */
98
+ export const orAll = <
99
+ const Values extends readonly PredicateInput[]
100
+ >(values: Values): CombinedPredicate<"or", Values> =>
101
+ combine("or", values, false)
102
+
103
+ /** Applies a query modifier only when the condition is true. */
104
+ export const when = <
105
+ const Condition extends boolean,
106
+ Modifier extends (value: any) => any
107
+ >(
108
+ condition: Condition,
109
+ modifier: Modifier
110
+ ) => <Value>(
111
+ value: Value
112
+ ): Condition extends true ? ReturnType<Modifier> : Condition extends false ? Value : Value | ReturnType<Modifier> =>
113
+ (condition ? modifier(value) : value) as never
114
+
115
+ /**
116
+ * Returns a selection fragment suitable for object spread.
117
+ *
118
+ * Dynamic conditions correctly make the selected fields optional.
119
+ */
120
+ export const includeIf = <
121
+ const Condition extends boolean,
122
+ const Selection extends Readonly<Record<string, unknown>>
123
+ >(
124
+ condition: Condition,
125
+ selection: Selection
126
+ ): Condition extends true
127
+ ? Selection
128
+ : Condition extends false
129
+ ? {}
130
+ : Partial<Selection> =>
131
+ (condition ? selection : {}) as never