metal-orm 1.1.10 → 1.1.16

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 (192) hide show
  1. package/README.md +758 -769
  2. package/dist/index.cjs +106 -43
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +5 -5
  5. package/dist/index.d.ts +5 -5
  6. package/dist/index.js +106 -43
  7. package/dist/index.js.map +1 -1
  8. package/package.json +100 -96
  9. package/scripts/generate-entities/render.mjs +21 -12
  10. package/scripts/generate-entities/schema.mjs +209 -195
  11. package/scripts/generate-entities/tree-detection.mjs +159 -153
  12. package/scripts/inflection/pt-br.mjs +468 -468
  13. package/scripts/run-eslint.mjs +33 -34
  14. package/scripts/show-sql-seed.sql +102 -0
  15. package/scripts/show-sql.mjs +321 -325
  16. package/src/bulk/bulk-delete-executor.ts +3 -5
  17. package/src/bulk/bulk-insert-executor.ts +7 -7
  18. package/src/bulk/bulk-update-executor.ts +2 -2
  19. package/src/bulk/bulk-upsert-executor.ts +5 -7
  20. package/src/codegen/naming-strategy.ts +54 -54
  21. package/src/codegen/typescript.ts +482 -482
  22. package/src/core/ast/adapters.ts +1 -1
  23. package/src/core/ast/builders.ts +123 -123
  24. package/src/core/ast/expression-builders.ts +745 -745
  25. package/src/core/ast/expression-nodes.ts +304 -304
  26. package/src/core/ast/expression-visitor.ts +212 -212
  27. package/src/core/ast/expression.ts +20 -20
  28. package/src/core/ast/helpers.ts +14 -14
  29. package/src/core/ast/join.ts +18 -18
  30. package/src/core/ast/query.ts +200 -200
  31. package/src/core/ddl/dialects/base-schema-dialect.ts +1 -1
  32. package/src/core/ddl/dialects/mssql-schema-dialect.ts +1 -1
  33. package/src/core/ddl/dialects/mysql-schema-dialect.ts +1 -1
  34. package/src/core/ddl/dialects/postgres-schema-dialect.ts +1 -1
  35. package/src/core/ddl/dialects/render-reference.test.ts +70 -70
  36. package/src/core/ddl/dialects/sqlite-schema-dialect.ts +1 -1
  37. package/src/core/ddl/introspect/catalogs/index.ts +5 -5
  38. package/src/core/ddl/introspect/catalogs/postgres.ts +146 -146
  39. package/src/core/ddl/introspect/context.ts +15 -15
  40. package/src/core/ddl/introspect/functions/postgres.ts +35 -35
  41. package/src/core/ddl/introspect/mysql.ts +113 -36
  42. package/src/core/ddl/introspect/registry.ts +40 -40
  43. package/src/core/ddl/introspect/run-select.ts +35 -35
  44. package/src/core/ddl/introspect/utils.ts +56 -56
  45. package/src/core/ddl/naming-strategy.ts +16 -16
  46. package/src/core/ddl/schema-dialect.ts +56 -56
  47. package/src/core/ddl/schema-diff.ts +234 -234
  48. package/src/core/ddl/schema-generator.ts +1 -1
  49. package/src/core/ddl/schema-introspect.ts +25 -25
  50. package/src/core/ddl/sql-writing.ts +171 -171
  51. package/src/core/dialect/abstract.ts +541 -541
  52. package/src/core/dialect/base/cte-compiler.ts +33 -33
  53. package/src/core/dialect/base/function-table-formatter.ts +103 -103
  54. package/src/core/dialect/base/groupby-compiler.ts +21 -21
  55. package/src/core/dialect/base/join-compiler.ts +25 -25
  56. package/src/core/dialect/base/orderby-compiler.ts +35 -35
  57. package/src/core/dialect/base/pagination-strategy.ts +32 -32
  58. package/src/core/dialect/base/returning-strategy.ts +57 -57
  59. package/src/core/dialect/base/sql-dialect.ts +294 -294
  60. package/src/core/dialect/dialect-factory.ts +91 -91
  61. package/src/core/dialect/mssql/functions.ts +120 -120
  62. package/src/core/dialect/mssql/index.ts +317 -317
  63. package/src/core/dialect/mysql/functions.ts +103 -103
  64. package/src/core/dialect/mysql/index.ts +105 -105
  65. package/src/core/dialect/postgres/functions.ts +108 -108
  66. package/src/core/dialect/postgres/index.ts +91 -91
  67. package/src/core/dialect/sqlite/functions.ts +129 -129
  68. package/src/core/dialect/sqlite/index.ts +67 -67
  69. package/src/core/driver/database-driver.ts +19 -19
  70. package/src/core/driver/mssql-driver.ts +23 -23
  71. package/src/core/driver/mysql-driver.ts +23 -23
  72. package/src/core/driver/postgres-driver.ts +23 -23
  73. package/src/core/driver/sqlite-driver.ts +23 -23
  74. package/src/core/execution/db-executor.ts +92 -92
  75. package/src/core/execution/executors/better-sqlite3-executor.ts +94 -94
  76. package/src/core/execution/executors/mssql-executor.ts +184 -184
  77. package/src/core/execution/executors/mysql-executor.ts +88 -88
  78. package/src/core/execution/executors/postgres-executor.ts +26 -26
  79. package/src/core/execution/executors/sqlite-executor.ts +42 -42
  80. package/src/core/execution/pooling/pool-types.ts +30 -30
  81. package/src/core/execution/pooling/pool.ts +275 -275
  82. package/src/core/functions/array.ts +35 -35
  83. package/src/core/functions/control-flow.ts +79 -79
  84. package/src/core/functions/datetime.ts +237 -237
  85. package/src/core/functions/json.ts +70 -70
  86. package/src/core/functions/numeric.ts +297 -297
  87. package/src/core/functions/text.ts +334 -334
  88. package/src/core/functions/types.ts +33 -33
  89. package/src/core/hydration/types.ts +51 -51
  90. package/src/core/sql/sql-operator-config.ts +39 -39
  91. package/src/core/sql/sql.ts +152 -152
  92. package/src/decorators/column-decorator.ts +87 -87
  93. package/src/decorators/decorator-metadata.ts +143 -143
  94. package/src/decorators/entity.ts +93 -93
  95. package/src/decorators/index.ts +39 -39
  96. package/src/decorators/relations.ts +231 -231
  97. package/src/decorators/transformers/built-in/string-transformers.ts +231 -231
  98. package/src/decorators/transformers/transformer-decorators.ts +134 -134
  99. package/src/decorators/transformers/transformer-executor.ts +171 -171
  100. package/src/decorators/transformers/transformer-metadata.ts +73 -73
  101. package/src/decorators/validators/built-in/br-cep-validator.ts +55 -55
  102. package/src/decorators/validators/built-in/br-cnpj-validator.ts +105 -105
  103. package/src/decorators/validators/built-in/br-cpf-validator.ts +103 -103
  104. package/src/decorators/validators/country-validator-registry.ts +64 -64
  105. package/src/decorators/validators/country-validators-decorators.ts +203 -203
  106. package/src/decorators/validators/country-validators.ts +105 -105
  107. package/src/decorators/validators/validator-metadata.ts +59 -59
  108. package/src/dto/dto-types.ts +226 -226
  109. package/src/dto/filter-types.ts +141 -141
  110. package/src/dto/index.ts +95 -95
  111. package/src/dto/openapi/generators/base.ts +29 -29
  112. package/src/dto/openapi/generators/column.ts +37 -37
  113. package/src/dto/openapi/generators/dto.ts +91 -91
  114. package/src/dto/openapi/generators/filter.ts +75 -75
  115. package/src/dto/openapi/generators/nested-dto.ts +618 -618
  116. package/src/dto/openapi/generators/pagination.ts +111 -111
  117. package/src/dto/openapi/generators/relation-filter.ts +228 -228
  118. package/src/dto/openapi/index.ts +12 -12
  119. package/src/dto/openapi/types.ts +101 -101
  120. package/src/dto/openapi/utilities.ts +95 -95
  121. package/src/dto/pagination-utils.ts +150 -150
  122. package/src/dto/transform.ts +197 -197
  123. package/src/index.ts +86 -86
  124. package/src/orm/als.ts +58 -58
  125. package/src/orm/domain-event-bus.ts +119 -119
  126. package/src/orm/entity-context.ts +86 -86
  127. package/src/orm/entity-materializer.ts +159 -159
  128. package/src/orm/entity-meta.ts +97 -97
  129. package/src/orm/entity-registry.ts +39 -39
  130. package/src/orm/entity-relation-cache.ts +39 -39
  131. package/src/orm/execute.ts +194 -194
  132. package/src/orm/execution-context.ts +18 -18
  133. package/src/orm/hydration-context.ts +26 -26
  134. package/src/orm/hydration.ts +112 -112
  135. package/src/orm/identity-map.ts +61 -61
  136. package/src/orm/interceptor-pipeline.ts +16 -16
  137. package/src/orm/lazy-batch/belongs-to-many.ts +119 -119
  138. package/src/orm/lazy-batch/belongs-to.ts +108 -108
  139. package/src/orm/lazy-batch/has-many.ts +69 -69
  140. package/src/orm/lazy-batch/has-one.ts +68 -68
  141. package/src/orm/lazy-batch/shared.ts +125 -125
  142. package/src/orm/orm-session.ts +620 -620
  143. package/src/orm/orm.ts +34 -34
  144. package/src/orm/pooled-executor-factory.ts +121 -121
  145. package/src/orm/query-logger.ts +40 -40
  146. package/src/orm/relation-change-processor.ts +420 -420
  147. package/src/orm/relations/belongs-to.ts +143 -143
  148. package/src/orm/relations/has-many.ts +204 -204
  149. package/src/orm/relations/has-one.ts +174 -174
  150. package/src/orm/relations/many-to-many.ts +288 -288
  151. package/src/orm/relations/morph-many.ts +156 -156
  152. package/src/orm/relations/morph-one.ts +151 -151
  153. package/src/orm/relations/morph-to.ts +162 -162
  154. package/src/orm/runtime-types.ts +92 -92
  155. package/src/orm/save-graph.ts +561 -561
  156. package/src/orm/transaction-runner.ts +24 -24
  157. package/src/orm/unit-of-work.ts +426 -426
  158. package/src/query/index.ts +69 -69
  159. package/src/query/target.ts +46 -46
  160. package/src/query-builder/column-selector.ts +81 -81
  161. package/src/query-builder/delete.ts +141 -141
  162. package/src/query-builder/insert-query-state.ts +149 -149
  163. package/src/query-builder/insert.ts +117 -117
  164. package/src/query-builder/query-ast-service.ts +287 -287
  165. package/src/query-builder/query-resolution.ts +78 -78
  166. package/src/query-builder/raw-column-parser.ts +38 -38
  167. package/src/query-builder/relation-filter-utils.ts +153 -153
  168. package/src/query-builder/relation-join-planner.ts +10 -10
  169. package/src/query-builder/relation-manager.ts +87 -87
  170. package/src/query-builder/relation-projection-helper.ts +102 -102
  171. package/src/query-builder/relation-types.ts +9 -9
  172. package/src/query-builder/relation-utils.ts +15 -15
  173. package/src/query-builder/select/cte-facet.ts +40 -40
  174. package/src/query-builder/select/from-facet.ts +80 -80
  175. package/src/query-builder/select/join-facet.ts +62 -62
  176. package/src/query-builder/select/predicate-facet.ts +104 -104
  177. package/src/query-builder/select/projection-facet.ts +70 -70
  178. package/src/query-builder/select/relation-facet.ts +81 -81
  179. package/src/query-builder/select/setop-facet.ts +36 -36
  180. package/src/query-builder/select-helpers.ts +1 -1
  181. package/src/query-builder/select-query-builder-deps.ts +130 -130
  182. package/src/query-builder/select-query-state.ts +207 -207
  183. package/src/query-builder/update.ts +151 -151
  184. package/src/schema/column-types.ts +322 -322
  185. package/src/schema/table-guards.ts +37 -37
  186. package/src/schema/table.ts +281 -281
  187. package/src/tree/index.ts +13 -13
  188. package/src/tree/nested-set-strategy.ts +545 -545
  189. package/src/tree/tree-decorator.ts +397 -397
  190. package/src/tree/tree-manager.ts +754 -754
  191. package/src/tree/tree-query.ts +427 -427
  192. package/src/tree/tree-types.ts +299 -299
