metal-orm 1.1.11 → 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 (182) hide show
  1. package/README.md +758 -769
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.js.map +1 -1
  4. package/package.json +100 -96
  5. package/scripts/generate-entities/schema.mjs +122 -122
  6. package/scripts/generate-entities/tree-detection.mjs +92 -92
  7. package/scripts/inflection/pt-br.mjs +468 -468
  8. package/scripts/run-eslint.mjs +33 -34
  9. package/scripts/show-sql-seed.sql +102 -0
  10. package/scripts/show-sql.mjs +321 -325
  11. package/src/codegen/naming-strategy.ts +54 -54
  12. package/src/codegen/typescript.ts +482 -482
  13. package/src/core/ast/adapters.ts +1 -1
  14. package/src/core/ast/builders.ts +123 -123
  15. package/src/core/ast/expression-builders.ts +745 -745
  16. package/src/core/ast/expression-nodes.ts +304 -304
  17. package/src/core/ast/expression-visitor.ts +212 -212
  18. package/src/core/ast/expression.ts +20 -20
  19. package/src/core/ast/helpers.ts +14 -14
  20. package/src/core/ast/join.ts +18 -18
  21. package/src/core/ast/query.ts +200 -200
  22. package/src/core/ddl/dialects/base-schema-dialect.ts +1 -1
  23. package/src/core/ddl/dialects/mssql-schema-dialect.ts +1 -1
  24. package/src/core/ddl/dialects/mysql-schema-dialect.ts +1 -1
  25. package/src/core/ddl/dialects/postgres-schema-dialect.ts +1 -1
  26. package/src/core/ddl/dialects/render-reference.test.ts +70 -70
  27. package/src/core/ddl/dialects/sqlite-schema-dialect.ts +1 -1
  28. package/src/core/ddl/introspect/catalogs/index.ts +5 -5
  29. package/src/core/ddl/introspect/catalogs/postgres.ts +146 -146
  30. package/src/core/ddl/introspect/context.ts +15 -15
  31. package/src/core/ddl/introspect/functions/postgres.ts +35 -35
  32. package/src/core/ddl/introspect/registry.ts +40 -40
  33. package/src/core/ddl/introspect/run-select.ts +35 -35
  34. package/src/core/ddl/introspect/utils.ts +56 -56
  35. package/src/core/ddl/naming-strategy.ts +16 -16
  36. package/src/core/ddl/schema-dialect.ts +56 -56
  37. package/src/core/ddl/schema-diff.ts +234 -234
  38. package/src/core/ddl/schema-generator.ts +1 -1
  39. package/src/core/ddl/schema-introspect.ts +25 -25
  40. package/src/core/ddl/sql-writing.ts +171 -171
  41. package/src/core/dialect/abstract.ts +541 -541
  42. package/src/core/dialect/base/cte-compiler.ts +33 -33
  43. package/src/core/dialect/base/function-table-formatter.ts +103 -103
  44. package/src/core/dialect/base/groupby-compiler.ts +21 -21
  45. package/src/core/dialect/base/join-compiler.ts +25 -25
  46. package/src/core/dialect/base/orderby-compiler.ts +35 -35
  47. package/src/core/dialect/base/pagination-strategy.ts +32 -32
  48. package/src/core/dialect/base/returning-strategy.ts +57 -57
  49. package/src/core/dialect/base/sql-dialect.ts +294 -294
  50. package/src/core/dialect/dialect-factory.ts +91 -91
  51. package/src/core/dialect/mssql/functions.ts +120 -120
  52. package/src/core/dialect/mssql/index.ts +317 -317
  53. package/src/core/dialect/mysql/functions.ts +103 -103
  54. package/src/core/dialect/mysql/index.ts +105 -105
  55. package/src/core/dialect/postgres/functions.ts +108 -108
  56. package/src/core/dialect/postgres/index.ts +91 -91
  57. package/src/core/dialect/sqlite/functions.ts +129 -129
  58. package/src/core/dialect/sqlite/index.ts +67 -67
  59. package/src/core/driver/database-driver.ts +19 -19
  60. package/src/core/driver/mssql-driver.ts +23 -23
  61. package/src/core/driver/mysql-driver.ts +23 -23
  62. package/src/core/driver/postgres-driver.ts +23 -23
  63. package/src/core/driver/sqlite-driver.ts +23 -23
  64. package/src/core/execution/db-executor.ts +92 -92
  65. package/src/core/execution/executors/better-sqlite3-executor.ts +90 -90
  66. package/src/core/execution/executors/mssql-executor.ts +184 -184
  67. package/src/core/execution/executors/mysql-executor.ts +88 -88
  68. package/src/core/execution/executors/postgres-executor.ts +26 -26
  69. package/src/core/execution/executors/sqlite-executor.ts +42 -42
  70. package/src/core/execution/pooling/pool-types.ts +30 -30
  71. package/src/core/execution/pooling/pool.ts +275 -275
  72. package/src/core/functions/array.ts +35 -35
  73. package/src/core/functions/control-flow.ts +79 -79
  74. package/src/core/functions/datetime.ts +237 -237
  75. package/src/core/functions/json.ts +70 -70
  76. package/src/core/functions/numeric.ts +297 -297
  77. package/src/core/functions/text.ts +334 -334
  78. package/src/core/functions/types.ts +33 -33
  79. package/src/core/hydration/types.ts +51 -51
  80. package/src/core/sql/sql-operator-config.ts +39 -39
  81. package/src/core/sql/sql.ts +152 -152
  82. package/src/decorators/column-decorator.ts +87 -87
  83. package/src/decorators/decorator-metadata.ts +143 -143
  84. package/src/decorators/entity.ts +93 -93
  85. package/src/decorators/index.ts +39 -39
  86. package/src/decorators/relations.ts +231 -231
  87. package/src/decorators/transformers/built-in/string-transformers.ts +231 -231
  88. package/src/decorators/transformers/transformer-decorators.ts +134 -134
  89. package/src/decorators/transformers/transformer-executor.ts +171 -171
  90. package/src/decorators/transformers/transformer-metadata.ts +73 -73
  91. package/src/decorators/validators/built-in/br-cep-validator.ts +55 -55
  92. package/src/decorators/validators/built-in/br-cnpj-validator.ts +105 -105
  93. package/src/decorators/validators/built-in/br-cpf-validator.ts +103 -103
  94. package/src/decorators/validators/country-validator-registry.ts +64 -64
  95. package/src/decorators/validators/country-validators-decorators.ts +203 -203
  96. package/src/decorators/validators/country-validators.ts +105 -105
  97. package/src/decorators/validators/validator-metadata.ts +59 -59
  98. package/src/dto/dto-types.ts +226 -226
  99. package/src/dto/filter-types.ts +141 -141
  100. package/src/dto/index.ts +95 -95
  101. package/src/dto/openapi/generators/base.ts +29 -29
  102. package/src/dto/openapi/generators/column.ts +37 -37
  103. package/src/dto/openapi/generators/dto.ts +91 -91
  104. package/src/dto/openapi/generators/filter.ts +75 -75
  105. package/src/dto/openapi/generators/nested-dto.ts +618 -618
  106. package/src/dto/openapi/generators/pagination.ts +111 -111
  107. package/src/dto/openapi/generators/relation-filter.ts +228 -228
  108. package/src/dto/openapi/index.ts +12 -12
  109. package/src/dto/openapi/types.ts +101 -101
  110. package/src/dto/openapi/utilities.ts +95 -95
  111. package/src/dto/pagination-utils.ts +150 -150
  112. package/src/dto/transform.ts +197 -197
  113. package/src/index.ts +86 -86
  114. package/src/orm/als.ts +58 -58
  115. package/src/orm/domain-event-bus.ts +119 -119
  116. package/src/orm/entity-context.ts +86 -86
  117. package/src/orm/entity-materializer.ts +159 -159
  118. package/src/orm/entity-meta.ts +97 -97
  119. package/src/orm/entity-registry.ts +39 -39
  120. package/src/orm/entity-relation-cache.ts +39 -39
  121. package/src/orm/execute.ts +194 -194
  122. package/src/orm/execution-context.ts +18 -18
  123. package/src/orm/hydration-context.ts +26 -26
  124. package/src/orm/hydration.ts +112 -112
  125. package/src/orm/identity-map.ts +61 -61
  126. package/src/orm/interceptor-pipeline.ts +16 -16
  127. package/src/orm/lazy-batch/belongs-to-many.ts +119 -119
  128. package/src/orm/lazy-batch/belongs-to.ts +108 -108
  129. package/src/orm/lazy-batch/has-many.ts +69 -69
  130. package/src/orm/lazy-batch/has-one.ts +68 -68
  131. package/src/orm/lazy-batch/shared.ts +125 -125
  132. package/src/orm/orm-session.ts +620 -620
  133. package/src/orm/orm.ts +34 -34
  134. package/src/orm/pooled-executor-factory.ts +121 -121
  135. package/src/orm/query-logger.ts +40 -40
  136. package/src/orm/relation-change-processor.ts +420 -420
  137. package/src/orm/relations/belongs-to.ts +143 -143
  138. package/src/orm/relations/has-many.ts +204 -204
  139. package/src/orm/relations/has-one.ts +174 -174
  140. package/src/orm/relations/many-to-many.ts +288 -288
  141. package/src/orm/relations/morph-many.ts +156 -156
  142. package/src/orm/relations/morph-one.ts +151 -151
  143. package/src/orm/relations/morph-to.ts +162 -162
  144. package/src/orm/runtime-types.ts +92 -92
  145. package/src/orm/save-graph.ts +561 -561
  146. package/src/orm/transaction-runner.ts +24 -24
  147. package/src/orm/unit-of-work.ts +426 -426
  148. package/src/query/index.ts +69 -69
  149. package/src/query/target.ts +46 -46
  150. package/src/query-builder/column-selector.ts +81 -81
  151. package/src/query-builder/delete.ts +141 -141
  152. package/src/query-builder/insert-query-state.ts +149 -149
  153. package/src/query-builder/insert.ts +117 -117
  154. package/src/query-builder/query-ast-service.ts +287 -287
  155. package/src/query-builder/query-resolution.ts +78 -78
  156. package/src/query-builder/raw-column-parser.ts +38 -38
  157. package/src/query-builder/relation-filter-utils.ts +153 -153
  158. package/src/query-builder/relation-join-planner.ts +10 -10
  159. package/src/query-builder/relation-manager.ts +87 -87
  160. package/src/query-builder/relation-projection-helper.ts +102 -102
  161. package/src/query-builder/relation-types.ts +9 -9
  162. package/src/query-builder/relation-utils.ts +15 -15
  163. package/src/query-builder/select/cte-facet.ts +40 -40
  164. package/src/query-builder/select/from-facet.ts +80 -80
  165. package/src/query-builder/select/join-facet.ts +62 -62
  166. package/src/query-builder/select/predicate-facet.ts +104 -104
  167. package/src/query-builder/select/projection-facet.ts +70 -70
  168. package/src/query-builder/select/relation-facet.ts +81 -81
  169. package/src/query-builder/select/setop-facet.ts +36 -36
  170. package/src/query-builder/select-helpers.ts +1 -1
  171. package/src/query-builder/select-query-builder-deps.ts +130 -130
  172. package/src/query-builder/select-query-state.ts +207 -207
  173. package/src/query-builder/update.ts +151 -151
  174. package/src/schema/column-types.ts +322 -322
  175. package/src/schema/table-guards.ts +37 -37
  176. package/src/schema/table.ts +281 -281
  177. package/src/tree/index.ts +13 -13
  178. package/src/tree/nested-set-strategy.ts +545 -545
  179. package/src/tree/tree-decorator.ts +397 -397
  180. package/src/tree/tree-manager.ts +754 -754
  181. package/src/tree/tree-query.ts +427 -427
  182. 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({