effect-qb 0.13.0 → 0.15.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 (146) hide show
  1. package/README.md +6 -1431
  2. package/dist/mysql.js +61945 -3611
  3. package/dist/postgres/metadata.js +2818 -0
  4. package/dist/postgres.js +9942 -5591
  5. package/package.json +21 -10
  6. package/src/internal/aggregation-validation.ts +3 -3
  7. package/src/internal/case-analysis.d.ts +18 -0
  8. package/src/internal/case-analysis.ts +4 -4
  9. package/src/internal/coercion/analysis.d.ts +7 -0
  10. package/src/internal/{coercion-analysis.ts → coercion/analysis.ts} +3 -3
  11. package/src/internal/coercion/errors.d.ts +17 -0
  12. package/src/internal/{coercion-errors.ts → coercion/errors.ts} +1 -1
  13. package/src/internal/coercion/kind.d.ts +4 -0
  14. package/src/internal/{coercion-kind.ts → coercion/kind.ts} +2 -2
  15. package/src/internal/{coercion-normalize.ts → coercion/normalize.ts} +1 -1
  16. package/src/internal/coercion/rules.d.ts +6 -0
  17. package/src/internal/{coercion-rules.ts → coercion/rules.ts} +2 -2
  18. package/src/internal/column-state.d.ts +190 -0
  19. package/src/internal/column-state.ts +119 -56
  20. package/src/internal/column.ts +387 -149
  21. package/src/internal/datatypes/define.d.ts +17 -0
  22. package/src/internal/datatypes/define.ts +18 -34
  23. package/src/internal/datatypes/lookup.d.ts +44 -0
  24. package/src/internal/datatypes/lookup.ts +61 -152
  25. package/src/internal/datatypes/shape.d.ts +16 -0
  26. package/src/internal/datatypes/shape.ts +1 -1
  27. package/src/internal/derived-table.d.ts +4 -0
  28. package/src/internal/derived-table.ts +21 -16
  29. package/src/internal/dsl-mutation-runtime.ts +378 -0
  30. package/src/internal/dsl-plan-runtime.ts +387 -0
  31. package/src/internal/dsl-query-runtime.ts +160 -0
  32. package/src/internal/dsl-transaction-ddl-runtime.ts +263 -0
  33. package/src/internal/executor.ts +173 -38
  34. package/src/internal/expression-ast.ts +19 -5
  35. package/src/internal/grouping-key.d.ts +3 -0
  36. package/src/internal/grouping-key.ts +1 -1
  37. package/src/internal/implication-runtime.d.ts +15 -0
  38. package/src/internal/implication-runtime.ts +171 -0
  39. package/src/internal/json/ast.d.ts +30 -0
  40. package/src/internal/json/ast.ts +1 -1
  41. package/src/internal/json/errors.d.ts +8 -0
  42. package/src/internal/json/path.d.ts +75 -0
  43. package/src/internal/json/path.ts +1 -1
  44. package/src/internal/json/types.d.ts +62 -0
  45. package/src/internal/predicate/analysis.d.ts +20 -0
  46. package/src/internal/{predicate-analysis.ts → predicate/analysis.ts} +13 -3
  47. package/src/internal/predicate/atom.d.ts +28 -0
  48. package/src/internal/{predicate-branches.ts → predicate/branches.ts} +2 -2
  49. package/src/internal/predicate/context.d.ts +67 -0
  50. package/src/internal/{predicate-context.ts → predicate/context.ts} +111 -32
  51. package/src/internal/predicate/formula.d.ts +35 -0
  52. package/src/internal/{predicate-formula.ts → predicate/formula.ts} +32 -20
  53. package/src/internal/predicate/key.d.ts +11 -0
  54. package/src/internal/{predicate-key.ts → predicate/key.ts} +2 -2
  55. package/src/internal/{predicate-nnf.ts → predicate/nnf.ts} +2 -2
  56. package/src/internal/predicate/normalize.d.ts +53 -0
  57. package/src/internal/predicate/normalize.ts +273 -0
  58. package/src/internal/predicate/runtime.d.ts +31 -0
  59. package/src/internal/predicate/runtime.ts +679 -0
  60. package/src/internal/projection-alias.d.ts +13 -0
  61. package/src/internal/projections.d.ts +31 -0
  62. package/src/internal/projections.ts +1 -1
  63. package/src/internal/query-ast.d.ts +217 -0
  64. package/src/internal/query-ast.ts +1 -1
  65. package/src/internal/query-requirements.d.ts +20 -0
  66. package/src/internal/query.d.ts +775 -0
  67. package/src/internal/query.ts +767 -275
  68. package/src/internal/renderer.ts +7 -21
  69. package/src/internal/row-set.d.ts +53 -0
  70. package/src/internal/{plan.ts → row-set.ts} +23 -11
  71. package/src/internal/{runtime-normalize.ts → runtime/normalize.ts} +9 -31
  72. package/src/internal/{runtime-schema.ts → runtime/schema.ts} +84 -55
  73. package/src/internal/runtime/value.d.ts +22 -0
  74. package/src/internal/{runtime-value.ts → runtime/value.ts} +2 -2
  75. package/src/internal/scalar.d.ts +107 -0
  76. package/src/internal/scalar.ts +191 -0
  77. package/src/internal/schema-derivation.d.ts +105 -0
  78. package/src/internal/schema-derivation.ts +93 -21
  79. package/src/internal/schema-expression.d.ts +18 -0
  80. package/src/internal/schema-expression.ts +75 -0
  81. package/src/internal/table-options.d.ts +94 -0
  82. package/src/internal/table-options.ts +94 -8
  83. package/src/internal/table.d.ts +173 -0
  84. package/src/internal/table.ts +135 -54
  85. package/src/mysql/column.ts +95 -18
  86. package/src/mysql/datatypes/index.ts +58 -3
  87. package/src/mysql/errors/generated.ts +57336 -0
  88. package/src/mysql/errors/index.ts +1 -0
  89. package/src/mysql/errors/normalize.ts +55 -53
  90. package/src/mysql/errors/types.ts +74 -0
  91. package/src/mysql/executor.ts +69 -7
  92. package/src/mysql/function/aggregate.ts +1 -5
  93. package/src/mysql/function/core.ts +1 -3
  94. package/src/mysql/function/index.ts +1 -1
  95. package/src/mysql/function/string.ts +1 -5
  96. package/src/mysql/function/temporal.ts +12 -15
  97. package/src/mysql/function/window.ts +1 -6
  98. package/src/{internal/mysql-dialect.ts → mysql/internal/dialect.ts} +1 -1
  99. package/src/mysql/internal/dsl.ts +6115 -0
  100. package/src/{internal/mysql-renderer.ts → mysql/internal/renderer.ts} +6 -6
  101. package/src/mysql/internal/sql-expression-renderer.ts +1455 -0
  102. package/src/mysql/json.ts +2 -0
  103. package/src/mysql/query.ts +111 -86
  104. package/src/mysql/renderer.ts +1 -1
  105. package/src/mysql/table.ts +1 -1
  106. package/src/mysql.ts +6 -4
  107. package/src/postgres/cast.ts +30 -0
  108. package/src/postgres/column.ts +178 -20
  109. package/src/postgres/datatypes/index.d.ts +515 -0
  110. package/src/postgres/datatypes/index.ts +49 -5
  111. package/src/postgres/datatypes/spec.d.ts +412 -0
  112. package/src/postgres/errors/generated.ts +2636 -0
  113. package/src/postgres/errors/index.ts +1 -0
  114. package/src/postgres/errors/normalize.ts +47 -62
  115. package/src/postgres/errors/types.ts +92 -34
  116. package/src/postgres/executor.ts +37 -5
  117. package/src/postgres/function/aggregate.ts +1 -5
  118. package/src/postgres/function/core.ts +20 -2
  119. package/src/postgres/function/index.ts +1 -1
  120. package/src/postgres/function/string.ts +1 -5
  121. package/src/postgres/function/temporal.ts +12 -15
  122. package/src/postgres/function/window.ts +1 -6
  123. package/src/{internal/postgres-dialect.ts → postgres/internal/dialect.ts} +1 -1
  124. package/src/{internal/query-factory.ts → postgres/internal/dsl.ts} +1568 -2120
  125. package/src/{internal/postgres-renderer.ts → postgres/internal/renderer.ts} +6 -6
  126. package/src/postgres/internal/schema-ddl.ts +108 -0
  127. package/src/postgres/internal/schema-model.ts +150 -0
  128. package/src/{internal → postgres/internal}/sql-expression-renderer.ts +112 -46
  129. package/src/postgres/json.ts +493 -0
  130. package/src/postgres/metadata.ts +31 -0
  131. package/src/postgres/query.ts +113 -86
  132. package/src/postgres/renderer.ts +3 -13
  133. package/src/postgres/schema-expression.ts +17 -0
  134. package/src/postgres/schema-management.ts +204 -0
  135. package/src/postgres/schema.ts +35 -0
  136. package/src/postgres/table.ts +316 -42
  137. package/src/postgres/type.ts +31 -0
  138. package/src/postgres.ts +20 -4
  139. package/CHANGELOG.md +0 -134
  140. package/src/internal/expression.ts +0 -327
  141. package/src/internal/predicate-normalize.ts +0 -202
  142. package/src/mysql/function/json.ts +0 -4
  143. package/src/mysql/private/query.ts +0 -13
  144. package/src/postgres/function/json.ts +0 -4
  145. package/src/postgres/private/query.ts +0 -13
  146. /package/src/internal/{predicate-atom.ts → predicate/atom.ts} +0 -0
