effect-qb 0.15.0 → 0.17.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 (88) hide show
  1. package/dist/mysql.js +1957 -595
  2. package/dist/postgres/metadata.js +2507 -182
  3. package/dist/postgres.js +9587 -8201
  4. package/dist/sqlite.js +8360 -0
  5. package/package.json +7 -2
  6. package/src/internal/column-state.ts +7 -0
  7. package/src/internal/column.ts +22 -0
  8. package/src/internal/derived-table.ts +29 -3
  9. package/src/internal/dialect.ts +14 -1
  10. package/src/internal/dsl-mutation-runtime.ts +173 -4
  11. package/src/internal/dsl-plan-runtime.ts +165 -20
  12. package/src/internal/dsl-query-runtime.ts +60 -6
  13. package/src/internal/dsl-transaction-ddl-runtime.ts +72 -2
  14. package/src/internal/executor.ts +62 -13
  15. package/src/internal/expression-ast.ts +3 -2
  16. package/src/internal/grouping-key.ts +141 -1
  17. package/src/internal/implication-runtime.ts +2 -1
  18. package/src/internal/json/types.ts +155 -40
  19. package/src/internal/predicate/analysis.ts +103 -1
  20. package/src/internal/predicate/atom.ts +7 -0
  21. package/src/internal/predicate/context.ts +170 -17
  22. package/src/internal/predicate/key.ts +64 -2
  23. package/src/internal/predicate/normalize.ts +115 -34
  24. package/src/internal/predicate/runtime.ts +144 -13
  25. package/src/internal/query.ts +563 -103
  26. package/src/internal/renderer.ts +39 -2
  27. package/src/internal/runtime/driver-value-mapping.ts +244 -0
  28. package/src/internal/runtime/normalize.ts +62 -38
  29. package/src/internal/runtime/schema.ts +5 -3
  30. package/src/internal/runtime/value.ts +153 -30
  31. package/src/internal/scalar.ts +11 -0
  32. package/src/internal/table-options.ts +108 -1
  33. package/src/internal/table.ts +87 -29
  34. package/src/mysql/column.ts +19 -2
  35. package/src/mysql/datatypes/index.ts +21 -0
  36. package/src/mysql/errors/catalog.ts +5 -5
  37. package/src/mysql/errors/normalize.ts +2 -2
  38. package/src/mysql/executor.ts +20 -5
  39. package/src/mysql/internal/dialect.ts +12 -6
  40. package/src/mysql/internal/dsl.ts +995 -263
  41. package/src/mysql/internal/renderer.ts +13 -3
  42. package/src/mysql/internal/sql-expression-renderer.ts +530 -128
  43. package/src/mysql/query.ts +9 -2
  44. package/src/mysql/renderer.ts +7 -2
  45. package/src/mysql/table.ts +38 -12
  46. package/src/postgres/cast.ts +22 -7
  47. package/src/postgres/column.ts +5 -2
  48. package/src/postgres/errors/normalize.ts +2 -2
  49. package/src/postgres/executor.ts +68 -10
  50. package/src/postgres/function/core.ts +19 -1
  51. package/src/postgres/internal/dialect.ts +12 -6
  52. package/src/postgres/internal/dsl.ts +958 -288
  53. package/src/postgres/internal/renderer.ts +13 -3
  54. package/src/postgres/internal/schema-ddl.ts +2 -1
  55. package/src/postgres/internal/schema-model.ts +6 -3
  56. package/src/postgres/internal/sql-expression-renderer.ts +477 -96
  57. package/src/postgres/json.ts +57 -17
  58. package/src/postgres/query.ts +9 -2
  59. package/src/postgres/renderer.ts +7 -2
  60. package/src/postgres/schema-management.ts +91 -4
  61. package/src/postgres/schema.ts +1 -1
  62. package/src/postgres/table.ts +189 -53
  63. package/src/postgres/type.ts +4 -0
  64. package/src/sqlite/column.ts +128 -0
  65. package/src/sqlite/datatypes/index.ts +79 -0
  66. package/src/sqlite/datatypes/spec.ts +98 -0
  67. package/src/sqlite/errors/catalog.ts +103 -0
  68. package/src/sqlite/errors/fields.ts +19 -0
  69. package/src/sqlite/errors/index.ts +19 -0
  70. package/src/sqlite/errors/normalize.ts +229 -0
  71. package/src/sqlite/errors/requirements.ts +71 -0
  72. package/src/sqlite/errors/types.ts +29 -0
  73. package/src/sqlite/executor.ts +227 -0
  74. package/src/sqlite/function/aggregate.ts +2 -0
  75. package/src/sqlite/function/core.ts +2 -0
  76. package/src/sqlite/function/index.ts +19 -0
  77. package/src/sqlite/function/string.ts +2 -0
  78. package/src/sqlite/function/temporal.ts +100 -0
  79. package/src/sqlite/function/window.ts +2 -0
  80. package/src/sqlite/internal/dialect.ts +37 -0
  81. package/src/sqlite/internal/dsl.ts +6926 -0
  82. package/src/sqlite/internal/renderer.ts +47 -0
  83. package/src/sqlite/internal/sql-expression-renderer.ts +1821 -0
  84. package/src/sqlite/json.ts +2 -0
  85. package/src/sqlite/query.ts +196 -0
  86. package/src/sqlite/renderer.ts +24 -0
  87. package/src/sqlite/table.ts +183 -0
  88. package/src/sqlite.ts +22 -0
