effect-qb 0.16.0 → 0.19.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 (128) hide show
  1. package/README.md +4 -0
  2. package/dist/index.js +8065 -0
  3. package/dist/mysql.js +4036 -2418
  4. package/dist/postgres/metadata.js +2536 -625
  5. package/dist/postgres.js +8248 -7857
  6. package/dist/sqlite.js +8854 -0
  7. package/dist/standard.js +8019 -0
  8. package/package.json +15 -3
  9. package/src/casing.ts +71 -0
  10. package/src/index.ts +2 -0
  11. package/src/internal/casing.ts +89 -0
  12. package/src/internal/column-state.ts +11 -6
  13. package/src/internal/column.ts +44 -7
  14. package/src/internal/datatypes/define.ts +2 -1
  15. package/src/internal/datatypes/enrich.ts +23 -0
  16. package/src/internal/datatypes/lookup.ts +14 -7
  17. package/src/internal/derived-table.ts +7 -13
  18. package/src/internal/dialect-renderers/mysql.ts +2046 -0
  19. package/src/{postgres/internal/sql-expression-renderer.ts → internal/dialect-renderers/postgres.ts} +867 -283
  20. package/src/{mysql/internal/sql-expression-renderer.ts → internal/dialect-renderers/sqlite.ts} +834 -358
  21. package/src/internal/dialect.ts +37 -0
  22. package/src/internal/dsl-mutation-runtime.ts +29 -10
  23. package/src/internal/dsl-plan-runtime.ts +41 -24
  24. package/src/internal/dsl-query-runtime.ts +11 -31
  25. package/src/internal/dsl-transaction-ddl-runtime.ts +61 -15
  26. package/src/internal/executor.ts +57 -15
  27. package/src/internal/expression-ast.ts +3 -2
  28. package/src/internal/grouping-key.ts +216 -9
  29. package/src/internal/implication-runtime.ts +3 -2
  30. package/src/internal/json/types.ts +155 -40
  31. package/src/internal/predicate/context.ts +14 -1
  32. package/src/internal/predicate/key.ts +19 -2
  33. package/src/internal/predicate/runtime.ts +30 -3
  34. package/src/internal/query.d.ts +38 -11
  35. package/src/internal/query.ts +315 -54
  36. package/src/internal/renderer.ts +51 -6
  37. package/src/internal/runtime/driver-value-mapping.ts +58 -0
  38. package/src/internal/runtime/normalize.ts +74 -43
  39. package/src/internal/runtime/schema.ts +5 -3
  40. package/src/internal/runtime/value.ts +153 -30
  41. package/src/internal/scalar.ts +6 -1
  42. package/src/internal/schema-derivation.d.ts +12 -61
  43. package/src/internal/schema-derivation.ts +90 -38
  44. package/src/internal/schema-expression.ts +2 -2
  45. package/src/internal/sql-expression-renderer.ts +19 -0
  46. package/src/internal/standard-dsl.ts +6885 -0
  47. package/src/internal/table-options.ts +229 -62
  48. package/src/internal/table.d.ts +33 -32
  49. package/src/internal/table.ts +469 -160
  50. package/src/mysql/column-extension.ts +3 -0
  51. package/src/mysql/column.ts +27 -12
  52. package/src/mysql/datatypes/index.ts +24 -2
  53. package/src/mysql/errors/catalog.ts +5 -5
  54. package/src/mysql/errors/normalize.ts +2 -2
  55. package/src/mysql/executor.ts +7 -5
  56. package/src/mysql/internal/dialect.ts +9 -4
  57. package/src/mysql/internal/dsl.ts +906 -324
  58. package/src/mysql/internal/renderer.ts +7 -2
  59. package/src/mysql/json.ts +37 -0
  60. package/src/mysql/query-extension.ts +16 -0
  61. package/src/mysql/query.ts +9 -2
  62. package/src/mysql/renderer.ts +31 -4
  63. package/src/mysql.ts +4 -12
  64. package/src/postgres/column-extension.ts +28 -0
  65. package/src/postgres/column.ts +9 -13
  66. package/src/postgres/datatypes/index.d.ts +2 -1
  67. package/src/postgres/datatypes/index.ts +3 -2
  68. package/src/postgres/errors/normalize.ts +2 -2
  69. package/src/postgres/executor.ts +55 -10
  70. package/src/postgres/function/core.ts +20 -4
  71. package/src/postgres/function/index.ts +1 -17
  72. package/src/postgres/internal/dialect.ts +9 -4
  73. package/src/postgres/internal/dsl.ts +850 -359
  74. package/src/postgres/internal/renderer.ts +7 -2
  75. package/src/postgres/internal/schema-ddl.ts +22 -9
  76. package/src/postgres/internal/schema-model.ts +244 -10
  77. package/src/postgres/json.ts +100 -24
  78. package/src/postgres/jsonb.ts +38 -0
  79. package/src/postgres/query-extension.ts +2 -0
  80. package/src/postgres/query.ts +9 -2
  81. package/src/postgres/renderer.ts +31 -4
  82. package/src/postgres/schema-management.ts +108 -16
  83. package/src/postgres/schema.ts +98 -15
  84. package/src/postgres/table.ts +203 -398
  85. package/src/postgres/type.ts +8 -7
  86. package/src/postgres.ts +9 -11
  87. package/src/sqlite/column-extension.ts +3 -0
  88. package/src/sqlite/column.ts +127 -0
  89. package/src/sqlite/datatypes/index.ts +80 -0
  90. package/src/sqlite/datatypes/spec.ts +98 -0
  91. package/src/sqlite/errors/catalog.ts +103 -0
  92. package/src/sqlite/errors/fields.ts +19 -0
  93. package/src/sqlite/errors/index.ts +19 -0
  94. package/src/sqlite/errors/normalize.ts +229 -0
  95. package/src/sqlite/errors/requirements.ts +71 -0
  96. package/src/sqlite/errors/types.ts +29 -0
  97. package/src/sqlite/executor.ts +229 -0
  98. package/src/sqlite/function/aggregate.ts +2 -0
  99. package/src/sqlite/function/core.ts +2 -0
  100. package/src/sqlite/function/index.ts +19 -0
  101. package/src/sqlite/function/string.ts +2 -0
  102. package/src/sqlite/function/temporal.ts +100 -0
  103. package/src/sqlite/function/window.ts +2 -0
  104. package/src/sqlite/internal/dialect.ts +42 -0
  105. package/src/sqlite/internal/dsl.ts +6979 -0
  106. package/src/sqlite/internal/renderer.ts +51 -0
  107. package/src/sqlite/json.ts +39 -0
  108. package/src/sqlite/query-extension.ts +2 -0
  109. package/src/sqlite/query.ts +196 -0
  110. package/src/sqlite/renderer.ts +51 -0
  111. package/src/sqlite.ts +14 -0
  112. package/src/standard/column.ts +163 -0
  113. package/src/standard/datatypes/index.ts +83 -0
  114. package/src/standard/datatypes/spec.ts +98 -0
  115. package/src/standard/dialect.ts +40 -0
  116. package/src/standard/function/aggregate.ts +2 -0
  117. package/src/standard/function/core.ts +2 -0
  118. package/src/standard/function/index.ts +18 -0
  119. package/src/standard/function/string.ts +2 -0
  120. package/src/standard/function/temporal.ts +78 -0
  121. package/src/standard/function/window.ts +2 -0
  122. package/src/standard/internal/renderer.ts +45 -0
  123. package/src/standard/query.ts +152 -0
  124. package/src/standard/renderer.ts +21 -0
  125. package/src/standard/table.ts +147 -0
  126. package/src/standard.ts +18 -0
  127. package/src/internal/aggregation-validation.ts +0 -57
  128. package/src/mysql/table.ts +0 -157