@@ -1,7 +1,7 @@
1
- import * as Query from "./query.js"
2
- import { type RenderState } from "./dialect.js"
3
- import { postgresDialect } from "./postgres-dialect.js"
4
- import { type Projection } from "./projections.js"
1
+ import * as Query from "../../internal/query.js"
2
+ import { type RenderState } from "../../internal/dialect.js"
3
+ import { postgresDialect } from "./dialect.js"
4
+ import { type Projection } from "../../internal/projections.js"
5
5
  import { renderQueryAst } from "./sql-expression-renderer.js"
6
6
 
7
7
  /**
@@ -19,7 +19,7 @@ export interface PostgresRenderResult {
19
19
  /**
20
20
  * Renders the current query AST into Postgres SQL plus bind parameters.
21
21
  */
22
- export const renderPostgresPlan = <PlanValue extends Query.QueryPlan<any, any, any, any, any, any, any, any, any>>(
22
+ export const renderPostgresPlan = <PlanValue extends Query.Plan.Any>(
23
23
  plan: Query.DialectCompatiblePlan<PlanValue, "postgres">
24
24
  ): PostgresRenderResult => {
25
25
  const state: RenderState = {
@@ -28,7 +28,7 @@ export const renderPostgresPlan = <PlanValue extends Query.QueryPlan<any, any, a
28
28
  cteNames: new Set<string>()
29
29
  }
30
30
  const rendered = renderQueryAst(
31
- Query.getAst(plan as Query.QueryPlan<any, any, any, any, any, any, any, any, any>) as any,
31
+ Query.getAst(plan as Query.Plan.Any) as any,
32
32
  state,
33
33
  postgresDialect
34
34
  )
@@ -0,0 +1,108 @@
1
+ import type * as Expression from "../../internal/scalar.js"
2
+ import type { RenderState, SqlDialect } from "../../internal/dialect.js"
3
+ import * as SchemaExpression from "../../internal/schema-expression.js"
4
+ import { renderExpression } from "./sql-expression-renderer.js"
5
+ import type { DdlExpressionLike } from "../../internal/table-options.js"
6
+ import { parse, toSql } from "pgsql-ast-parser"
7
+ import { postgresDialect } from "./dialect.js"
8
+
9
+ export const renderDdlExpression = (
10
+ expression: DdlExpressionLike,
11
+ state: RenderState,
12
+ dialect: SqlDialect
13
+ ): string =>
14
+ SchemaExpression.isSchemaExpression(expression)
15
+ ? SchemaExpression.render(expression)
16
+ : renderExpression(expression as Expression.Any, state, dialect)
17
+
18
+ const escapeString = (value: string): string => `'${value.replaceAll("'", "''")}'`
19
+
20
+ const inlineLiteralDialect: SqlDialect<"postgres"> = {
21
+ ...postgresDialect,
22
+ renderLiteral(value) {
23
+ if (value === null) {
24
+ return "null"
25
+ }
26
+ if (typeof value === "boolean") {
27
+ return value ? "true" : "false"
28
+ }
29
+ if (typeof value === "number" || typeof value === "bigint") {
30
+ return String(value)
31
+ }
32
+ if (value instanceof Date) {
33
+ return escapeString(value.toISOString())
34
+ }
35
+ return escapeString(String(value))
36
+ }
37
+ }
38
+
39
+ export const renderDdlExpressionSql = (expression: DdlExpressionLike): string =>
40
+ SchemaExpression.isSchemaExpression(expression)
41
+ ? SchemaExpression.render(expression)
42
+ : renderExpression(expression as Expression.Any, {
43
+ params: [],
44
+ ctes: [],
45
+ cteNames: new Set()
46
+ }, inlineLiteralDialect)
47
+
48
+ const stripRedundantOuterParens = (value: string): string => {
49
+ let current = value.trim()
50
+ while (current.startsWith("(") && current.endsWith(")")) {
51
+ let depth = 0
52
+ let wrapsWholeExpression = true
53
+ let inSingleQuote = false
54
+ let inDoubleQuote = false
55
+ for (let index = 0; index < current.length; index++) {
56
+ const char = current[index]!
57
+ const previous = index > 0 ? current[index - 1] : undefined
58
+ if (char === "'" && !inDoubleQuote && previous !== "\\") {
59
+ inSingleQuote = !inSingleQuote
60
+ continue
61
+ }
62
+ if (char === "\"" && !inSingleQuote && previous !== "\\") {
63
+ inDoubleQuote = !inDoubleQuote
64
+ continue
65
+ }
66
+ if (inSingleQuote || inDoubleQuote) {
67
+ continue
68
+ }
69
+ if (char === "(") {
70
+ depth += 1
71
+ } else if (char === ")") {
72
+ depth -= 1
73
+ if (depth === 0 && index < current.length - 1) {
74
+ wrapsWholeExpression = false
75
+ break
76
+ }
77
+ }
78
+ }
79
+ if (!wrapsWholeExpression) {
80
+ break
81
+ }
82
+ current = current.slice(1, -1).trim()
83
+ }
84
+ return current
85
+ }
86
+
87
+ const canonicalizeDdlExpressionSql = (value: string): string =>
88
+ stripRedundantOuterParens(
89
+ value
90
+ .trim()
91
+ .replace(/\s+/g, " ")
92
+ .replace(/"[^"]+"\./g, "")
93
+ .replace(/"([A-Za-z_][A-Za-z0-9_]*)"/g, "$1")
94
+ .replace(/\bCOLLATE\b/g, "collate")
95
+ .replace(
96
+ /cast\(((?:'(?:[^']|'')*'|"[^"]+"|[a-zA-Z_][a-zA-Z0-9_]*|\([^()]+\))) as ([^)]+)\)/gi,
97
+ (_, expression: string, target: string) => `${expression}::${target.trim()}`
98
+ )
99
+ )
100
+
101
+ export const normalizeDdlExpressionSql = (expression: DdlExpressionLike): string => {
102
+ const rendered = renderDdlExpressionSql(expression)
103
+ try {
104
+ return canonicalizeDdlExpressionSql(toSql.expr(parse(rendered, "expr")))
105
+ } catch {
106
+ return canonicalizeDdlExpressionSql(rendered)
107
+ }
108
+ }
@@ -0,0 +1,150 @@
1
+ import * as Table from "../../internal/table.js"
2
+ import type { AnyColumnDefinition } from "../../internal/column-state.js"
3
+ import { normalizeDdlExpressionSql } from "./schema-ddl.js"
4
+ import type { TableOptionSpec } from "../../internal/table-options.js"
5
+ import type { EnumDefinition } from "../schema-management.js"
6
+ import { EnumTypeId } from "../schema-management.js"
7
+
8
+ export interface EnumModel {
9
+ readonly kind: "enum"
10
+ readonly schemaName?: string
11
+ readonly name: string
12
+ readonly values: readonly string[]
13
+ }
14
+
15
+ export interface ColumnModel {
16
+ readonly name: string
17
+ readonly ddlType: string
18
+ readonly dbTypeKind: string
19
+ readonly typeKind?: string
20
+ readonly typeSchema?: string
21
+ readonly nullable: boolean
22
+ readonly hasDefault: boolean
23
+ readonly generated: boolean
24
+ readonly defaultSql?: string
25
+ readonly generatedSql?: string
26
+ readonly identity?: {
27
+ readonly generation: "always" | "byDefault"
28
+ }
29
+ readonly column?: AnyColumnDefinition
30
+ }
31
+
32
+ export interface TableModel {
33
+ readonly kind: "table"
34
+ readonly schemaName?: string
35
+ readonly name: string
36
+ readonly columns: readonly ColumnModel[]
37
+ readonly options: readonly TableOptionSpec[]
38
+ readonly table?: Table.AnyTable
39
+ }
40
+
41
+ export interface SchemaModel {
42
+ readonly dialect: "postgres"
43
+ readonly enums: readonly EnumModel[]
44
+ readonly tables: readonly TableModel[]
45
+ }
46
+
47
+ export const isTableDefinition = (value: unknown): value is Table.AnyTable =>
48
+ value !== null &&
49
+ (typeof value === "object" || typeof value === "function") &&
50
+ Table.TypeId in value
51
+
52
+ export const isEnumDefinition = (value: unknown): value is EnumDefinition =>
53
+ typeof value === "object" && value !== null && EnumTypeId in value
54
+
55
+ export const toTableModel = (table: Table.AnyTable): TableModel => {
56
+ const state = table[Table.TypeId]
57
+ const fields = state.fields as Record<string, AnyColumnDefinition>
58
+ const columns = Object.entries(fields).map(([name, column]) => {
59
+ const metadata = column.metadata
60
+ const enumDefinition = metadata.enum
61
+ const ddlType = metadata.ddlType ?? metadata.dbType.kind
62
+ return {
63
+ name,
64
+ ddlType,
65
+ dbTypeKind: enumDefinition?.name ?? column.metadata.dbType.kind,
66
+ typeKind: enumDefinition === undefined ? undefined : "e",
67
+ typeSchema: enumDefinition?.schemaName,
68
+ nullable: column.metadata.nullable,
69
+ hasDefault: column.metadata.hasDefault,
70
+ generated: column.metadata.generated,
71
+ defaultSql: column.metadata.defaultValue === undefined
72
+ ? undefined
73
+ : normalizeDdlExpressionSql(column.metadata.defaultValue),
74
+ generatedSql: column.metadata.generatedValue === undefined
75
+ ? undefined
76
+ : normalizeDdlExpressionSql(column.metadata.generatedValue),
77
+ identity: column.metadata.identity,
78
+ column
79
+ }
80
+ }) satisfies ReadonlyArray<ColumnModel>
81
+ return {
82
+ kind: "table",
83
+ schemaName: state.schemaName,
84
+ name: state.baseName,
85
+ columns,
86
+ options: table[Table.OptionsSymbol],
87
+ table
88
+ }
89
+ }
90
+
91
+ export const toEnumModel = <
92
+ Name extends string,
93
+ Values extends readonly [string, ...string[]],
94
+ SchemaName extends string | undefined
95
+ >(
96
+ definition: EnumDefinition<Name, Values, SchemaName>
97
+ ): EnumModel => ({
98
+ kind: "enum",
99
+ schemaName: definition.schemaName,
100
+ name: definition.name,
101
+ values: [...definition.values]
102
+ })
103
+
104
+ const enumModelsOfTable = (table: Table.AnyTable): readonly EnumModel[] => {
105
+ const state = table[Table.TypeId]
106
+ const fields = state.fields as Record<string, AnyColumnDefinition>
107
+ return Object.values(fields)
108
+ .flatMap((column) => column.metadata.enum === undefined
109
+ ? []
110
+ : [{
111
+ kind: "enum" as const,
112
+ schemaName: column.metadata.enum.schemaName,
113
+ name: column.metadata.enum.name,
114
+ values: [...column.metadata.enum.values]
115
+ } satisfies EnumModel
116
+ ])
117
+ }
118
+
119
+ export const fromDiscoveredValues = (values: ReadonlyArray<unknown>): SchemaModel => {
120
+ const tables = values.filter(isTableDefinition).map(toTableModel)
121
+ const enums = new Map<string, EnumModel>()
122
+ for (const value of values) {
123
+ if (isEnumDefinition(value)) {
124
+ enums.set(enumKey(value.schemaName, value.name), toEnumModel(value))
125
+ } else if (isTableDefinition(value)) {
126
+ for (const enumModel of enumModelsOfTable(value)) {
127
+ const key = enumKey(enumModel.schemaName, enumModel.name)
128
+ const existing = enums.get(key)
129
+ if (existing === undefined) {
130
+ enums.set(key, enumModel)
131
+ continue
132
+ }
133
+ if (JSON.stringify(existing.values) !== JSON.stringify(enumModel.values)) {
134
+ throw new Error(`Conflicting enum definitions discovered for '${key}'`)
135
+ }
136
+ }
137
+ }
138
+ }
139
+ return {
140
+ dialect: "postgres",
141
+ enums: [...enums.values()],
142
+ tables
143
+ }
144
+ }
145
+
146
+ export const tableKey = (schemaName: string | undefined, name: string): string =>
147
+ `${schemaName ?? "public"}.${name}`
148
+
149
+ export const enumKey = (schemaName: string | undefined, name: string): string =>
150
+ `${schemaName ?? "public"}.${name}`
@@ -1,12 +1,14 @@
1
- import * as Query from "./query.js"
2
- import * as Expression from "./expression.js"
3
- import * as Table from "./table.js"
4
- import * as QueryAst from "./query-ast.js"
5
- import type { RenderState, SqlDialect } from "./dialect.js"
6
- import * as ExpressionAst from "./expression-ast.js"
7
- import * as JsonPath from "./json/path.js"
8
- import { flattenSelection, type Projection } from "./projections.js"
9
- import { type SelectionValue, validateAggregationSelection } from "./aggregation-validation.js"
1
+ import * as Query from "../../internal/query.js"
2
+ import * as Expression from "../../internal/scalar.js"
3
+ import * as Table from "../../internal/table.js"
4
+ import * as QueryAst from "../../internal/query-ast.js"
5
+ import type { RenderState, SqlDialect } from "../../internal/dialect.js"
6
+ import * as ExpressionAst from "../../internal/expression-ast.js"
7
+ import * as JsonPath from "../../internal/json/path.js"
8
+ import { flattenSelection, type Projection } from "../../internal/projections.js"
9
+ import { type SelectionValue, validateAggregationSelection } from "../../internal/aggregation-validation.js"
10
+ import * as SchemaExpression from "../../internal/schema-expression.js"
11
+ import type { DdlExpressionLike } from "../../internal/table-options.js"
10
12
 
11
13
  const renderDbType = (
12
14
  dialect: SqlDialect,
@@ -44,6 +46,15 @@ const renderCastType = (
44
46
  }
45
47
  }
46
48
 
49
+ const renderDdlExpression = (
50
+ expression: DdlExpressionLike,
51
+ state: RenderState,
52
+ dialect: SqlDialect
53
+ ): string =>
54
+ SchemaExpression.isSchemaExpression(expression)
55
+ ? SchemaExpression.render(expression)
56
+ : renderExpression(expression, state, dialect)
57
+
47
58
  const renderColumnDefinition = (
48
59
  dialect: SqlDialect,
49
60
  state: RenderState,
@@ -52,12 +63,14 @@ const renderColumnDefinition = (
52
63
  ): string => {
53
64
  const clauses = [
54
65
  dialect.quoteIdentifier(columnName),
55
- renderDbType(dialect, column.metadata.dbType)
66
+ column.metadata.ddlType ?? renderDbType(dialect, column.metadata.dbType)
56
67
  ]
57
- if (column.metadata.generatedValue) {
58
- clauses.push(`generated always as (${renderExpression(column.metadata.generatedValue, state, dialect)}) stored`)
68
+ if (column.metadata.identity) {
69
+ clauses.push(`generated ${column.metadata.identity.generation === "byDefault" ? "by default" : "always"} as identity`)
70
+ } else if (column.metadata.generatedValue) {
71
+ clauses.push(`generated always as (${renderDdlExpression(column.metadata.generatedValue, state, dialect)}) stored`)
59
72
  } else if (column.metadata.defaultValue) {
60
- clauses.push(`default ${renderExpression(column.metadata.defaultValue, state, dialect)}`)
73
+ clauses.push(`default ${renderDdlExpression(column.metadata.defaultValue, state, dialect)}`)
61
74
  }
62
75
  if (!column.metadata.nullable) {
63
76
  clauses.push("not null")
@@ -65,14 +78,6 @@ const renderColumnDefinition = (
65
78
  return clauses.join(" ")
66
79
  }
67
80
 
68
- const renderCheckPredicate = (
69
- predicate: Expression.Any,
70
- state: RenderState,
71
- dialect: SqlDialect
72
- ): string => {
73
- return renderExpression(predicate, state, dialect)
74
- }
75
-
76
81
  const renderCreateTableSql = (
77
82
  targetSource: QueryAst.FromClause,
78
83
  state: RenderState,
@@ -87,21 +92,21 @@ const renderCreateTableSql = (
87
92
  for (const option of table[Table.OptionsSymbol]) {
88
93
  switch (option.kind) {
89
94
  case "primaryKey":
90
- definitions.push(`primary key (${option.columns.map((column) => dialect.quoteIdentifier(column)).join(", ")})`)
95
+ definitions.push(`${option.name ? `constraint ${dialect.quoteIdentifier(option.name)} ` : ""}primary key (${option.columns.map((column) => dialect.quoteIdentifier(column)).join(", ")})${option.deferrable ? ` deferrable${option.initiallyDeferred ? " initially deferred" : ""}` : ""}`)
91
96
  break
92
97
  case "unique":
93
- definitions.push(`unique (${option.columns.map((column) => dialect.quoteIdentifier(column)).join(", ")})`)
98
+ definitions.push(`${option.name ? `constraint ${dialect.quoteIdentifier(option.name)} ` : ""}unique${option.nullsNotDistinct ? " nulls not distinct" : ""} (${option.columns.map((column) => dialect.quoteIdentifier(column)).join(", ")})${option.deferrable ? ` deferrable${option.initiallyDeferred ? " initially deferred" : ""}` : ""}`)
94
99
  break
95
100
  case "foreignKey": {
96
101
  const reference = option.references()
97
102
  definitions.push(
98
- `foreign key (${option.columns.map((column) => dialect.quoteIdentifier(column)).join(", ")}) references ${dialect.renderTableReference(reference.tableName, reference.tableName, reference.schemaName)} (${reference.columns.map((column) => dialect.quoteIdentifier(column)).join(", ")})`
103
+ `${option.name ? `constraint ${dialect.quoteIdentifier(option.name)} ` : ""}foreign key (${option.columns.map((column) => dialect.quoteIdentifier(column)).join(", ")}) references ${dialect.renderTableReference(reference.tableName, reference.tableName, reference.schemaName)} (${reference.columns.map((column) => dialect.quoteIdentifier(column)).join(", ")})${option.onDelete ? ` on delete ${option.onDelete.replace(/[A-Z]/g, (value) => ` ${value.toLowerCase()}`).trim()}` : ""}${option.onUpdate ? ` on update ${option.onUpdate.replace(/[A-Z]/g, (value) => ` ${value.toLowerCase()}`).trim()}` : ""}${option.deferrable ? ` deferrable${option.initiallyDeferred ? " initially deferred" : ""}` : ""}`
99
104
  )
100
105
  break
101
106
  }
102
107
  case "check":
103
108
  definitions.push(
104
- `constraint ${dialect.quoteIdentifier(option.name)} check (${renderCheckPredicate(option.predicate, state, dialect)})`
109
+ `constraint ${dialect.quoteIdentifier(option.name)} check (${renderDdlExpression(option.predicate, state, dialect)})${option.noInherit ? " no inherit" : ""}`
105
110
  )
106
111
  break
107
112
  case "index":
@@ -258,6 +263,12 @@ const renderPostgresJsonPathArray = (
258
263
  }
259
264
  }).join(", ")}]`
260
265
 
266
+ const renderPostgresTextLiteral = (
267
+ value: string,
268
+ state: RenderState,
269
+ dialect: SqlDialect
270
+ ): string => `cast(${dialect.renderLiteral(value, state)} as text)`
271
+
261
272
  const renderPostgresJsonAccessStep = (
262
273
  segment: JsonPath.AnySegment,
263
274
  textMode: boolean,
@@ -288,6 +299,10 @@ const renderPostgresJsonValue = (
288
299
  : `cast(${rendered} as jsonb)`
289
300
  }
290
301
 
302
+ const renderPostgresJsonKind = (
303
+ value: Expression.Any
304
+ ): "json" | "jsonb" => value[Expression.TypeId].dbType.kind === "jsonb" ? "jsonb" : "json"
305
+
291
306
  const renderJsonOpaquePath = (
292
307
  value: unknown,
293
308
  state: RenderState,
@@ -311,6 +326,24 @@ const renderFunctionCall = (
311
326
  state: RenderState,
312
327
  dialect: SqlDialect
313
328
  ): string => {
329
+ if (name === "array") {
330
+ return `ARRAY[${args.map((arg) => renderExpression(arg, state, dialect)).join(", ")}]`
331
+ }
332
+ if (name === "extract" && args.length === 2) {
333
+ const field = args[0]
334
+ const source = args[1]
335
+ if (field === undefined) {
336
+ throw new Error("Unsupported SQL extract expression")
337
+ }
338
+ if (source === undefined) {
339
+ throw new Error("Unsupported SQL extract expression")
340
+ }
341
+ const fieldRuntime = isExpression(field) && field[Expression.TypeId].dbType.kind === "text" && typeof field[Expression.TypeId].runtime === "string"
342
+ ? field[Expression.TypeId].runtime
343
+ : undefined
344
+ const renderedField = fieldRuntime ?? renderExpression(field, state, dialect)
345
+ return `extract(${renderedField} from ${renderExpression(source, state, dialect)})`
346
+ }
314
347
  const renderedArgs = args.map((arg) => renderExpression(arg, state, dialect)).join(", ")
315
348
  if (args.length === 0) {
316
349
  switch (name) {
@@ -328,6 +361,7 @@ const renderFunctionCall = (
328
361
  }
329
362
 
330
363
  const renderJsonExpression = (
364
+ expression: Expression.Any,
331
365
  ast: Record<string, unknown>,
332
366
  state: RenderState,
333
367
  dialect: SqlDialect
@@ -340,6 +374,12 @@ const renderJsonExpression = (
340
374
  const base = extractJsonBase(ast)
341
375
  const segments = extractJsonPathSegments(ast)
342
376
  const exact = segments.every((segment) => segment.kind === "key" || segment.kind === "index")
377
+ const postgresExpressionKind = dialect.name === "postgres" && isJsonExpression(expression)
378
+ ? renderPostgresJsonKind(expression)
379
+ : undefined
380
+ const postgresBaseKind = dialect.name === "postgres" && isJsonExpression(base)
381
+ ? renderPostgresJsonKind(base)
382
+ : undefined
343
383
 
344
384
  switch (kind) {
345
385
  case "jsonGet":
@@ -363,7 +403,7 @@ const renderJsonExpression = (
363
403
  }
364
404
  const jsonPathLiteral = dialect.renderLiteral(renderJsonPathStringLiteral(segments), state)
365
405
  const queried = `jsonb_path_query_first(${renderPostgresJsonValue(base, state, dialect)}, ${jsonPathLiteral})`
366
- return textMode ? `cast(${queried} as text)` : queried
406
+ return textMode ? `(${queried} #>> '{}')` : queried
367
407
  }
368
408
  if (dialect.name === "mysql") {
369
409
  const extracted = `json_extract(${baseSql}, ${renderMySqlJsonPath(segments, state, dialect)})`
@@ -387,12 +427,12 @@ const renderJsonExpression = (
387
427
  }
388
428
  if (dialect.name === "postgres") {
389
429
  if (kind === "jsonHasAnyKeys") {
390
- return `(${baseSql} ?| ${renderPostgresJsonPathArray(keys, state, dialect)})`
430
+ return `(${baseSql} ?| array[${keys.map((key) => renderPostgresTextLiteral(String(key), state, dialect)).join(", ")}])`
391
431
  }
392
432
  if (kind === "jsonHasAllKeys") {
393
- return `(${baseSql} ?& ${renderPostgresJsonPathArray(keys, state, dialect)})`
433
+ return `(${baseSql} ?& array[${keys.map((key) => renderPostgresTextLiteral(String(key), state, dialect)).join(", ")}])`
394
434
  }
395
- return `(${baseSql} ? ${dialect.renderLiteral(keys[0]!, state)})`
435
+ return `(${baseSql} ? ${renderPostgresTextLiteral(String(keys[0]!), state, dialect)})`
396
436
  }
397
437
  if (dialect.name === "mysql") {
398
438
  const mode = kind === "jsonHasAllKeys" ? "all" : "one"
@@ -423,7 +463,7 @@ const renderJsonExpression = (
423
463
  renderExpression(entry.value, state, dialect)
424
464
  ])
425
465
  if (dialect.name === "postgres") {
426
- return `jsonb_build_object(${renderedEntries.join(", ")})`
466
+ return `${postgresExpressionKind === "jsonb" ? "jsonb" : "json"}_build_object(${renderedEntries.join(", ")})`
427
467
  }
428
468
  if (dialect.name === "mysql") {
429
469
  return `json_object(${renderedEntries.join(", ")})`
@@ -436,7 +476,7 @@ const renderJsonExpression = (
436
476
  : []
437
477
  const renderedValues = values.map((value) => renderExpression(value, state, dialect)).join(", ")
438
478
  if (dialect.name === "postgres") {
439
- return `jsonb_build_array(${renderedValues})`
479
+ return `${postgresExpressionKind === "jsonb" ? "jsonb" : "json"}_build_array(${renderedValues})`
440
480
  }
441
481
  if (dialect.name === "mysql") {
442
482
  return `json_array(${renderedValues})`
@@ -470,7 +510,8 @@ const renderJsonExpression = (
470
510
  return undefined
471
511
  }
472
512
  if (dialect.name === "postgres") {
473
- return `jsonb_typeof(${renderPostgresJsonValue(base, state, dialect)})`
513
+ const baseSql = renderExpression(base, state, dialect)
514
+ return `${postgresBaseKind === "jsonb" ? "jsonb" : "json"}_typeof(${baseSql})`
474
515
  }
475
516
  if (dialect.name === "mysql") {
476
517
  return `json_type(${renderExpression(base, state, dialect)})`
@@ -481,8 +522,11 @@ const renderJsonExpression = (
481
522
  return undefined
482
523
  }
483
524
  if (dialect.name === "postgres") {
484
- const jsonb = renderPostgresJsonValue(base, state, dialect)
485
- return `(case when jsonb_typeof(${jsonb}) = 'array' then jsonb_array_length(${jsonb}) when jsonb_typeof(${jsonb}) = 'object' then jsonb_object_length(${jsonb}) else null end)`
525
+ const baseSql = renderExpression(base, state, dialect)
526
+ const typeOf = `${postgresBaseKind === "jsonb" ? "jsonb" : "json"}_typeof`
527
+ const arrayLength = `${postgresBaseKind === "jsonb" ? "jsonb" : "json"}_array_length`
528
+ const objectKeys = `${postgresBaseKind === "jsonb" ? "jsonb" : "json"}_object_keys`
529
+ return `(case when ${typeOf}(${baseSql}) = 'array' then ${arrayLength}(${baseSql}) when ${typeOf}(${baseSql}) = 'object' then (select count(*)::int from ${objectKeys}(${baseSql})) else null end)`
486
530
  }
487
531
  if (dialect.name === "mysql") {
488
532
  return `json_length(${renderExpression(base, state, dialect)})`
@@ -493,8 +537,10 @@ const renderJsonExpression = (
493
537
  return undefined
494
538
  }
495
539
  if (dialect.name === "postgres") {
496
- const jsonb = renderPostgresJsonValue(base, state, dialect)
497
- return `(case when jsonb_typeof(${jsonb}) = 'object' then array(select jsonb_object_keys(${jsonb})) else null end)`
540
+ const baseSql = renderExpression(base, state, dialect)
541
+ const typeOf = `${postgresBaseKind === "jsonb" ? "jsonb" : "json"}_typeof`
542
+ const objectKeys = `${postgresBaseKind === "jsonb" ? "jsonb" : "json"}_object_keys`
543
+ return `(case when ${typeOf}(${baseSql}) = 'object' then array(select ${objectKeys}(${baseSql})) else null end)`
498
544
  }
499
545
  if (dialect.name === "mysql") {
500
546
  return `json_keys(${renderExpression(base, state, dialect)})`
@@ -505,7 +551,7 @@ const renderJsonExpression = (
505
551
  return undefined
506
552
  }
507
553
  if (dialect.name === "postgres") {
508
- return `jsonb_strip_nulls(${renderPostgresJsonValue(base, state, dialect)})`
554
+ return `${postgresBaseKind === "jsonb" ? "jsonb" : "json"}_strip_nulls(${renderExpression(base, state, dialect)})`
509
555
  }
510
556
  unsupportedJsonFeature(dialect, "jsonStripNulls")
511
557
  return undefined
@@ -772,7 +818,7 @@ export const renderQueryAst = (
772
818
  case "set": {
773
819
  const setAst = ast as QueryAst.Ast<Record<string, unknown>, any, "set">
774
820
  const base = renderQueryAst(
775
- Query.getAst(setAst.setBase as Query.QueryPlan<any, any, any, any, any, any, any, any, any, any>) as QueryAst.Ast<
821
+ Query.getAst(setAst.setBase as Query.Plan.Any) as QueryAst.Ast<
776
822
  Record<string, unknown>,
777
823
  any,
778
824
  QueryAst.QueryStatement
@@ -785,7 +831,7 @@ export const renderQueryAst = (
785
831
  `(${base.sql})`,
786
832
  ...(setAst.setOperations ?? []).map((entry) => {
787
833
  const rendered = renderQueryAst(
788
- Query.getAst(entry.query as Query.QueryPlan<any, any, any, any, any, any, any, any, any, any>) as QueryAst.Ast<
834
+ Query.getAst(entry.query as Query.Plan.Any) as QueryAst.Ast<
789
835
  Record<string, unknown>,
790
836
  any,
791
837
  QueryAst.QueryStatement
@@ -812,7 +858,7 @@ export const renderQueryAst = (
812
858
  } else if (insertAst.insertSource?.kind === "query") {
813
859
  const columns = insertAst.insertSource.columns.map((column) => dialect.quoteIdentifier(column)).join(", ")
814
860
  const renderedQuery = renderQueryAst(
815
- Query.getAst(insertAst.insertSource.query as Query.QueryPlan<any, any, any, any, any, any, any, any, any, any>) as QueryAst.Ast<
861
+ Query.getAst(insertAst.insertSource.query as Query.Plan.Any) as QueryAst.Ast<
816
862
  Record<string, unknown>,
817
863
  any,
818
864
  QueryAst.QueryStatement
@@ -1105,7 +1151,7 @@ const renderSourceReference = (
1105
1151
  if (typeof source === "object" && source !== null && "kind" in source && (source as { readonly kind?: string }).kind === "cte") {
1106
1152
  const cte = source as unknown as {
1107
1153
  readonly name: string
1108
- readonly plan: Query.QueryPlan<any, any, any, any, any, any, any, any, any>
1154
+ readonly plan: Query.Plan.Any
1109
1155
  readonly recursive?: boolean
1110
1156
  }
1111
1157
  if (!state.cteNames.has(cte.name)) {
@@ -1122,7 +1168,7 @@ const renderSourceReference = (
1122
1168
  if (typeof source === "object" && source !== null && "kind" in source && (source as { readonly kind?: string }).kind === "derived") {
1123
1169
  const derived = source as unknown as {
1124
1170
  readonly name: string
1125
- readonly plan: Query.QueryPlan<any, any, any, any, any, any, any, any, any>
1171
+ readonly plan: Query.Plan.Any
1126
1172
  }
1127
1173
  if (!state.cteNames.has(derived.name)) {
1128
1174
  // derived tables are inlined, so no CTE registration is needed
@@ -1132,7 +1178,7 @@ const renderSourceReference = (
1132
1178
  if (typeof source === "object" && source !== null && "kind" in source && (source as { readonly kind?: string }).kind === "lateral") {
1133
1179
  const lateral = source as unknown as {
1134
1180
  readonly name: string
1135
- readonly plan: Query.QueryPlan<any, any, any, any, any, any, any, any, any>
1181
+ readonly plan: Query.Plan.Any
1136
1182
  }
1137
1183
  return `lateral (${renderQueryAst(Query.getAst(lateral.plan) as QueryAst.Ast<Record<string, unknown>, any, QueryAst.QueryStatement>, state, dialect).sql}) as ${dialect.quoteIdentifier(lateral.name)}`
1138
1184
  }
@@ -1184,7 +1230,7 @@ export const renderExpression = (
1184
1230
  const rawAst = (expression as Expression.Any & {
1185
1231
  readonly [ExpressionAst.TypeId]: ExpressionAst.Any
1186
1232
  })[ExpressionAst.TypeId] as ExpressionAst.Any | Record<string, unknown>
1187
- const jsonSql = renderJsonExpression(rawAst as Record<string, unknown>, state, dialect)
1233
+ const jsonSql = renderJsonExpression(expression, rawAst as Record<string, unknown>, state, dialect)
1188
1234
  if (jsonSql !== undefined) {
1189
1235
  return jsonSql
1190
1236
  }
@@ -1201,9 +1247,11 @@ export const renderExpression = (
1201
1247
  : operator === "gt"
1202
1248
  ? ">"
1203
1249
  : ">="
1204
- switch (ast.kind) {
1250
+ switch (ast.kind) {
1205
1251
  case "column":
1206
- return `${dialect.quoteIdentifier(ast.tableName)}.${dialect.quoteIdentifier(ast.columnName)}`
1252
+ return ast.tableName.length === 0
1253
+ ? dialect.quoteIdentifier(ast.columnName)
1254
+ : `${dialect.quoteIdentifier(ast.tableName)}.${dialect.quoteIdentifier(ast.columnName)}`
1207
1255
  case "literal":
1208
1256
  return dialect.renderLiteral(ast.value, state)
1209
1257
  case "excluded":
@@ -1212,6 +1260,8 @@ export const renderExpression = (
1212
1260
  : `excluded.${dialect.quoteIdentifier(ast.columnName)}`
1213
1261
  case "cast":
1214
1262
  return `cast(${renderExpression(ast.value, state, dialect)} as ${renderCastType(dialect, ast.target)})`
1263
+ case "collate":
1264
+ return `(${renderExpression(ast.value, state, dialect)} collate ${ast.collation.map((segment) => dialect.quoteIdentifier(segment)).join(".")})`
1215
1265
  case "function":
1216
1266
  return renderFunctionCall(ast.name, Array.isArray(ast.args) ? ast.args : [], state, dialect)
1217
1267
  case "eq":
@@ -1232,6 +1282,22 @@ export const renderExpression = (
1232
1282
  return dialect.name === "postgres"
1233
1283
  ? `(${renderExpression(ast.left, state, dialect)} ilike ${renderExpression(ast.right, state, dialect)})`
1234
1284
  : `(lower(${renderExpression(ast.left, state, dialect)}) like lower(${renderExpression(ast.right, state, dialect)}))`
1285
+ case "regexMatch":
1286
+ return dialect.name === "postgres"
1287
+ ? `(${renderExpression(ast.left, state, dialect)} ~ ${renderExpression(ast.right, state, dialect)})`
1288
+ : `(${renderExpression(ast.left, state, dialect)} regexp ${renderExpression(ast.right, state, dialect)})`
1289
+ case "regexIMatch":
1290
+ return dialect.name === "postgres"
1291
+ ? `(${renderExpression(ast.left, state, dialect)} ~* ${renderExpression(ast.right, state, dialect)})`
1292
+ : `(${renderExpression(ast.left, state, dialect)} regexp ${renderExpression(ast.right, state, dialect)})`
1293
+ case "regexNotMatch":
1294
+ return dialect.name === "postgres"
1295
+ ? `(${renderExpression(ast.left, state, dialect)} !~ ${renderExpression(ast.right, state, dialect)})`
1296
+ : `(${renderExpression(ast.left, state, dialect)} not regexp ${renderExpression(ast.right, state, dialect)})`
1297
+ case "regexNotIMatch":
1298
+ return dialect.name === "postgres"
1299
+ ? `(${renderExpression(ast.left, state, dialect)} !~* ${renderExpression(ast.right, state, dialect)})`
1300
+ : `(${renderExpression(ast.left, state, dialect)} not regexp ${renderExpression(ast.right, state, dialect)})`
1235
1301
  case "isDistinctFrom":
1236
1302
  return dialect.name === "mysql"
1237
1303
  ? `(not (${renderExpression(ast.left, state, dialect)} <=> ${renderExpression(ast.right, state, dialect)}))`