@@ -4,6 +4,7 @@ import type { PredicateAtom } from "./atom.js"
4
4
  import type {
5
5
  EqColumnAtom,
6
6
  EqLiteralAtom,
7
+ LiteralSetAtom,
7
8
  NeqLiteralAtom,
8
9
  NonNullAtom,
9
10
  NullAtom,
@@ -24,6 +25,7 @@ export interface RuntimeContext {
24
25
  readonly nullKeys: ReadonlySet<string>
25
26
  readonly eqLiterals: ReadonlyMap<string, string>
26
27
  readonly neqLiterals: ReadonlyMap<string, ReadonlySet<string>>
28
+ readonly literalSets: ReadonlyMap<string, ReadonlySet<string>>
27
29
  readonly sourceNames: ReadonlySet<string>
28
30
  readonly contradiction: boolean
29
31
  readonly unknown: boolean
@@ -34,6 +36,7 @@ type MutableContext = {
34
36
  nullKeys: Set<string>
35
37
  eqLiterals: Map<string, string>
36
38
  neqLiterals: Map<string, Set<string>>
39
+ literalSets: Map<string, Set<string>>
37
40
  sourceNames: Set<string>
38
41
  contradiction: boolean
39
42
  unknown: boolean
@@ -72,6 +75,7 @@ const emptyContext = (): MutableContext => ({
72
75
  nullKeys: new Set(),
73
76
  eqLiterals: new Map(),
74
77
  neqLiterals: new Map(),
78
+ literalSets: new Map(),
75
79
  sourceNames: new Set(),
76
80
  contradiction: false,
77
81
  unknown: false
@@ -84,6 +88,9 @@ const cloneContext = (context: MutableContext): MutableContext => ({
84
88
  neqLiterals: new Map(
85
89
  Array.from(context.neqLiterals.entries(), ([key, values]) => [key, new Set(values)])
86
90
  ),
91
+ literalSets: new Map(
92
+ Array.from(context.literalSets.entries(), ([key, values]) => [key, new Set(values)])
93
+ ),
87
94
  sourceNames: new Set(context.sourceNames),
88
95
  contradiction: context.contradiction,
89
96
  unknown: context.unknown
@@ -91,7 +98,26 @@ const cloneContext = (context: MutableContext): MutableContext => ({
91
98
 
92
99
  const freezeContext = (context: MutableContext): RuntimeContext => context
93
100
 
94
- const sourceNameOfKey = (key: string): string => key.split(".", 1)[0] ?? key
101
+ export const columnPredicateKey = (tableName: string, columnName: string): string =>
102
+ JSON.stringify([tableName, columnName])
103
+
104
+ const columnPredicateKeyParts = (key: string): readonly [string, string] | undefined => {
105
+ const jsonSeparator = key.indexOf("#json:")
106
+ const columnKey = jsonSeparator === -1 ? key : key.slice(0, jsonSeparator)
107
+ try {
108
+ const parsed = JSON.parse(columnKey) as unknown
109
+ return Array.isArray(parsed) &&
110
+ parsed.length === 2 &&
111
+ typeof parsed[0] === "string" &&
112
+ typeof parsed[1] === "string"
113
+ ? [parsed[0], parsed[1]]
114
+ : undefined
115
+ } catch {
116
+ return undefined
117
+ }
118
+ }
119
+
120
+ const sourceNameOfKey = (key: string): string => columnPredicateKeyParts(key)?.[0] ?? key.split(".", 1)[0] ?? key
95
121
 
96
122
  const addSourceName = (context: MutableContext, key: string): void => {
97
123
  context.sourceNames.add(sourceNameOfKey(key))
@@ -123,7 +149,12 @@ const addEqLiteral = (context: MutableContext, key: string, value: string): void
123
149
  if (neqValues?.has(value)) {
124
150
  context.contradiction = true
125
151
  }
152
+ const existingSet = context.literalSets.get(key)
153
+ if (existingSet !== undefined && !existingSet.has(value)) {
154
+ context.contradiction = true
155
+ }
126
156
  context.eqLiterals.set(key, value)
157
+ context.literalSets.set(key, new Set([value]))
127
158
  }
128
159
 
129
160
  const addNeqLiteral = (context: MutableContext, key: string, value: string): void => {
@@ -136,6 +167,24 @@ const addNeqLiteral = (context: MutableContext, key: string, value: string): voi
136
167
  context.neqLiterals.set(key, values)
137
168
  }
138
169
 
170
+ const addLiteralSet = (context: MutableContext, key: string, values: ReadonlySet<string>): void => {
171
+ addNonNull(context, key)
172
+ const existingEq = context.eqLiterals.get(key)
173
+ if (existingEq !== undefined && !values.has(existingEq)) {
174
+ context.contradiction = true
175
+ }
176
+ const existing = context.literalSets.get(key)
177
+ context.literalSets.set(
178
+ key,
179
+ existing === undefined
180
+ ? new Set(values)
181
+ : new Set(Array.from(existing).filter((value) => values.has(value)))
182
+ )
183
+ if (context.literalSets.get(key)?.size === 0) {
184
+ context.contradiction = true
185
+ }
186
+ }
187
+
139
188
  const applyEqColumn = (context: MutableContext, left: string, right: string): void => {
140
189
  const leftValue = context.eqLiterals.get(left)
141
190
  const rightValue = context.eqLiterals.get(right)
@@ -176,6 +225,9 @@ const applyAtom = (context: MutableContext, atom: PredicateAtom): void => {
176
225
  case "neq-literal":
177
226
  addNeqLiteral(context, atom.key, atom.value)
178
227
  return
228
+ case "literal-set":
229
+ addLiteralSet(context, atom.key, new Set(atom.values))
230
+ return
179
231
  case "eq-column":
180
232
  applyEqColumn(context, atom.left, atom.right)
181
233
  return
@@ -199,6 +251,9 @@ const applyNegativeAtom = (context: MutableContext, atom: PredicateAtom): void =
199
251
  case "neq-literal":
200
252
  addEqLiteral(context, atom.key, atom.value)
201
253
  return
254
+ case "literal-set":
255
+ addNonNull(context, atom.key)
256
+ return
202
257
  case "eq-column":
203
258
  addNonNull(context, atom.left)
204
259
  addNonNull(context, atom.right)
@@ -240,6 +295,21 @@ const intersectNeqLiterals = (
240
295
  return result
241
296
  }
242
297
 
298
+ const unionLiteralSets = (
299
+ left: ReadonlyMap<string, ReadonlySet<string>>,
300
+ right: ReadonlyMap<string, ReadonlySet<string>>
301
+ ): Map<string, Set<string>> => {
302
+ const result = new Map<string, Set<string>>()
303
+ for (const [key, leftValues] of left) {
304
+ const rightValues = right.get(key)
305
+ if (rightValues === undefined) {
306
+ continue
307
+ }
308
+ result.set(key, new Set([...leftValues, ...rightValues]))
309
+ }
310
+ return result
311
+ }
312
+
243
313
  const intersectContexts = (left: MutableContext, right: MutableContext): MutableContext => {
244
314
  if (left.contradiction) {
245
315
  return cloneContext(right)
@@ -252,6 +322,7 @@ const intersectContexts = (left: MutableContext, right: MutableContext): Mutable
252
322
  nullKeys: new Set(Array.from(left.nullKeys).filter((key) => right.nullKeys.has(key))),
253
323
  eqLiterals: intersectEqLiterals(left.eqLiterals, right.eqLiterals),
254
324
  neqLiterals: intersectNeqLiterals(left.neqLiterals, right.neqLiterals),
325
+ literalSets: unionLiteralSets(left.literalSets, right.literalSets),
255
326
  sourceNames: new Set(Array.from(left.sourceNames).filter((name) => right.sourceNames.has(name))),
256
327
  contradiction: false,
257
328
  unknown: left.unknown || right.unknown
@@ -334,7 +405,59 @@ const astOf = (value: Expression.Any): ExpressionAst.Any =>
334
405
 
335
406
  const columnKeyOfExpression = (value: Expression.Any): string | undefined => {
336
407
  const ast = astOf(value)
337
- return ast.kind === "column" ? `${ast.tableName}.${ast.columnName}` : undefined
408
+ return ast.kind === "column" ? columnPredicateKey(ast.tableName, ast.columnName) : undefined
409
+ }
410
+
411
+ const sameDbType = (left: Expression.DbType.Any, right: Expression.DbType.Any): boolean =>
412
+ left.dialect === right.dialect && left.kind === right.kind
413
+
414
+ const escapeJsonPathPredicateKeySegment = (value: string): string =>
415
+ value.replaceAll("\\", "\\\\").replaceAll(".", "\\.")
416
+
417
+ const jsonPathPredicateKeyOfExpression = (value: Expression.Any): string | undefined => {
418
+ const ast = astOf(value)
419
+ switch (ast.kind) {
420
+ case "jsonGetText":
421
+ case "jsonPathText":
422
+ case "jsonAccessText":
423
+ case "jsonTraverseText": {
424
+ const jsonAst = ast as ExpressionAst.JsonAccessNode
425
+ const segments = jsonAst.segments
426
+ if (segments.length === 0 || segments.length > 8) {
427
+ return undefined
428
+ }
429
+ const path: Array<string> = []
430
+ for (const segment of segments) {
431
+ if (typeof segment !== "object" || segment === null || segment.kind !== "key") {
432
+ return undefined
433
+ }
434
+ path.push(segment.key)
435
+ }
436
+ if (path.length === 0) {
437
+ return undefined
438
+ }
439
+ const baseKey = columnKeyOfExpression(jsonAst.base)
440
+ return baseKey === undefined
441
+ ? undefined
442
+ : `${baseKey}#json:${path.map(escapeJsonPathPredicateKeySegment).join(".")}`
443
+ }
444
+ default:
445
+ return undefined
446
+ }
447
+ }
448
+
449
+ const predicateKeyOfExpression = (value: Expression.Any): string | undefined =>
450
+ columnKeyOfExpression(value) ?? castPredicateKeyOfExpression(value) ?? jsonPathPredicateKeyOfExpression(value)
451
+
452
+ const castPredicateKeyOfExpression = (value: Expression.Any): string | undefined => {
453
+ const ast = astOf(value)
454
+ if (ast.kind !== "cast") {
455
+ return undefined
456
+ }
457
+ const source = ast.value as Expression.Any
458
+ return sameDbType(source[Expression.TypeId].dbType, ast.target)
459
+ ? predicateKeyOfExpression(source)
460
+ : undefined
338
461
  }
339
462
 
340
463
  const valueKeyOfLiteral = (value: unknown): string => {
@@ -357,7 +480,7 @@ const valueKeyOfLiteral = (value: unknown): string => {
357
480
  }
358
481
 
359
482
  const nonNullFactsOfExpression = (value: Expression.Any): PredicateFormula | undefined => {
360
- const key = columnKeyOfExpression(value)
483
+ const key = predicateKeyOfExpression(value)
361
484
  return key === undefined ? undefined : atomFormula<NonNullAtom<string>>({ kind: "is-not-null", key })
362
485
  }
363
486
 
@@ -375,8 +498,8 @@ const combineFacts = (
375
498
  }
376
499
 
377
500
  const formulaOfEq = (left: Expression.Any, right: Expression.Any): PredicateFormula => {
378
- const leftKey = columnKeyOfExpression(left)
379
- const rightKey = columnKeyOfExpression(right)
501
+ const leftKey = predicateKeyOfExpression(left)
502
+ const rightKey = predicateKeyOfExpression(right)
380
503
  const leftAst = astOf(left)
381
504
  const rightAst = astOf(right)
382
505
  const leftLiteral = leftAst.kind === "literal" ? leftAst.value : undefined
@@ -428,8 +551,8 @@ const formulaOfEq = (left: Expression.Any, right: Expression.Any): PredicateForm
428
551
  }
429
552
 
430
553
  const formulaOfNeq = (left: Expression.Any, right: Expression.Any): PredicateFormula => {
431
- const leftKey = columnKeyOfExpression(left)
432
- const rightKey = columnKeyOfExpression(right)
554
+ const leftKey = predicateKeyOfExpression(left)
555
+ const rightKey = predicateKeyOfExpression(right)
433
556
  const leftAst = astOf(left)
434
557
  const rightAst = astOf(right)
435
558
  const leftLiteral = leftAst.kind === "literal" ? leftAst.value : undefined
@@ -477,8 +600,8 @@ const formulaOfNeq = (left: Expression.Any, right: Expression.Any): PredicateFor
477
600
  }
478
601
 
479
602
  const formulaOfIsNotDistinctFrom = (left: Expression.Any, right: Expression.Any): PredicateFormula => {
480
- const leftKey = columnKeyOfExpression(left)
481
- const rightKey = columnKeyOfExpression(right)
603
+ const leftKey = predicateKeyOfExpression(left)
604
+ const rightKey = predicateKeyOfExpression(right)
482
605
  const leftAst = astOf(left)
483
606
  const rightAst = astOf(right)
484
607
  const leftLiteral = leftAst.kind === "literal" ? leftAst.value : undefined
@@ -587,13 +710,13 @@ export const formulaOfExpression = (value: Expression.Any): PredicateFormula =>
587
710
  }
588
711
  return unknownTag("literal:non-boolean")
589
712
  case "isNull": {
590
- const key = columnKeyOfExpression(ast.value)
713
+ const key = predicateKeyOfExpression(ast.value)
591
714
  return key === undefined
592
715
  ? unknownTag("isNull:unsupported")
593
716
  : atomFormula<NullAtom<string>>({ kind: "is-null", key })
594
717
  }
595
718
  case "isNotNull": {
596
- const key = columnKeyOfExpression(ast.value)
719
+ const key = predicateKeyOfExpression(ast.value)
597
720
  return key === undefined
598
721
  ? unknownTag("isNotNull:unsupported")
599
722
  : atomFormula<NonNullAtom<string>>({ kind: "is-not-null", key })
@@ -614,8 +737,16 @@ export const formulaOfExpression = (value: Expression.Any): PredicateFormula =>
614
737
  return anyFormula(ast.values.map((value: Expression.Any) => formulaOfExpression(value)))
615
738
  case "in": {
616
739
  const [left, ...rest] = ast.values
617
- return left === undefined
618
- ? falseFormula()
740
+ if (left === undefined) {
741
+ return falseFormula()
742
+ }
743
+ const key = predicateKeyOfExpression(left)
744
+ const literalValues = rest.map((entry: Expression.Any) => {
745
+ const entryAst = astOf(entry)
746
+ return entryAst.kind === "literal" && entryAst.value !== null ? valueKeyOfLiteral(entryAst.value) : undefined
747
+ })
748
+ return key !== undefined && literalValues.every((entry): entry is string => entry !== undefined)
749
+ ? atomFormula<LiteralSetAtom<string, string>>({ kind: "literal-set", key, values: literalValues })
619
750
  : anyFormula(rest.map((value: Expression.Any) => formulaOfEq(left, value)))
620
751
  }
621
752
  case "notIn": {