@@ -1,9 +1,10 @@
1
1
  import * as Query from "../../internal/query.js"
2
2
  import type * as Expression from "../../internal/scalar.js"
3
+ import type * as Casing from "../../internal/casing.js"
3
4
  import { type RenderState } from "../../internal/dialect.js"
4
5
  import { postgresDialect } from "./dialect.js"
5
6
  import { type Projection } from "../../internal/projections.js"
6
- import { renderQueryAst } from "./sql-expression-renderer.js"
7
+ import { renderQueryAst } from "../../internal/sql-expression-renderer.js"
7
8
 
8
9
  /**
9
10
  * Minimal rendered-query payload produced by the built-in Postgres renderer.
@@ -20,6 +21,7 @@ export interface PostgresRenderResult {
20
21
 
21
22
  export interface PostgresRenderOptions {
22
23
  readonly valueMappings?: Expression.DriverValueMappings
24
+ readonly casing?: Casing.Options
23
25
  }
24
26
 
25
27
  /**
@@ -32,8 +34,11 @@ export const renderPostgresPlan = <PlanValue extends Query.Plan.Any>(
32
34
  const state: RenderState = {
33
35
  params: [],
34
36
  valueMappings: options.valueMappings,
37
+ casing: options.casing,
35
38
  ctes: [],
36
- cteNames: new Set<string>()
39
+ cteNames: new Set<string>(),
40
+ cteSources: new Map<string, unknown>(),
41
+ sourceNames: new Map()
37
42
  }
38
43
  const rendered = renderQueryAst(
39
44
  Query.getAst(plan as Query.Plan.Any) as any,
@@ -1,7 +1,7 @@
1
1
  import type * as Expression from "../../internal/scalar.js"
2
2
  import type { RenderState, SqlDialect } from "../../internal/dialect.js"
3
3
  import * as SchemaExpression from "../../internal/schema-expression.js"
4
- import { renderExpression } from "./sql-expression-renderer.js"
4
+ import { renderExpression } from "../../internal/sql-expression-renderer.js"
5
5
  import type { DdlExpressionLike } from "../../internal/table-options.js"
6
6
  import { parse, toSql } from "pgsql-ast-parser"
7
7
  import { postgresDialect } from "./dialect.js"
@@ -30,20 +30,30 @@ const inlineLiteralDialect: SqlDialect<"postgres"> = {
30
30
  return String(value)
31
31
  }
32
32
  if (value instanceof Date) {
33
+ if (Number.isNaN(value.getTime())) {
34
+ throw new Error("Expected a valid Date value")
35
+ }
33
36
  return escapeString(value.toISOString())
34
37
  }
35
38
  return escapeString(String(value))
36
39
  }
37
40
  }
38
41
 
39
- export const renderDdlExpressionSql = (expression: DdlExpressionLike): string =>
42
+ const makeExpressionState = (state: Partial<RenderState> = {}): RenderState => ({
43
+ ...state,
44
+ params: [],
45
+ ctes: [],
46
+ cteNames: new Set(),
47
+ cteSources: new Map()
48
+ })
49
+
50
+ export const renderDdlExpressionSql = (
51
+ expression: DdlExpressionLike,
52
+ state?: Partial<RenderState>
53
+ ): string =>
40
54
  SchemaExpression.isSchemaExpression(expression)
41
55
  ? SchemaExpression.render(expression)
42
- : renderExpression(expression as Expression.Any, {
43
- params: [],
44
- ctes: [],
45
- cteNames: new Set()
46
- }, inlineLiteralDialect)
56
+ : renderExpression(expression as Expression.Any, makeExpressionState(state), inlineLiteralDialect)
47
57
 
48
58
  const stripRedundantOuterParens = (value: string): string => {
49
59
  let current = value.trim()
@@ -98,8 +108,11 @@ const canonicalizeDdlExpressionSql = (value: string): string =>
98
108
  )
99
109
  )
100
110
 
101
- export const normalizeDdlExpressionSql = (expression: DdlExpressionLike): string => {
102
- const rendered = renderDdlExpressionSql(expression)
111
+ export const normalizeDdlExpressionSql = (
112
+ expression: DdlExpressionLike,
113
+ state?: Partial<RenderState>
114
+ ): string => {
115
+ const rendered = renderDdlExpressionSql(expression, state)
103
116
  try {
104
117
  return canonicalizeDdlExpressionSql(toSql.expr(parse(rendered, "expr")))
105
118
  } catch {
@@ -1,7 +1,11 @@
1
1
  import * as Table from "../../internal/table.js"
2
2
  import type { AnyColumnDefinition } from "../../internal/column-state.js"
3
+ import type { RenderState } from "../../internal/dialect.js"
4
+ import * as Casing from "../../internal/casing.js"
5
+ import * as Expression from "../../internal/scalar.js"
6
+ import * as SchemaExpression from "../../internal/schema-expression.js"
3
7
  import { normalizeDdlExpressionSql } from "./schema-ddl.js"
4
- import type { TableOptionSpec } from "../../internal/table-options.js"
8
+ import { validateOptions, type ColumnList, type DdlExpressionLike, type IndexKeySpec, type TableOptionSpec } from "../../internal/table-options.js"
5
9
  import type { EnumDefinition } from "../schema-management.js"
6
10
  import { EnumTypeId } from "../schema-management.js"
7
11
 
@@ -52,15 +56,238 @@ export const isTableDefinition = (value: unknown): value is Table.AnyTable =>
52
56
  export const isEnumDefinition = (value: unknown): value is EnumDefinition =>
53
57
  typeof value === "object" && value !== null && EnumTypeId in value
54
58
 
59
+ const applyCasing = (
60
+ casing: Casing.Options | undefined,
61
+ category: Casing.Category,
62
+ name: string
63
+ ): string =>
64
+ Casing.applyCategory(casing, category, name)
65
+
66
+ const mapColumnList = (
67
+ columns: ColumnList,
68
+ casing: Casing.Options | undefined
69
+ ): ColumnList =>
70
+ !Array.isArray(columns)
71
+ ? columns
72
+ : columns.length === 0
73
+ ? columns
74
+ : [
75
+ mapCasedValue(columns[0], casing, "columns"),
76
+ ...columns.slice(1).map((column) => mapCasedValue(column, casing, "columns"))
77
+ ] as ColumnList
78
+
79
+ const expressionStateForTable = (
80
+ state: Table.AnyTable[typeof Table.TypeId],
81
+ tableName: string,
82
+ columns: ReadonlyMap<string, string>,
83
+ casing: Casing.Options | undefined
84
+ ): Partial<RenderState> => ({
85
+ casing,
86
+ rowLocalColumns: true,
87
+ sourceNames: new Map([
88
+ [state.name, { tableName, columns }],
89
+ [state.baseName, { tableName, columns }]
90
+ ])
91
+ })
92
+
93
+ const mapDdlExpression = (
94
+ expression: DdlExpressionLike,
95
+ state: Partial<RenderState>
96
+ ): SchemaExpression.SchemaExpression =>
97
+ SchemaExpression.fromSql(normalizeDdlExpressionSql(expression, state))
98
+
99
+ function mapOptionName(
100
+ name: string,
101
+ casing: Casing.Options | undefined,
102
+ category: "indexes" | "constraints"
103
+ ): string
104
+ function mapOptionName(
105
+ name: unknown,
106
+ casing: Casing.Options | undefined,
107
+ category: "indexes" | "constraints"
108
+ ): unknown
109
+ function mapOptionName(
110
+ name: unknown,
111
+ casing: Casing.Options | undefined,
112
+ category: "indexes" | "constraints"
113
+ ): unknown {
114
+ return typeof name === "string"
115
+ ? applyCasing(casing, category, name)
116
+ : name
117
+ }
118
+
119
+ function mapCasedValue(
120
+ value: string,
121
+ casing: Casing.Options | undefined,
122
+ category: Casing.Category
123
+ ): string
124
+ function mapCasedValue(
125
+ value: unknown,
126
+ casing: Casing.Options | undefined,
127
+ category: Casing.Category
128
+ ): unknown
129
+ function mapCasedValue(
130
+ value: unknown,
131
+ casing: Casing.Options | undefined,
132
+ category: Casing.Category
133
+ ): unknown {
134
+ return typeof value === "string"
135
+ ? applyCasing(casing, category, value)
136
+ : value
137
+ }
138
+
139
+ const isDdlExpressionLike = (value: unknown): value is DdlExpressionLike =>
140
+ typeof value === "object" &&
141
+ value !== null &&
142
+ (Expression.TypeId in value || SchemaExpression.TypeId in value)
143
+
144
+ function mapIndexKey(
145
+ key: IndexKeySpec,
146
+ casing: Casing.Options | undefined,
147
+ expressionState: Partial<RenderState>
148
+ ): IndexKeySpec
149
+ function mapIndexKey(
150
+ key: unknown,
151
+ casing: Casing.Options | undefined,
152
+ expressionState: Partial<RenderState>
153
+ ): unknown
154
+ function mapIndexKey(
155
+ key: unknown,
156
+ casing: Casing.Options | undefined,
157
+ expressionState: Partial<RenderState>
158
+ ): unknown {
159
+ if (typeof key !== "object" || key === null || !("kind" in key)) {
160
+ return key
161
+ }
162
+ const kind = (key as { readonly kind?: unknown }).kind
163
+ if (kind === "column") {
164
+ const column = (key as { readonly column?: unknown }).column
165
+ return typeof column === "string"
166
+ ? {
167
+ ...key,
168
+ column: applyCasing(casing, "columns", column)
169
+ }
170
+ : key
171
+ }
172
+ if (kind === "expression") {
173
+ const expression = (key as { readonly expression?: unknown }).expression
174
+ return isDdlExpressionLike(expression)
175
+ ? {
176
+ ...key,
177
+ expression: mapDdlExpression(expression, expressionState)
178
+ }
179
+ : key
180
+ }
181
+ return key
182
+ }
183
+
184
+ const mapOption = (
185
+ option: TableOptionSpec,
186
+ casing: Casing.Options | undefined,
187
+ expressionState: Partial<RenderState>
188
+ ): TableOptionSpec => {
189
+ switch (option.kind) {
190
+ case "index":
191
+ return {
192
+ ...option,
193
+ columns: option.columns === undefined ? undefined : mapColumnList(option.columns, casing),
194
+ name: option.name === undefined ? undefined : mapOptionName(option.name, casing, "indexes"),
195
+ include: option.include === undefined
196
+ ? undefined
197
+ : Array.isArray(option.include)
198
+ ? option.include.map((column) => mapCasedValue(column, casing, "columns")) as unknown as readonly string[]
199
+ : option.include,
200
+ predicate: option.predicate === undefined
201
+ ? undefined
202
+ : isDdlExpressionLike(option.predicate)
203
+ ? mapDdlExpression(option.predicate, expressionState)
204
+ : option.predicate,
205
+ keys: option.keys === undefined
206
+ ? undefined
207
+ : Array.isArray(option.keys)
208
+ ? option.keys.length === 0
209
+ ? option.keys
210
+ : [
211
+ mapIndexKey(option.keys[0], casing, expressionState),
212
+ ...option.keys.slice(1).map((key) => mapIndexKey(key, casing, expressionState))
213
+ ] as unknown as readonly [IndexKeySpec, ...IndexKeySpec[]]
214
+ : option.keys
215
+ }
216
+ case "primaryKey":
217
+ return {
218
+ ...option,
219
+ columns: mapColumnList(option.columns, casing),
220
+ name: option.name === undefined ? undefined : mapOptionName(option.name, casing, "constraints")
221
+ }
222
+ case "unique":
223
+ return {
224
+ ...option,
225
+ columns: mapColumnList(option.columns, casing),
226
+ name: option.name === undefined ? undefined : mapOptionName(option.name, casing, "constraints")
227
+ }
228
+ case "foreignKey":
229
+ return {
230
+ ...option,
231
+ columns: mapColumnList(option.columns, casing),
232
+ name: option.name === undefined ? undefined : mapOptionName(option.name, casing, "constraints"),
233
+ references: () => {
234
+ const reference = typeof option.references === "function"
235
+ ? option.references()
236
+ : option.references
237
+ if (typeof reference !== "object" || reference === null) {
238
+ return reference
239
+ }
240
+ const referenceCasing = reference.casing
241
+ return {
242
+ ...reference,
243
+ tableName: mapCasedValue(reference.tableName, referenceCasing, "tables"),
244
+ schemaName: reference.schemaName === undefined
245
+ ? undefined
246
+ : mapCasedValue(reference.schemaName, referenceCasing, "schemas"),
247
+ columns: mapColumnList(reference.columns, referenceCasing),
248
+ knownColumns: reference.knownColumns === undefined
249
+ ? undefined
250
+ : Array.isArray(reference.knownColumns)
251
+ ? reference.knownColumns.map((column) =>
252
+ mapCasedValue(column, referenceCasing, "columns")) as unknown as readonly string[]
253
+ : reference.knownColumns
254
+ }
255
+ }
256
+ }
257
+ case "check":
258
+ return {
259
+ ...option,
260
+ name: mapOptionName(option.name, casing, "constraints"),
261
+ predicate: isDdlExpressionLike(option.predicate)
262
+ ? mapDdlExpression(option.predicate, expressionState)
263
+ : option.predicate
264
+ }
265
+ default:
266
+ return option
267
+ }
268
+ }
269
+
55
270
  export const toTableModel = (table: Table.AnyTable): TableModel => {
56
271
  const state = table[Table.TypeId]
272
+ const casing = state.casing
273
+ const tableName = applyCasing(casing, "tables", state.baseName)
274
+ const schemaName = state.schemaName === undefined
275
+ ? undefined
276
+ : applyCasing(casing, "schemas", state.schemaName)
57
277
  const fields = state.fields as Record<string, AnyColumnDefinition>
278
+ const options = table[Table.OptionsSymbol] as unknown
279
+ const normalizedOptions = (Array.isArray(options) ? options : [options]) as readonly TableOptionSpec[]
280
+ validateOptions(state.name, fields, normalizedOptions)
281
+ const columnNames = new Map(
282
+ Object.keys(fields).map((name) => [name, applyCasing(casing, "columns", name)] as const)
283
+ )
284
+ const expressionState = expressionStateForTable(state, tableName, columnNames, casing)
58
285
  const columns = Object.entries(fields).map(([name, column]) => {
59
286
  const metadata = column.metadata
60
287
  const enumDefinition = metadata.enum
61
288
  const ddlType = metadata.ddlType ?? metadata.dbType.kind
62
289
  return {
63
- name,
290
+ name: columnNames.get(name) ?? name,
64
291
  ddlType,
65
292
  dbTypeKind: enumDefinition?.name ?? column.metadata.dbType.kind,
66
293
  typeKind: enumDefinition === undefined ? undefined : "e",
@@ -70,20 +297,24 @@ export const toTableModel = (table: Table.AnyTable): TableModel => {
70
297
  generated: column.metadata.generated,
71
298
  defaultSql: column.metadata.defaultValue === undefined
72
299
  ? undefined
73
- : normalizeDdlExpressionSql(column.metadata.defaultValue),
300
+ : normalizeDdlExpressionSql(column.metadata.defaultValue, expressionState),
74
301
  generatedSql: column.metadata.generatedValue === undefined
75
302
  ? undefined
76
- : normalizeDdlExpressionSql(column.metadata.generatedValue),
303
+ : normalizeDdlExpressionSql(column.metadata.generatedValue, expressionState),
77
304
  identity: column.metadata.identity,
78
305
  column
79
306
  }
80
307
  }) satisfies ReadonlyArray<ColumnModel>
81
308
  return {
82
309
  kind: "table",
83
- schemaName: state.schemaName,
84
- name: state.baseName,
310
+ schemaName,
311
+ name: tableName,
85
312
  columns,
86
- options: table[Table.OptionsSymbol],
313
+ options: normalizedOptions.map((option) =>
314
+ typeof option === "object" && option !== null && "kind" in option
315
+ ? mapOption(option, casing, expressionState)
316
+ : option as TableOptionSpec
317
+ ),
87
318
  table
88
319
  }
89
320
  }
@@ -121,17 +352,17 @@ export const fromDiscoveredValues = (values: ReadonlyArray<unknown>): SchemaMode
121
352
  const enums = new Map<string, EnumModel>()
122
353
  for (const value of values) {
123
354
  if (isEnumDefinition(value)) {
124
- enums.set(enumKey(value.schemaName, value.name), toEnumModel(value))
355
+ enums.set(modelIdentityKey(value.schemaName, value.name), toEnumModel(value))
125
356
  } else if (isTableDefinition(value)) {
126
357
  for (const enumModel of enumModelsOfTable(value)) {
127
- const key = enumKey(enumModel.schemaName, enumModel.name)
358
+ const key = modelIdentityKey(enumModel.schemaName, enumModel.name)
128
359
  const existing = enums.get(key)
129
360
  if (existing === undefined) {
130
361
  enums.set(key, enumModel)
131
362
  continue
132
363
  }
133
364
  if (JSON.stringify(existing.values) !== JSON.stringify(enumModel.values)) {
134
- throw new Error(`Conflicting enum definitions discovered for '${key}'`)
365
+ throw new Error(`Conflicting enum definitions discovered for '${enumKey(enumModel.schemaName, enumModel.name)}'`)
135
366
  }
136
367
  }
137
368
  }
@@ -148,3 +379,6 @@ export const tableKey = (schemaName: string | undefined, name: string): string =
148
379
 
149
380
  export const enumKey = (schemaName: string | undefined, name: string): string =>
150
381
  `${schemaName ?? "public"}.${name}`
382
+
383
+ const modelIdentityKey = (schemaName: string | undefined, name: string): string =>
384
+ JSON.stringify([schemaName ?? "public", name])
@@ -1,17 +1,20 @@
1
1
  import * as Expression from "../internal/scalar.js"
2
+ import type * as ExpressionAst from "../internal/expression-ast.js"
2
3
  import type { JsonPathUsageError } from "../internal/json/errors.js"
3
4
  import * as JsonPath from "../internal/json/path.js"
4
5
  import type {
5
6
  JsonDeleteAtPath,
6
7
  JsonInsertAtPath,
7
8
  JsonSetAtPath,
9
+ JsonTextResult,
8
10
  JsonValueAtPath
9
11
  } from "../internal/json/types.js"
12
+ import type { LiteralStringInput } from "../internal/table-options.js"
10
13
  import { json as postgresJson, jsonb as postgresJsonb } from "./internal/dsl.js"
11
14
 
12
15
  type PostgresJsonExpression<Runtime = unknown> = Expression.Scalar<
13
16
  Runtime,
14
- Expression.DbType.Json<"postgres", "json" | "jsonb">,
17
+ Expression.DbType.Json<"postgres" | "standard", "json" | "jsonb">,
15
18
  Expression.Nullability,
16
19
  string,
17
20
  Expression.ScalarKind,
@@ -29,10 +32,24 @@ type PostgresJsonbExpression<Runtime = unknown> = Expression.Scalar<
29
32
 
30
33
  type ExactJsonPathInput = JsonPath.ExactSegment | JsonPath.Path<any>
31
34
 
35
+ type JsonPathPredicateExpression = Expression.Scalar<
36
+ string | null,
37
+ Expression.DbType.Any,
38
+ Expression.Nullability,
39
+ string,
40
+ Expression.ScalarKind,
41
+ Expression.BindingId
42
+ >
43
+
44
+ type JsonPathPredicateQuery = JsonPath.Path<any> | JsonPathPredicateExpression | string
45
+
46
+ type JsonPathPredicateQueryInput<Query extends JsonPathPredicateQuery> =
47
+ Query extends string ? LiteralStringInput<Query> : Query
48
+
32
49
  type ExactJsonPathUsageError<Target> = {
33
50
  readonly __effect_qb_error__: "effect-qb: postgres json helpers only accept exact key/index paths"
34
51
  readonly __effect_qb_json_path__: Target
35
- readonly __effect_qb_hint__: "Use Postgres.Json.jsonb.path(...) when you need wildcard(), slice(), or descend() segments"
52
+ readonly __effect_qb_hint__: "Use Postgres.Jsonb.path(...) when you need wildcard(), slice(), or descend() segments"
36
53
  }
37
54
 
38
55
  type ExactJsonPathGuard<Target> = Target extends JsonPath.Path<any>
@@ -75,6 +92,16 @@ type JsonSetOutputOf<
75
92
  ? JsonSetAtPath<Root, JsonPath.Path<[Target]>, Next, Operation>
76
93
  : never
77
94
 
95
+ type JsonSetOutputWithCreateMissing<
96
+ Root,
97
+ Target extends JsonPath.Path<any> | JsonPath.CanonicalSegment,
98
+ Next,
99
+ Operation extends string,
100
+ CreateMissing extends boolean
101
+ > = false extends CreateMissing
102
+ ? Root | JsonSetOutputOf<Root, Target, Next, Operation>
103
+ : JsonSetOutputOf<Root, Target, Next, Operation>
104
+
78
105
  type JsonInsertOutputOf<
79
106
  Root,
80
107
  Target extends JsonPath.Path<any> | JsonPath.CanonicalSegment,
@@ -129,7 +156,7 @@ type JsonbOnlyUsageError<
129
156
  readonly __effect_qb_error__: "effect-qb: postgres jsonb helpers require a jsonb expression"
130
157
  readonly __effect_qb_json_operation__: Operation
131
158
  readonly __effect_qb_received_kind__: Expression.DbTypeOf<Value>["kind"]
132
- readonly __effect_qb_hint__: "Use Column.jsonb(...), Cast.to(..., Type.jsonb()), or Postgres.Json.jsonb.toJsonb(...)"
159
+ readonly __effect_qb_hint__: "Use Column.jsonb(...), Cast.to(..., Type.jsonb()), or Postgres.Jsonb.toJsonb(...)"
133
160
  }
134
161
 
135
162
  type JsonbBaseGuard<
@@ -144,17 +171,37 @@ type JsonNullabilityOf<Output> =
144
171
  ? Exclude<Output, null> extends never ? "always" : "maybe"
145
172
  : "never"
146
173
 
174
+ type JsonPathSegmentsOf<Target extends JsonPath.Path<any> | JsonPath.CanonicalSegment> =
175
+ Target extends JsonPath.Path<infer Segments extends readonly JsonPath.CanonicalSegment[]> ? Segments :
176
+ Target extends JsonPath.CanonicalSegment ? readonly [Target] :
177
+ readonly []
178
+
179
+ type JsonGetAccessKind<Target extends JsonPath.Path<any> | JsonPath.CanonicalSegment> =
180
+ Target extends JsonPath.Path<any>
181
+ ? JsonPath.IsExactPath<Target> extends true ? "jsonPath" : "jsonTraverse"
182
+ : Target extends JsonPath.ExactSegment ? "jsonGet" : "jsonAccess"
183
+
184
+ type JsonTextAccessKind<Target extends JsonPath.Path<any> | JsonPath.CanonicalSegment> =
185
+ Target extends JsonPath.Path<any>
186
+ ? JsonPath.IsExactPath<Target> extends true ? "jsonPathText" : "jsonTraverseText"
187
+ : Target extends JsonPath.ExactSegment ? "jsonGetText" : "jsonAccessText"
188
+
147
189
  type JsonResultExpression<
148
190
  Runtime,
149
- Db extends Expression.DbType.Json<any, any>
191
+ Db extends Expression.DbType.Json<any, any>,
192
+ Kind extends Expression.ScalarKind = Expression.ScalarKind,
193
+ Dependencies extends Expression.BindingId = Expression.BindingId,
194
+ Ast extends ExpressionAst.Any = never
150
195
  > = Expression.Scalar<
151
196
  Runtime,
152
197
  Db,
153
198
  JsonNullabilityOf<Runtime>,
154
199
  string,
155
- Expression.ScalarKind,
156
- Expression.BindingId
157
- >
200
+ Kind,
201
+ Dependencies
202
+ > & ([Ast] extends [never] ? unknown : {
203
+ readonly [ExpressionAst.TypeId]: Ast
204
+ })
158
205
 
159
206
  type JsonDbOf<Base extends PostgresJsonExpression<any>> =
160
207
  Expression.DbTypeOf<Base> extends Expression.DbType.Json<"postgres", infer Variant>
@@ -167,14 +214,17 @@ type JsonGetResultExpression<
167
214
  Operation extends string
168
215
  > = JsonResultExpression<
169
216
  JsonPathOutputOf<Expression.RuntimeOf<Base>, Target, Operation>,
170
- JsonDbOf<Base>
217
+ JsonDbOf<Base>,
218
+ Expression.KindOf<Base>,
219
+ Expression.DependenciesOf<Base>,
220
+ ExpressionAst.JsonAccessNode<JsonGetAccessKind<Target>, Base, JsonPathSegmentsOf<Target>>
171
221
  >
172
222
 
173
223
  type JsonTextRuntime<
174
224
  Base extends PostgresJsonExpression<any>,
175
225
  Target extends JsonPath.Path<any> | JsonPath.CanonicalSegment
176
226
  > =
177
- Extract<JsonPathOutputOf<Expression.RuntimeOf<Base>, Target, "json.text">, string> |
227
+ JsonTextResult<Exclude<JsonPathOutputOf<Expression.RuntimeOf<Base>, Target, "json.text">, JsonPathUsageError<any, any, any, any> | null>> |
178
228
  (null extends JsonPathOutputOf<Expression.RuntimeOf<Base>, Target, "json.text"> ? null : never)
179
229
 
180
230
  type JsonTextResultExpression<
@@ -185,9 +235,11 @@ type JsonTextResultExpression<
185
235
  Expression.DbType.Base<"postgres", "text">,
186
236
  JsonNullabilityOf<JsonTextRuntime<Base, Target>>,
187
237
  string,
188
- Expression.ScalarKind,
189
- Expression.BindingId
190
- >
238
+ Expression.KindOf<Base>,
239
+ Expression.DependenciesOf<Base>
240
+ > & {
241
+ readonly [ExpressionAst.TypeId]: ExpressionAst.JsonAccessNode<JsonTextAccessKind<Target>, Base, JsonPathSegmentsOf<Target>>
242
+ }
191
243
 
192
244
  const exactPath = <Segments extends readonly JsonPath.CanonicalSegment[]>(
193
245
  ...segments: Segments & ExactJsonPathSegmentsGuard<Segments>
@@ -222,7 +274,7 @@ const json = {
222
274
  typeof jsonGetDirect & {
223
275
  <Target extends ExactJsonPathInput>(
224
276
  target: Target & ExactJsonPathGuard<Target>
225
- ): <Base extends PostgresJsonExpression<any>>(base: Base) => ReturnType<typeof jsonGetDirect>
277
+ ): <Base extends PostgresJsonExpression<any>>(base: Base) => JsonGetResultExpression<Base, Target, "json.get">
226
278
  },
227
279
  access: <
228
280
  Base extends PostgresJsonExpression<any>,
@@ -245,7 +297,7 @@ const json = {
245
297
  typeof jsonTextDirect & {
246
298
  <Target extends ExactJsonPathInput>(
247
299
  target: Target & ExactJsonPathGuard<Target>
248
- ): <Base extends PostgresJsonExpression<any>>(base: Base) => ReturnType<typeof jsonTextDirect>
300
+ ): <Base extends PostgresJsonExpression<any>>(base: Base) => JsonTextResultExpression<Base, Target>
249
301
  },
250
302
  accessText: <
251
303
  Base extends PostgresJsonExpression<any>,
@@ -416,17 +468,20 @@ const jsonb = {
416
468
  set: <
417
469
  Base extends PostgresJsonExpression<any>,
418
470
  Target extends JsonPath.CanonicalSegment | JsonPath.Path<any>,
419
- Next extends Parameters<typeof postgresJsonb.set>[2]
471
+ Next extends Parameters<typeof postgresJsonb.set>[2],
472
+ CreateMissing extends boolean = true
420
473
  >(
421
474
  base: Base & JsonbBaseGuard<Base, "jsonb.set">,
422
- target: Target & JsonSetPathGuard<Expression.RuntimeOf<Base>, Target, Next, "json.set">,
475
+ target: Target & JsonSetPathGuard<Expression.RuntimeOf<Base>, Target, NoInfer<Next>, "json.set">,
423
476
  next: Next,
424
- options?: Parameters<typeof postgresJsonb.set>[3]
477
+ options?: {
478
+ readonly createMissing?: CreateMissing
479
+ }
425
480
  ): JsonResultExpression<
426
- JsonSetOutputOf<Expression.RuntimeOf<Base>, Target, Next, "json.set">,
481
+ JsonSetOutputWithCreateMissing<Expression.RuntimeOf<Base>, Target, Next, "json.set", CreateMissing>,
427
482
  Expression.DbTypeOf<Base>
428
483
  > => postgresJsonb.set(base as any, target as any, next, options) as unknown as JsonResultExpression<
429
- JsonSetOutputOf<Expression.RuntimeOf<Base>, Target, Next, "json.set">,
484
+ JsonSetOutputWithCreateMissing<Expression.RuntimeOf<Base>, Target, Next, "json.set", CreateMissing>,
430
485
  Expression.DbTypeOf<Base>
431
486
  >,
432
487
  insert: <
@@ -436,7 +491,7 @@ const jsonb = {
436
491
  InsertAfter extends boolean = false
437
492
  >(
438
493
  base: Base & JsonbBaseGuard<Base, "jsonb.insert">,
439
- target: Target & JsonInsertPathGuard<Expression.RuntimeOf<Base>, Target, Next, InsertAfter, "json.insert">,
494
+ target: Target & JsonInsertPathGuard<Expression.RuntimeOf<Base>, Target, NoInfer<Next>, NoInfer<InsertAfter>, "json.insert">,
440
495
  next: Next,
441
496
  options?: {
442
497
  readonly insertAfter?: InsertAfter
@@ -474,20 +529,41 @@ const jsonb = {
474
529
  base: Base & JsonbBaseGuard<Base, "jsonb.stripNulls">
475
530
  ) => postgresJsonb.stripNulls(base as Base),
476
531
  pathExists: <
477
- Base extends PostgresJsonExpression<any>
532
+ Base extends PostgresJsonExpression<any>,
533
+ Query extends JsonPathPredicateQuery
478
534
  >(
479
535
  base: Base & JsonbBaseGuard<Base, "jsonb.pathExists">,
480
- query: Parameters<typeof postgresJsonb.pathExists>[1]
536
+ query: JsonPathPredicateQueryInput<Query>
481
537
  ) => postgresJsonb.pathExists(base as Base, query),
482
538
  pathMatch: <
483
- Base extends PostgresJsonExpression<any>
539
+ Base extends PostgresJsonExpression<any>,
540
+ Query extends JsonPathPredicateQuery
484
541
  >(
485
542
  base: Base & JsonbBaseGuard<Base, "jsonb.pathMatch">,
486
- query: Parameters<typeof postgresJsonb.pathMatch>[1]
543
+ query: JsonPathPredicateQueryInput<Query>
487
544
  ) => postgresJsonb.pathMatch(base as Base, query)
488
545
  }
489
546
 
490
547
  /** Postgres shared JSON helpers for exact paths and functions that work on both json and jsonb. */
