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,6 +1,9 @@
1
1
  import type * as Schema from "effect/Schema"
2
2
 
3
+ import type * as QueryAst from "./query-ast.js"
4
+ import type { Projection } from "./projections.js"
3
5
  import type * as Expression from "./scalar.js"
6
+ import type * as Casing from "./casing.js"
4
7
 
5
8
  /**
6
9
  * Mutable rendering state shared while serializing SQL for a concrete dialect.
@@ -11,12 +14,20 @@ import type * as Expression from "./scalar.js"
11
14
  export interface RenderState {
12
15
  readonly params: unknown[]
13
16
  readonly valueMappings?: Expression.DriverValueMappings
17
+ readonly casing?: Casing.Options
14
18
  readonly ctes: {
15
19
  readonly name: string
16
20
  readonly sql: string
17
21
  readonly recursive?: boolean
18
22
  }[]
19
23
  readonly cteNames: Set<string>
24
+ readonly cteSources: Map<string, unknown>
25
+ readonly sourceNames?: Map<string, {
26
+ readonly tableName: string
27
+ readonly columns: ReadonlyMap<string, string>
28
+ }>
29
+ readonly rowLocalColumns?: boolean
30
+ readonly allowExcluded?: boolean
20
31
  }
21
32
 
22
33
  export interface RenderValueContext {
@@ -25,6 +36,22 @@ export interface RenderValueContext {
25
36
  readonly driverValueMapping?: Expression.DriverValueMapping
26
37
  }
27
38
 
39
+ export const quoteDoubleQuotedIdentifier = (value: string): string => {
40
+ return `"${value.replaceAll("\"", "\"\"")}"`
41
+ }
42
+
43
+ export const quoteBacktickIdentifier = (value: string): string => {
44
+ return `\`${value.replaceAll("`", "``")}\``
45
+ }
46
+
47
+ export const renderDbTypeName = (value: string): string =>
48
+ value
49
+
50
+ export interface RenderedAst {
51
+ readonly sql: string
52
+ readonly projections: readonly Projection[]
53
+ }
54
+
28
55
  /**
29
56
  * Minimal runtime contract for a SQL dialect.
30
57
  *
@@ -38,4 +65,14 @@ export interface SqlDialect<Name extends string = string> {
38
65
  renderLiteral(value: unknown, state: RenderState, context?: RenderValueContext): string
39
66
  renderTableReference(tableName: string, baseTableName: string, schemaName?: string): string
40
67
  renderConcat(values: readonly string[]): string
68
+ renderQueryAst(
69
+ ast: QueryAst.Ast<Record<string, unknown>, any, QueryAst.QueryStatement>,
70
+ state: RenderState,
71
+ dialect: SqlDialect<Name>
72
+ ): RenderedAst
73
+ renderExpression(
74
+ expression: Expression.Any,
75
+ state: RenderState,
76
+ dialect: SqlDialect<Name>
77
+ ): string
41
78
  }
@@ -1,7 +1,11 @@
1
1
  import * as Expression from "./scalar.js"
2
2
  import * as Plan from "./row-set.js"
3
+ import { normalizeStatementFlag } from "./dsl-transaction-ddl-runtime.js"
3
4
 
4
5
  type DslMutationRuntimeContext = {
6
+ readonly profile: {
7
+ readonly dialect: string
8
+ }
5
9
  readonly makePlan: (...args: readonly any[]) => any
6
10
  readonly getAst: (plan: any) => any
7
11
  readonly getQueryState: (plan: any) => any
@@ -14,11 +18,21 @@ type DslMutationRuntimeContext = {
14
18
  readonly buildConflictTarget: (target: any, input: any) => any
15
19
  readonly mutationTargetClauses: (target: any) => readonly any[]
16
20
  readonly mutationAvailableSources: (target: any) => Record<string, any>
17
- readonly normalizeColumnList: (columns: string | readonly string[]) => readonly string[]
21
+ readonly normalizeConflictColumns: (target: any, columns: string | readonly string[]) => readonly string[]
18
22
  readonly targetSourceDetails: (target: any) => { readonly sourceName: string; readonly sourceBaseName: string }
19
23
  readonly sourceDetails: (source: any) => { readonly sourceName: string; readonly sourceBaseName: string }
20
24
  }
21
25
 
26
+ export const expectConflictClause = <
27
+ Conflict extends {
28
+ readonly kind: string
29
+ readonly action: string
30
+ readonly target?: { readonly kind: string; readonly name?: string }
31
+ } | undefined
32
+ >(
33
+ conflict: Conflict
34
+ ): Conflict => conflict
35
+
22
36
  export const makeDslMutationRuntime = (ctx: DslMutationRuntimeContext) => {
23
37
  const insert = (target: any, values?: Record<string, unknown>) => {
24
38
  const { sourceName, sourceBaseName } = ctx.targetSourceDetails(target)
@@ -105,7 +119,7 @@ export const makeDslMutationRuntime = (ctx: DslMutationRuntimeContext) => {
105
119
  const columns = ctx.normalizeInsertSelectColumns(selection)
106
120
  return ctx.makePlan({
107
121
  selection: current.selection,
108
- required: ctx.currentRequiredList(sourcePlan[Plan.TypeId].required).filter((name) => name !== sourceName),
122
+ required: ctx.currentRequiredList(sourcePlan[Plan.TypeId].required),
109
123
  available: current.available,
110
124
  dialect: current.dialect
111
125
  }, {
@@ -125,17 +139,23 @@ export const makeDslMutationRuntime = (ctx: DslMutationRuntimeContext) => {
125
139
  const currentAst = ctx.getAst(plan)
126
140
  const currentQuery = ctx.getQueryState(plan)
127
141
  const insertTarget = currentAst.into!.source
128
- const conflictTarget = ctx.buildConflictTarget(insertTarget, target)
142
+ const conflictTarget = expectConflictClause({
143
+ kind: "conflict",
144
+ action: "doNothing",
145
+ target: ctx.buildConflictTarget(insertTarget, target)
146
+ }).target
129
147
  const updateAssignments = options.update
130
148
  ? ctx.buildMutationAssignments(insertTarget, options.update)
131
149
  : []
132
150
  const updateWhere = options.where === undefined
133
151
  ? undefined
134
152
  : ctx.toDialectExpression(options.where)
153
+ const targetWhere = conflictTarget.kind === "columns" ? conflictTarget.where : undefined
135
154
  const required = [
136
155
  ...ctx.currentRequiredList(current.required),
137
156
  ...updateAssignments.flatMap((entry) => Object.keys(entry.value[Expression.TypeId].dependencies)),
138
- ...(updateWhere ? Object.keys(updateWhere[Expression.TypeId].dependencies) : [])
157
+ ...(updateWhere ? Object.keys(updateWhere[Expression.TypeId].dependencies) : []),
158
+ ...(targetWhere ? Object.keys(targetWhere[Expression.TypeId].dependencies) : [])
139
159
  ].filter((name, index, list) =>
140
160
  !(name in current.available) && list.indexOf(name) === index)
141
161
  return ctx.makePlan({
@@ -215,7 +235,7 @@ export const makeDslMutationRuntime = (ctx: DslMutationRuntimeContext) => {
215
235
  kind: "conflict",
216
236
  target: {
217
237
  kind: "columns",
218
- columns: ctx.normalizeColumnList(conflictColumns) as readonly [string, ...string[]]
238
+ columns: ctx.normalizeConflictColumns(target, conflictColumns) as readonly [string, ...string[]]
219
239
  },
220
240
  action: updateAssignments.length > 0 ? "doUpdate" : "doNothing",
221
241
  values: updateAssignments.length > 0 ? updateAssignments : undefined
@@ -250,6 +270,8 @@ export const makeDslMutationRuntime = (ctx: DslMutationRuntimeContext) => {
250
270
  }
251
271
 
252
272
  const truncate = (target: any, options: { readonly restartIdentity?: boolean; readonly cascade?: boolean } = {}) => {
273
+ const restartIdentity = normalizeStatementFlag(options.restartIdentity)
274
+ const cascade = normalizeStatementFlag(options.cascade)
253
275
  const { sourceName, sourceBaseName } = ctx.targetSourceDetails(target)
254
276
  return ctx.makePlan({
255
277
  selection: {},
@@ -267,8 +289,8 @@ export const makeDslMutationRuntime = (ctx: DslMutationRuntimeContext) => {
267
289
  },
268
290
  truncate: {
269
291
  kind: "truncate",
270
- restartIdentity: options.restartIdentity ?? false,
271
- cascade: options.cascade ?? false
292
+ restartIdentity,
293
+ cascade
272
294
  },
273
295
  where: [],
274
296
  having: [],
@@ -284,9 +306,6 @@ export const makeDslMutationRuntime = (ctx: DslMutationRuntimeContext) => {
284
306
  const onExpression = ctx.toDialectExpression(on)
285
307
  const matched = options.whenMatched
286
308
  const notMatched = options.whenNotMatched
287
- if (matched && "delete" in matched && "update" in matched) {
288
- throw new Error("merge whenMatched cannot specify both update and delete")
289
- }
290
309
  const matchedPredicate = matched?.predicate ? ctx.toDialectExpression(matched.predicate) : undefined
291
310
  const matchedAssignments = matched && "update" in matched && matched.update
292
311
  ? ctx.buildMutationAssignments(target, matched.update)
@@ -1,6 +1,5 @@
1
1
  import * as Expression from "./scalar.js"
2
2
  import * as Plan from "./row-set.js"
3
- import * as Table from "./table.js"
4
3
 
5
4
  type DslPlanRuntimeContext = {
6
5
  readonly profile: {
@@ -22,7 +21,27 @@ type DslPlanRuntimeContext = {
22
21
  readonly attachInsertSource: (plan: any, source: any) => any
23
22
  }
24
23
 
24
+ type LockMode = "update" | "share" | "lowPriority" | "ignore" | "quick"
25
+
26
+ export const renderSelectLockMode = (mode: LockMode): string =>
27
+ mode === "update" ? "for update" : "for share"
28
+
29
+ export const renderMysqlMutationLockMode = (
30
+ mode: LockMode,
31
+ _statement: "update" | "delete"
32
+ ): string => {
33
+ if (mode === "lowPriority") {
34
+ return " low_priority"
35
+ }
36
+ return mode === "ignore" ? " ignore" : " quick"
37
+ }
38
+
25
39
  export const makeDslPlanRuntime = (ctx: DslPlanRuntimeContext) => {
40
+ const sourceRequiredList = (source: any): readonly string[] =>
41
+ typeof source === "object" && source !== null && "required" in source
42
+ ? ctx.currentRequiredList(source.required)
43
+ : []
44
+
26
45
  const buildSetOperation = (kind: string, all: boolean, left: any, right: any) => {
27
46
  const leftState = left[Plan.TypeId]
28
47
  const leftAst = ctx.getAst(left)
@@ -92,32 +111,26 @@ export const makeDslPlanRuntime = (ctx: DslPlanRuntimeContext) => {
92
111
  return ctx.attachInsertSource(plan, source)
93
112
  }
94
113
 
95
- if (
96
- typeof source !== "object" ||
97
- source === null ||
98
- ("kind" in source && source.kind === "values" && !("name" in source)) ||
99
- (!(Table.TypeId in source) && !("name" in source && "baseName" in source))
100
- ) {
101
- throw new Error("from(...) requires an aliased source in select/update statements")
102
- }
103
-
104
114
  const sourceLike = source
105
115
  const { sourceName, sourceBaseName } = ctx.sourceDetails(sourceLike)
106
116
  const presenceWitnesses = ctx.presenceWitnessesOfSourceLike(sourceLike)
117
+ const sourceRequired = sourceRequiredList(sourceLike)
107
118
 
108
119
  if (currentQuery.statement === "select") {
120
+ const nextAvailable = {
121
+ [sourceName]: {
122
+ name: sourceName,
123
+ mode: "required",
124
+ baseName: sourceBaseName,
125
+ _presentFormula: ctx.trueFormula(),
126
+ _presenceWitnesses: presenceWitnesses
127
+ }
128
+ }
109
129
  return ctx.makePlan({
110
130
  selection: current.selection,
111
- required: ctx.currentRequiredList(current.required).filter((name) => name !== sourceName),
112
- available: {
113
- [sourceName]: {
114
- name: sourceName,
115
- mode: "required",
116
- baseName: sourceBaseName,
117
- _presentFormula: ctx.trueFormula(),
118
- _presenceWitnesses: presenceWitnesses
119
- }
120
- },
131
+ required: [...ctx.currentRequiredList(current.required), ...sourceRequired].filter((name, index, values) =>
132
+ !(name in nextAvailable) && values.indexOf(name) === index),
133
+ available: nextAvailable,
121
134
  dialect: current.dialect
122
135
  }, {
123
136
  ...currentAst,
@@ -143,7 +156,8 @@ export const makeDslPlanRuntime = (ctx: DslPlanRuntimeContext) => {
143
156
  }
144
157
  return ctx.makePlan({
145
158
  selection: current.selection,
146
- required: ctx.currentRequiredList(current.required).filter((name) => !(name in nextAvailable)),
159
+ required: [...ctx.currentRequiredList(current.required), ...sourceRequired].filter((name, index, values) =>
160
+ !(name in nextAvailable) && values.indexOf(name) === index),
147
161
  available: nextAvailable,
148
162
  dialect: current.dialect
149
163
  }, {
@@ -160,7 +174,7 @@ export const makeDslPlanRuntime = (ctx: DslPlanRuntimeContext) => {
160
174
  }, currentQuery.assumptions, currentQuery.capabilities, currentQuery.statement)
161
175
  }
162
176
 
163
- throw new Error(`from(...) is not supported for ${currentQuery.statement} statements`)
177
+ return plan
164
178
  }
165
179
 
166
180
  const having = (predicate: any) =>
@@ -195,6 +209,7 @@ export const makeDslPlanRuntime = (ctx: DslPlanRuntimeContext) => {
195
209
  const currentQuery = ctx.getQueryState(plan)
196
210
  const { sourceName, sourceBaseName } = ctx.sourceDetails(table)
197
211
  const presenceWitnesses = ctx.presenceWitnessesOfSourceLike(table)
212
+ const sourceRequired = sourceRequiredList(table)
198
213
  const nextAvailable = {
199
214
  ...current.available,
200
215
  [sourceName]: {
@@ -207,7 +222,8 @@ export const makeDslPlanRuntime = (ctx: DslPlanRuntimeContext) => {
207
222
  }
208
223
  return ctx.makePlan({
209
224
  selection: current.selection,
210
- required: ctx.currentRequiredList(current.required).filter((name) => !(name in nextAvailable)),
225
+ required: [...ctx.currentRequiredList(current.required), ...sourceRequired].filter((name, index, values) =>
226
+ !(name in nextAvailable) && values.indexOf(name) === index),
211
227
  available: nextAvailable,
212
228
  dialect: current.dialect ?? table[Plan.TypeId]?.dialect ?? table.dialect
213
229
  }, {
@@ -230,6 +246,7 @@ export const makeDslPlanRuntime = (ctx: DslPlanRuntimeContext) => {
230
246
  const onFormula = ctx.formulaOfExpressionRuntime(onExpression)
231
247
  const { sourceName, sourceBaseName } = ctx.sourceDetails(table)
232
248
  const presenceWitnesses = ctx.presenceWitnessesOfSourceLike(table)
249
+ const sourceRequired = sourceRequiredList(table)
233
250
  const baseAvailable = (kind === "right" || kind === "full"
234
251
  ? Object.fromEntries(
235
252
  Object.entries(current.available as Record<string, any>).map(([name, source]) => [name, {
@@ -253,7 +270,7 @@ export const makeDslPlanRuntime = (ctx: DslPlanRuntimeContext) => {
253
270
  }
254
271
  return ctx.makePlan({
255
272
  selection: current.selection,
256
- required: [...ctx.currentRequiredList(current.required), ...ctx.extractRequiredFromDialectInputRuntime(on)].filter((name, index, values) =>
273
+ required: [...ctx.currentRequiredList(current.required), ...sourceRequired, ...ctx.extractRequiredFromDialectInputRuntime(on)].filter((name, index, values) =>
257
274
  !(name in nextAvailable) && values.indexOf(name) === index),
258
275
  available: nextAvailable,
259
276
  dialect: current.dialect ?? table.dialect ?? onExpression[Expression.TypeId].dialect
@@ -20,20 +20,12 @@ type DslQueryRuntimeContext = {
20
20
 
21
21
  export const makeDslQueryRuntime = (ctx: DslQueryRuntimeContext) => {
22
22
  const values = (rows: readonly [Record<string, any>, ...Record<string, any>[]]) => {
23
- if (rows.length === 0) {
24
- throw new Error("values(...) requires at least one row")
25
- }
26
- const normalizedRows = rows.map((row) => ctx.normalizeValuesRow(row)) as unknown as readonly [
27
- Record<string, Expression.Any>,
28
- ...Record<string, Expression.Any>[]
29
- ]
23
+ const [first, ...rest] = rows
24
+ const normalizedRows = [
25
+ ctx.normalizeValuesRow(first),
26
+ ...rest.map((row) => ctx.normalizeValuesRow(row))
27
+ ] satisfies readonly [Record<string, Expression.Any>, ...Record<string, Expression.Any>[]]
30
28
  const columnNames = Object.keys(normalizedRows[0]!)
31
- for (const row of normalizedRows) {
32
- const rowKeys = Object.keys(row)
33
- if (rowKeys.length !== columnNames.length || !rowKeys.every((key, index) => key === columnNames[index])) {
34
- throw new Error("values(...) rows must project the same columns in the same order")
35
- }
36
- }
37
29
  return Object.assign(Object.create(ctx.ValuesInputProto), {
38
30
  kind: "values",
39
31
  dialect: ctx.profile.dialect,
@@ -45,20 +37,6 @@ export const makeDslQueryRuntime = (ctx: DslQueryRuntimeContext) => {
45
37
  const unnest = (columns: Record<string, readonly any[]>, alias: string) => {
46
38
  const normalizedColumns = ctx.normalizeUnnestColumns(columns)
47
39
  const columnNames = Object.keys(normalizedColumns)
48
- if (columnNames.length === 0) {
49
- throw new Error("unnest(...) requires at least one column array")
50
- }
51
- const firstColumn = normalizedColumns[columnNames[0] as keyof typeof normalizedColumns]
52
- const rowCount = firstColumn?.length ?? 0
53
- if (rowCount === 0) {
54
- throw new Error("unnest(...) requires at least one row")
55
- }
56
- for (const columnName of columnNames) {
57
- const values = normalizedColumns[columnName]!
58
- if (values.length !== rowCount) {
59
- throw new Error("unnest(...) column arrays must have the same length")
60
- }
61
- }
62
40
  const firstRow = Object.fromEntries(
63
41
  columnNames.map((columnName) => [columnName, normalizedColumns[columnName]![0]!])
64
42
  ) as Record<string, Expression.Any>
@@ -97,8 +75,8 @@ export const makeDslQueryRuntime = (ctx: DslQueryRuntimeContext) => {
97
75
  return Object.assign(source, columns)
98
76
  }
99
77
 
100
- const select = (selection: any) =>
101
- ctx.makePlan({
78
+ const select = (selection: any = {}) => {
79
+ return ctx.makePlan({
102
80
  selection,
103
81
  required: ctx.extractRequiredRuntime(selection),
104
82
  available: {},
@@ -112,6 +90,7 @@ export const makeDslQueryRuntime = (ctx: DslQueryRuntimeContext) => {
112
90
  groupBy: [],
113
91
  orderBy: []
114
92
  }, undefined, "read", "select")
93
+ }
115
94
 
116
95
  const groupBy = (...values: readonly Expression.Any[]) =>
117
96
  (plan: any) => {
@@ -132,8 +111,8 @@ export const makeDslQueryRuntime = (ctx: DslQueryRuntimeContext) => {
132
111
  }, currentQuery.assumptions, currentQuery.capabilities, currentQuery.statement)
133
112
  }
134
113
 
135
- const returning = (selection: any) =>
136
- (plan: any) => {
114
+ const returning = (selection: any) => {
115
+ return (plan: any) => {
137
116
  const current = plan[Plan.TypeId]
138
117
  const currentAst = ctx.getAst(plan)
139
118
  const currentQuery = ctx.getQueryState(plan)
@@ -148,6 +127,7 @@ export const makeDslQueryRuntime = (ctx: DslQueryRuntimeContext) => {
148
127
  select: selection
149
128
  }, currentQuery.assumptions, currentQuery.capabilities, currentQuery.statement, currentQuery.target, currentQuery.insertSource)
150
129
  }
130
+ }
151
131
 
152
132
  return {
153
133
  values,
@@ -10,9 +10,44 @@ type DslTransactionDdlRuntimeContext = {
10
10
  readonly defaultIndexName: (tableName: string, columns: readonly string[], unique: boolean) => string
11
11
  }
12
12
 
13
+ export const renderTransactionIsolationLevel = (
14
+ isolationLevel: unknown
15
+ ): string => {
16
+ if (isolationLevel === undefined) {
17
+ return ""
18
+ }
19
+ return `isolation level ${isolationLevel as string}`
20
+ }
21
+
22
+ export const expectDdlClauseKind = <
23
+ Ddl extends { readonly kind: string },
24
+ Kind extends Ddl["kind"]
25
+ >(
26
+ ddl: Ddl | undefined,
27
+ _kind: Kind
28
+ ): Extract<Ddl, { readonly kind: Kind }> =>
29
+ ddl as Extract<Ddl, { readonly kind: Kind }>
30
+
31
+ export const expectTruncateClause = <
32
+ Truncate extends { readonly kind: string }
33
+ >(
34
+ truncate: Truncate | undefined
35
+ ): Extract<Truncate, { readonly kind: "truncate" }> =>
36
+ truncate as Extract<Truncate, { readonly kind: "truncate" }>
37
+
38
+ export const normalizeStatementFlag = (value: unknown): boolean =>
39
+ (value as boolean | undefined) ?? false
40
+
41
+ export const normalizeStatementIdentifier = (
42
+ _apiName: string,
43
+ _identifierName: string,
44
+ value: unknown
45
+ ): string =>
46
+ value as string
47
+
13
48
  export const makeDslTransactionDdlRuntime = (ctx: DslTransactionDdlRuntimeContext) => {
14
- const transaction = (options: { readonly isolationLevel?: any; readonly readOnly?: boolean } = {}) =>
15
- ctx.makePlan({
49
+ const transaction = (options: { readonly isolationLevel?: any; readonly readOnly?: boolean } = {}) => {
50
+ return ctx.makePlan({
16
51
  selection: {},
17
52
  required: [],
18
53
  available: {},
@@ -31,6 +66,7 @@ export const makeDslTransactionDdlRuntime = (ctx: DslTransactionDdlRuntimeContex
31
66
  groupBy: [],
32
67
  orderBy: []
33
68
  }, undefined, "transaction", "transaction")
69
+ }
34
70
 
35
71
  const commit = () =>
36
72
  ctx.makePlan({
@@ -70,8 +106,8 @@ export const makeDslTransactionDdlRuntime = (ctx: DslTransactionDdlRuntimeContex
70
106
  orderBy: []
71
107
  }, undefined, "transaction", "rollback")
72
108
 
73
- const savepoint = (name: string) =>
74
- ctx.makePlan({
109
+ const savepoint = (name: string) => {
110
+ return ctx.makePlan({
75
111
  selection: {},
76
112
  required: [],
77
113
  available: {},
@@ -89,9 +125,10 @@ export const makeDslTransactionDdlRuntime = (ctx: DslTransactionDdlRuntimeContex
89
125
  groupBy: [],
90
126
  orderBy: []
91
127
  }, undefined, "transaction", "savepoint")
128
+ }
92
129
 
93
- const rollbackTo = (name: string) =>
94
- ctx.makePlan({
130
+ const rollbackTo = (name: string) => {
131
+ return ctx.makePlan({
95
132
  selection: {},
96
133
  required: [],
97
134
  available: {},
@@ -109,9 +146,10 @@ export const makeDslTransactionDdlRuntime = (ctx: DslTransactionDdlRuntimeContex
109
146
  groupBy: [],
110
147
  orderBy: []
111
148
  }, undefined, "transaction", "rollbackTo")
149
+ }
112
150
 
113
- const releaseSavepoint = (name: string) =>
114
- ctx.makePlan({
151
+ const releaseSavepoint = (name: string) => {
152
+ return ctx.makePlan({
115
153
  selection: {},
116
154
  required: [],
117
155
  available: {},
@@ -129,8 +167,10 @@ export const makeDslTransactionDdlRuntime = (ctx: DslTransactionDdlRuntimeContex
129
167
  groupBy: [],
130
168
  orderBy: []
131
169
  }, undefined, "transaction", "releaseSavepoint")
170
+ }
132
171
 
133
172
  const createTable = (target: any, options: { readonly ifNotExists?: boolean } = {}) => {
173
+ const ifNotExists = normalizeStatementFlag(options.ifNotExists)
134
174
  const { sourceName, sourceBaseName } = ctx.targetSourceDetails(target)
135
175
  return ctx.makePlan({
136
176
  selection: {},
@@ -148,7 +188,7 @@ export const makeDslTransactionDdlRuntime = (ctx: DslTransactionDdlRuntimeContex
148
188
  },
149
189
  ddl: {
150
190
  kind: "createTable",
151
- ifNotExists: options.ifNotExists ?? false
191
+ ifNotExists
152
192
  },
153
193
  where: [],
154
194
  having: [],
@@ -159,6 +199,7 @@ export const makeDslTransactionDdlRuntime = (ctx: DslTransactionDdlRuntimeContex
159
199
  }
160
200
 
161
201
  const dropTable = (target: any, options: { readonly ifExists?: boolean } = {}) => {
202
+ const ifExists = normalizeStatementFlag(options.ifExists)
162
203
  const { sourceName, sourceBaseName } = ctx.targetSourceDetails(target)
163
204
  return ctx.makePlan({
164
205
  selection: {},
@@ -176,7 +217,7 @@ export const makeDslTransactionDdlRuntime = (ctx: DslTransactionDdlRuntimeContex
176
217
  },
177
218
  ddl: {
178
219
  kind: "dropTable",
179
- ifExists: options.ifExists ?? false
220
+ ifExists
180
221
  },
181
222
  where: [],
182
223
  having: [],
@@ -188,6 +229,9 @@ export const makeDslTransactionDdlRuntime = (ctx: DslTransactionDdlRuntimeContex
188
229
 
189
230
  const createIndex = (target: any, columns: string | readonly string[], options: { readonly name?: string; readonly unique?: boolean; readonly ifNotExists?: boolean } = {}) => {
190
231
  const normalizedColumns = ctx.normalizeColumnList(columns)
232
+ const unique = normalizeStatementFlag(options.unique)
233
+ const ifNotExists = normalizeStatementFlag(options.ifNotExists)
234
+ const name = options.name
191
235
  const { sourceName, sourceBaseName } = ctx.targetSourceDetails(target)
192
236
  return ctx.makePlan({
193
237
  selection: {},
@@ -205,10 +249,10 @@ export const makeDslTransactionDdlRuntime = (ctx: DslTransactionDdlRuntimeContex
205
249
  },
206
250
  ddl: {
207
251
  kind: "createIndex",
208
- name: options.name ?? ctx.defaultIndexName(sourceBaseName, normalizedColumns, options.unique ?? false),
252
+ name: name ?? ctx.defaultIndexName(sourceBaseName, normalizedColumns, unique),
209
253
  columns: normalizedColumns,
210
- unique: options.unique ?? false,
211
- ifNotExists: options.ifNotExists ?? false
254
+ unique,
255
+ ifNotExists
212
256
  },
213
257
  where: [],
214
258
  having: [],
@@ -220,6 +264,8 @@ export const makeDslTransactionDdlRuntime = (ctx: DslTransactionDdlRuntimeContex
220
264
 
221
265
  const dropIndex = (target: any, columns: string | readonly string[], options: { readonly name?: string; readonly ifExists?: boolean } = {}) => {
222
266
  const normalizedColumns = ctx.normalizeColumnList(columns)
267
+ const ifExists = normalizeStatementFlag(options.ifExists)
268
+ const name = options.name
223
269
  const { sourceName, sourceBaseName } = ctx.targetSourceDetails(target)
224
270
  return ctx.makePlan({
225
271
  selection: {},
@@ -237,8 +283,8 @@ export const makeDslTransactionDdlRuntime = (ctx: DslTransactionDdlRuntimeContex
237
283
  },
238
284
  ddl: {
239
285
  kind: "dropIndex",
240
- name: options.name ?? ctx.defaultIndexName(sourceBaseName, normalizedColumns, false),
241
- ifExists: options.ifExists ?? false
286
+ name: name ?? ctx.defaultIndexName(sourceBaseName, normalizedColumns, false),
287
+ ifExists
242
288
  },
243
289
  where: [],
244
290
  having: [],