@@ -1,78 +1,78 @@
1
- import { SelectQueryNode, UpdateQueryNode, DeleteQueryNode, TableSourceNode } from '../core/ast/query.js';
2
- import { TableDef } from '../schema/table.js';
3
- import type { SelectQueryBuilder } from './select.js';
4
- import type { UpdateQueryBuilder } from './update.js';
5
- import type { DeleteQueryBuilder } from './delete.js';
6
-
7
- /**
8
- * Resolves a SelectQueryBuilder or SelectQueryNode to a SelectQueryNode AST
9
- * @param query - Query builder or AST node
10
- * @returns SelectQueryNode AST
11
- */
12
- export function resolveSelectQuery<TSub extends TableDef>(
13
- query: SelectQueryBuilder<unknown, TSub> | SelectQueryNode
14
- ): SelectQueryNode {
15
- const candidate = query as { getAST?: () => SelectQueryNode };
16
- return typeof candidate.getAST === 'function' && candidate.getAST
17
- ? candidate.getAST()
18
- : (query as SelectQueryNode);
19
- }
20
-
21
- /**
22
- * Resolves a UpdateQueryBuilder or UpdateQueryNode to a UpdateQueryNode AST
23
- * @param query - Query builder or AST node
24
- * @returns UpdateQueryNode AST
25
- */
26
- export function resolveUpdateQuery<T>(
27
- query: UpdateQueryBuilder<T> | UpdateQueryNode
28
- ): UpdateQueryNode {
29
- const candidate = query as { getAST?: () => UpdateQueryNode };
30
- return typeof candidate.getAST === 'function' && candidate.getAST
31
- ? candidate.getAST()
32
- : (query as UpdateQueryNode);
33
- }
34
-
35
- /**
36
- * Resolves a DeleteQueryBuilder or DeleteQueryNode to a DeleteQueryNode AST
37
- * @param query - Query builder or AST node
38
- * @returns DeleteQueryNode AST
39
- */
40
- export function resolveDeleteQuery<T>(
41
- query: DeleteQueryBuilder<T> | DeleteQueryNode
42
- ): DeleteQueryNode {
43
- const candidate = query as { getAST?: () => DeleteQueryNode };
44
- return typeof candidate.getAST === 'function' && candidate.getAST
45
- ? candidate.getAST()
46
- : (query as DeleteQueryNode);
47
- }
48
-
49
- /**
50
- * Resolves a TableDef or TableSourceNode to a TableSourceNode
51
- * @param source - Table definition or source node
52
- * @returns TableSourceNode
53
- */
54
- export function resolveTableSource(source: TableDef | TableSourceNode): TableSourceNode {
55
- if (isTableSourceNode(source)) {
56
- return source;
57
- }
58
- return { type: 'Table', name: source.name, schema: source.schema };
59
- }
60
-
61
- /**
62
- * Resolves a join target (TableDef, TableSourceNode, or string relation name)
63
- * @param table - Join target
64
- * @returns TableSourceNode or string
65
- */
66
- export function resolveJoinTarget(table: TableDef | TableSourceNode | string): TableSourceNode | string {
67
- if (typeof table === 'string') return table;
68
- return resolveTableSource(table);
69
- }
70
-
71
- /**
72
- * Type guard to check if a value is a TableSourceNode
73
- * @param source - Value to check
74
- * @returns True if value is a TableSourceNode
75
- */
76
- function isTableSourceNode(source: TableDef | TableSourceNode): source is TableSourceNode {
77
- return typeof (source as TableSourceNode).type === 'string';
78
- }
1
+ import { SelectQueryNode, UpdateQueryNode, DeleteQueryNode, TableSourceNode } from '../core/ast/query.js';
2
+ import { TableDef } from '../schema/table.js';
3
+ import type { SelectQueryBuilder } from './select.js';
4
+ import type { UpdateQueryBuilder } from './update.js';
5
+ import type { DeleteQueryBuilder } from './delete.js';
6
+
7
+ /**
8
+ * Resolves a SelectQueryBuilder or SelectQueryNode to a SelectQueryNode AST
9
+ * @param query - Query builder or AST node
10
+ * @returns SelectQueryNode AST
11
+ */
12
+ export function resolveSelectQuery<TSub extends TableDef>(
13
+ query: SelectQueryBuilder<unknown, TSub> | SelectQueryNode
14
+ ): SelectQueryNode {
15
+ const candidate = query as { getAST?: () => SelectQueryNode };
16
+ return typeof candidate.getAST === 'function' && candidate.getAST
17
+ ? candidate.getAST()
18
+ : (query as SelectQueryNode);
19
+ }
20
+
21
+ /**
22
+ * Resolves a UpdateQueryBuilder or UpdateQueryNode to a UpdateQueryNode AST
23
+ * @param query - Query builder or AST node
24
+ * @returns UpdateQueryNode AST
25
+ */
26
+ export function resolveUpdateQuery<T>(
27
+ query: UpdateQueryBuilder<T> | UpdateQueryNode
28
+ ): UpdateQueryNode {
29
+ const candidate = query as { getAST?: () => UpdateQueryNode };
30
+ return typeof candidate.getAST === 'function' && candidate.getAST
31
+ ? candidate.getAST()
32
+ : (query as UpdateQueryNode);
33
+ }
34
+
35
+ /**
36
+ * Resolves a DeleteQueryBuilder or DeleteQueryNode to a DeleteQueryNode AST
37
+ * @param query - Query builder or AST node
38
+ * @returns DeleteQueryNode AST
39
+ */
40
+ export function resolveDeleteQuery<T>(
41
+ query: DeleteQueryBuilder<T> | DeleteQueryNode
42
+ ): DeleteQueryNode {
43
+ const candidate = query as { getAST?: () => DeleteQueryNode };
44
+ return typeof candidate.getAST === 'function' && candidate.getAST
45
+ ? candidate.getAST()
46
+ : (query as DeleteQueryNode);
47
+ }
48
+
49
+ /**
50
+ * Resolves a TableDef or TableSourceNode to a TableSourceNode
51
+ * @param source - Table definition or source node
52
+ * @returns TableSourceNode
53
+ */
54
+ export function resolveTableSource(source: TableDef | TableSourceNode): TableSourceNode {
55
+ if (isTableSourceNode(source)) {
56
+ return source;
57
+ }
58
+ return { type: 'Table', name: source.name, schema: source.schema };
59
+ }
60
+
61
+ /**
62
+ * Resolves a join target (TableDef, TableSourceNode, or string relation name)
63
+ * @param table - Join target
64
+ * @returns TableSourceNode or string
65
+ */
66
+ export function resolveJoinTarget(table: TableDef | TableSourceNode | string): TableSourceNode | string {
67
+ if (typeof table === 'string') return table;
68
+ return resolveTableSource(table);
69
+ }
70
+
71
+ /**
72
+ * Type guard to check if a value is a TableSourceNode
73
+ * @param source - Value to check
74
+ * @returns True if value is a TableSourceNode
75
+ */
76
+ function isTableSourceNode(source: TableDef | TableSourceNode): source is TableSourceNode {
77
+ return typeof (source as TableSourceNode).type === 'string';
78
+ }
@@ -1,38 +1,38 @@
1
- import { ColumnNode } from '../core/ast/expression.js';
2
- import { CommonTableExpressionNode } from '../core/ast/query.js';
3
-
4
- /**
5
- * Best-effort helper that tries to convert a raw column expression into a `ColumnNode`.
6
- * This parser is intentionally limited; use it only for simple references or function calls.
7
- *
8
- * @param col - Raw column expression string (e.g., "column", "table.column", "COUNT(column)")
9
- * @param tableName - Default table name to use when no table is specified
10
- * @param ctes - Optional array of CTEs for context when parsing column references
11
- * @returns A ColumnNode representing the parsed column expression
12
- */
13
- export const parseRawColumn = (
14
- col: string,
15
- tableName: string,
16
- ctes?: CommonTableExpressionNode[]
17
- ): ColumnNode => {
18
- if (col.includes('(')) {
19
- const [_fn, rest] = col.split('(');
20
- void _fn;
21
- const colName = rest.replace(')', '');
22
- const [table, name] = colName.includes('.') ? colName.split('.') : [tableName, colName];
23
- return { type: 'Column', table, name, alias: col };
24
- }
25
-
26
- if (col.includes('.')) {
27
- const [potentialCteName, columnName] = col.split('.');
28
- const hasCte = ctes?.some(cte => cte.name === potentialCteName);
29
-
30
- if (hasCte) {
31
- return { type: 'Column', table: tableName, name: col };
32
- }
33
-
34
- return { type: 'Column', table: potentialCteName, name: columnName };
35
- }
36
-
37
- return { type: 'Column', table: tableName, name: col };
38
- };
1
+ import { ColumnNode } from '../core/ast/expression.js';
2
+ import { CommonTableExpressionNode } from '../core/ast/query.js';
3
+
4
+ /**
5
+ * Best-effort helper that tries to convert a raw column expression into a `ColumnNode`.
6
+ * This parser is intentionally limited; use it only for simple references or function calls.
7
+ *
8
+ * @param col - Raw column expression string (e.g., "column", "table.column", "COUNT(column)")
9
+ * @param tableName - Default table name to use when no table is specified
10
+ * @param ctes - Optional array of CTEs for context when parsing column references
11
+ * @returns A ColumnNode representing the parsed column expression
12
+ */
13
+ export const parseRawColumn = (
14
+ col: string,
15
+ tableName: string,
16
+ ctes?: CommonTableExpressionNode[]
17
+ ): ColumnNode => {
18
+ if (col.includes('(')) {
19
+ const [_fn, rest] = col.split('(');
20
+ void _fn;
21
+ const colName = rest.replace(')', '');
22
+ const [table, name] = colName.includes('.') ? colName.split('.') : [tableName, colName];
23
+ return { type: 'Column', table, name, alias: col };
24
+ }
25
+
26
+ if (col.includes('.')) {
27
+ const [potentialCteName, columnName] = col.split('.');
28
+ const hasCte = ctes?.some(cte => cte.name === potentialCteName);
29
+
30
+ if (hasCte) {
31
+ return { type: 'Column', table: tableName, name: col };
32
+ }
33
+
34
+ return { type: 'Column', table: potentialCteName, name: columnName };
35
+ }
36
+
37
+ return { type: 'Column', table: tableName, name: col };
38
+ };
@@ -1,70 +1,70 @@
1
- import { ExpressionNode, OperandNode, isOperandNode } from '../core/ast/expression.js';
2
- import { OrderingTerm } from '../core/ast/query.js';
3
-
4
- type FilterTableCollector = {
5
- tables: Set<string>;
6
- hasSubquery: boolean;
7
- };
8
-
9
- export type SplitFilterExpressionsResult = {
10
- selfFilters: ExpressionNode[];
11
- crossFilters: ExpressionNode[];
12
- };
13
-
14
- export const splitFilterExpressions = (
15
- filter: ExpressionNode | undefined,
16
- allowedTables: Set<string>
17
- ): SplitFilterExpressionsResult => {
18
- const terms = flattenAnd(filter);
19
- const selfFilters: ExpressionNode[] = [];
20
- const crossFilters: ExpressionNode[] = [];
21
-
22
- for (const term of terms) {
23
- if (isExpressionSelfContained(term, allowedTables)) {
24
- selfFilters.push(term);
25
- } else {
26
- crossFilters.push(term);
27
- }
28
- }
29
-
30
- return { selfFilters, crossFilters };
31
- };
32
-
33
- const flattenAnd = (node?: ExpressionNode): ExpressionNode[] => {
34
- if (!node) return [];
35
- if (node.type === 'LogicalExpression' && node.operator === 'AND') {
36
- return node.operands.flatMap(operand => flattenAnd(operand));
37
- }
38
- return [node];
39
- };
40
-
41
- const isExpressionSelfContained = (expr: ExpressionNode, allowedTables: Set<string>): boolean => {
42
- const collector = collectReferencedTables(expr);
43
- if (collector.hasSubquery) return false;
44
- if (collector.tables.size === 0) return true;
45
- for (const table of collector.tables) {
46
- if (!allowedTables.has(table)) {
47
- return false;
48
- }
49
- }
50
- return true;
51
- };
52
-
53
- const collectReferencedTables = (expr: ExpressionNode): FilterTableCollector => {
54
- const collector: FilterTableCollector = {
55
- tables: new Set(),
56
- hasSubquery: false
57
- };
58
- collectFromExpression(expr, collector);
59
- return collector;
60
- };
61
-
62
- const collectFromExpression = (expr: ExpressionNode, collector: FilterTableCollector): void => {
63
- switch (expr.type) {
64
- case 'BinaryExpression':
65
- collectFromOperand(expr.left, collector);
66
- collectFromOperand(expr.right, collector);
67
- break;
1
+ import { ExpressionNode, OperandNode, isOperandNode } from '../core/ast/expression.js';
2
+ import { OrderingTerm } from '../core/ast/query.js';
3
+
4
+ type FilterTableCollector = {
5
+ tables: Set<string>;
6
+ hasSubquery: boolean;
7
+ };
8
+
9
+ export type SplitFilterExpressionsResult = {
10
+ selfFilters: ExpressionNode[];
11
+ crossFilters: ExpressionNode[];
12
+ };
13
+
14
+ export const splitFilterExpressions = (
15
+ filter: ExpressionNode | undefined,
16
+ allowedTables: Set<string>
17
+ ): SplitFilterExpressionsResult => {
18
+ const terms = flattenAnd(filter);
19
+ const selfFilters: ExpressionNode[] = [];
20
+ const crossFilters: ExpressionNode[] = [];
21
+
22
+ for (const term of terms) {
23
+ if (isExpressionSelfContained(term, allowedTables)) {
24
+ selfFilters.push(term);
25
+ } else {
26
+ crossFilters.push(term);
27
+ }
28
+ }
29
+
30
+ return { selfFilters, crossFilters };
31
+ };
32
+
33
+ const flattenAnd = (node?: ExpressionNode): ExpressionNode[] => {
34
+ if (!node) return [];
35
+ if (node.type === 'LogicalExpression' && node.operator === 'AND') {
36
+ return node.operands.flatMap(operand => flattenAnd(operand));
37
+ }
38
+ return [node];
39
+ };
40
+
41
+ const isExpressionSelfContained = (expr: ExpressionNode, allowedTables: Set<string>): boolean => {
42
+ const collector = collectReferencedTables(expr);
43
+ if (collector.hasSubquery) return false;
44
+ if (collector.tables.size === 0) return true;
45
+ for (const table of collector.tables) {
46
+ if (!allowedTables.has(table)) {
47
+ return false;
48
+ }
49
+ }
50
+ return true;
51
+ };
52
+
53
+ const collectReferencedTables = (expr: ExpressionNode): FilterTableCollector => {
54
+ const collector: FilterTableCollector = {
55
+ tables: new Set(),
56
+ hasSubquery: false
57
+ };
58
+ collectFromExpression(expr, collector);
59
+ return collector;
60
+ };
61
+
62
+ const collectFromExpression = (expr: ExpressionNode, collector: FilterTableCollector): void => {
63
+ switch (expr.type) {
64
+ case 'BinaryExpression':
65
+ collectFromOperand(expr.left, collector);
66
+ collectFromOperand(expr.right, collector);
67
+ break;
68
68
  case 'LogicalExpression':
69
69
  expr.operands.forEach(operand => collectFromExpression(operand, collector));
70
70
  break;
@@ -74,89 +74,89 @@ const collectFromExpression = (expr: ExpressionNode, collector: FilterTableColle
74
74
  case 'NullExpression':
75
75
  collectFromOperand(expr.left, collector);
76
76
  break;
77
- case 'InExpression':
78
- collectFromOperand(expr.left, collector);
79
- if (Array.isArray(expr.right)) {
80
- expr.right.forEach(value => collectFromOperand(value, collector));
81
- } else {
82
- collector.hasSubquery = true;
83
- }
84
- break;
85
- case 'ExistsExpression':
86
- collector.hasSubquery = true;
87
- break;
88
- case 'BetweenExpression':
89
- collectFromOperand(expr.left, collector);
90
- collectFromOperand(expr.lower, collector);
91
- collectFromOperand(expr.upper, collector);
92
- break;
93
- case 'ArithmeticExpression':
94
- case 'BitwiseExpression':
95
- collectFromOperand(expr.left, collector);
96
- collectFromOperand(expr.right, collector);
97
- break;
98
- default:
99
- break;
100
- }
101
- };
102
-
103
- const collectFromOperand = (node: OperandNode, collector: FilterTableCollector): void => {
104
- switch (node.type) {
105
- case 'Column':
106
- collector.tables.add(node.table);
107
- break;
108
- case 'Function':
109
- node.args.forEach(arg => collectFromOperand(arg, collector));
110
- if (node.separator) {
111
- collectFromOperand(node.separator, collector);
112
- }
113
- if (node.orderBy) {
114
- node.orderBy.forEach(order => collectFromOrderingTerm(order.term, collector));
115
- }
116
- break;
117
- case 'JsonPath':
118
- collectFromOperand(node.column, collector);
119
- break;
120
- case 'ScalarSubquery':
121
- collector.hasSubquery = true;
122
- break;
123
- case 'CaseExpression':
124
- node.conditions.forEach(({ when, then }) => {
125
- collectFromExpression(when, collector);
126
- collectFromOperand(then, collector);
127
- });
128
- if (node.else) {
129
- collectFromOperand(node.else, collector);
130
- }
131
- break;
132
- case 'Cast':
133
- collectFromOperand(node.expression, collector);
134
- break;
135
- case 'WindowFunction':
136
- node.args.forEach(arg => collectFromOperand(arg, collector));
137
- node.partitionBy?.forEach(part => collectFromOperand(part, collector));
138
- node.orderBy?.forEach(order => collectFromOrderingTerm(order.term, collector));
139
- break;
140
- case 'Collate':
141
- collectFromOperand(node.expression, collector);
142
- break;
143
- case 'ArithmeticExpression':
144
- case 'BitwiseExpression':
145
- collectFromOperand(node.left, collector);
146
- collectFromOperand(node.right, collector);
147
- break;
148
- case 'Literal':
149
- case 'AliasRef':
150
- break;
151
- default:
152
- break;
153
- }
154
- };
155
-
156
- const collectFromOrderingTerm = (term: OrderingTerm, collector: FilterTableCollector): void => {
157
- if (isOperandNode(term)) {
158
- collectFromOperand(term, collector);
159
- return;
160
- }
161
- collectFromExpression(term, collector);
162
- };
77
+ case 'InExpression':
78
+ collectFromOperand(expr.left, collector);
79
+ if (Array.isArray(expr.right)) {
80
+ expr.right.forEach(value => collectFromOperand(value, collector));
81
+ } else {
82
+ collector.hasSubquery = true;
83
+ }
84
+ break;
85
+ case 'ExistsExpression':
86
+ collector.hasSubquery = true;
87
+ break;
88
+ case 'BetweenExpression':
89
+ collectFromOperand(expr.left, collector);
90
+ collectFromOperand(expr.lower, collector);
91
+ collectFromOperand(expr.upper, collector);
92
+ break;
93
+ case 'ArithmeticExpression':
94
+ case 'BitwiseExpression':
95
+ collectFromOperand(expr.left, collector);
96
+ collectFromOperand(expr.right, collector);
97
+ break;
98
+ default:
99
+ break;
100
+ }
101
+ };
102
+
103
+ const collectFromOperand = (node: OperandNode, collector: FilterTableCollector): void => {
104
+ switch (node.type) {
105
+ case 'Column':
106
+ collector.tables.add(node.table);
107
+ break;
108
+ case 'Function':
109
+ node.args.forEach(arg => collectFromOperand(arg, collector));
110
+ if (node.separator) {
111
+ collectFromOperand(node.separator, collector);
112
+ }
113
+ if (node.orderBy) {
114
+ node.orderBy.forEach(order => collectFromOrderingTerm(order.term, collector));
115
+ }
116
+ break;
117
+ case 'JsonPath':
118
+ collectFromOperand(node.column, collector);
119
+ break;
120
+ case 'ScalarSubquery':
121
+ collector.hasSubquery = true;
122
+ break;
123
+ case 'CaseExpression':
124
+ node.conditions.forEach(({ when, then }) => {
125
+ collectFromExpression(when, collector);
126
+ collectFromOperand(then, collector);
127
+ });
128
+ if (node.else) {
129
+ collectFromOperand(node.else, collector);
130
+ }
131
+ break;
132
+ case 'Cast':
133
+ collectFromOperand(node.expression, collector);
134
+ break;
135
+ case 'WindowFunction':
136
+ node.args.forEach(arg => collectFromOperand(arg, collector));
137
+ node.partitionBy?.forEach(part => collectFromOperand(part, collector));
138
+ node.orderBy?.forEach(order => collectFromOrderingTerm(order.term, collector));
139
+ break;
140
+ case 'Collate':
141
+ collectFromOperand(node.expression, collector);
142
+ break;
143
+ case 'ArithmeticExpression':
144
+ case 'BitwiseExpression':
145
+ collectFromOperand(node.left, collector);
146
+ collectFromOperand(node.right, collector);
147
+ break;
148
+ case 'Literal':
149
+ case 'AliasRef':
150
+ break;
151
+ default:
152
+ break;
153
+ }
154
+ };
155
+
156
+ const collectFromOrderingTerm = (term: OrderingTerm, collector: FilterTableCollector): void => {
157
+ if (isOperandNode(term)) {
158
+ collectFromOperand(term, collector);
159
+ return;
160
+ }
161
+ collectFromExpression(term, collector);
162
+ };
@@ -1,23 +1,23 @@
1
- import { TableDef } from '../schema/table.js';
1
+ import { TableDef } from '../schema/table.js';
2
2
  import { RelationDef } from '../schema/relation.js';
3
3
  import { SelectQueryState } from './select-query-state.js';
4
4
  import { TableSourceNode } from '../core/ast/query.js';
5
5
  import { ExpressionNode } from '../core/ast/expression.js';
6
6
  import { JoinKind } from '../core/sql/sql.js';
7
7
  import { addRelationJoin } from './relation-join-strategies.js';
8
-
8
+
9
9
  export class RelationJoinPlanner {
10
10
  constructor(
11
11
  private readonly table: TableDef
12
12
  ) {}
13
-
14
- withJoin(
15
- state: SelectQueryState,
16
- relationName: string,
17
- relation: RelationDef,
18
- joinKind: JoinKind,
19
- extraCondition?: ExpressionNode,
20
- tableSource?: TableSourceNode
13
+
14
+ withJoin(
15
+ state: SelectQueryState,
16
+ relationName: string,
17
+ relation: RelationDef,
18
+ joinKind: JoinKind,
19
+ extraCondition?: ExpressionNode,
20
+ tableSource?: TableSourceNode
21
21
  ): SelectQueryState {
22
22
  const rootAlias = state.ast.from.type === 'Table' ? state.ast.from.alias : undefined;
23
23
  return addRelationJoin({