491
548
  export { json }
549
+ export const key = json.key
550
+ export const index = json.index
551
+ export const path = json.path
552
+ export const get = json.get
553
+ export const access = json.access
554
+ export const traverse = json.traverse
555
+ export const text = json.text
556
+ export const accessText = json.accessText
557
+ export const traverseText = json.traverseText
558
+ export const buildObject = json.buildObject
559
+ export const buildArray = json.buildArray
560
+ export const toJson = json.toJson
561
+ export const typeOf = json.typeOf
562
+ export const length = json.length
563
+ export const keys = json.keys
564
+ export const stripNulls = json.stripNulls
565
+ export const delete_ = json.delete
566
+ export { delete_ as delete }
567
+ export const remove = json.remove
492
568
  /** Postgres jsonb-only helpers for containment, mutation, wildcard paths, and SQL/JSON path predicates. */
493
569
  export { jsonb }
@@ -0,0 +1,38 @@
1
+ import { jsonb } from "./json.js"
2
+
3
+ /** Postgres jsonb-only helpers for containment, mutation, wildcard paths, and SQL/JSON path predicates. */
4
+ export { jsonb }
5
+ export const key = jsonb.key
6
+ export const index = jsonb.index
7
+ export const wildcard = jsonb.wildcard
8
+ export const slice = jsonb.slice
9
+ export const descend = jsonb.descend
10
+ export const path = jsonb.path
11
+ export const get = jsonb.get
12
+ export const access = jsonb.access
13
+ export const traverse = jsonb.traverse
14
+ export const text = jsonb.text
15
+ export const accessText = jsonb.accessText
16
+ export const traverseText = jsonb.traverseText
17
+ export const contains = jsonb.contains
18
+ export const containedBy = jsonb.containedBy
19
+ export const hasKey = jsonb.hasKey
20
+ export const keyExists = jsonb.keyExists
21
+ export const hasAnyKeys = jsonb.hasAnyKeys
22
+ export const hasAllKeys = jsonb.hasAllKeys
23
+ export const delete_ = jsonb.delete
24
+ export { delete_ as delete }
25
+ export const remove = jsonb.remove
26
+ export const set = jsonb.set
27
+ export const insert = jsonb.insert
28
+ export const concat = jsonb.concat
29
+ export const merge = jsonb.merge
30
+ export const buildObject = jsonb.buildObject
31
+ export const buildArray = jsonb.buildArray
32
+ export const toJsonb = jsonb.toJsonb
33
+ export const typeOf = jsonb.typeOf
34
+ export const length = jsonb.length
35
+ export const keys = jsonb.keys
36
+ export const stripNulls = jsonb.stripNulls
37
+ export const pathExists = jsonb.pathExists
38
+ export const pathMatch = jsonb.pathMatch