metal-orm 1.1.11 → 1.1.18
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.
- package/README.md +758 -769
- package/dist/index.cjs.map +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +100 -96
- package/scripts/generate-entities/schema.mjs +122 -122
- package/scripts/generate-entities/tree-detection.mjs +92 -92
- package/scripts/inflection/pt-br.mjs +468 -468
- package/scripts/run-eslint.mjs +33 -34
- package/scripts/show-sql-seed.sql +102 -0
- package/scripts/show-sql.mjs +321 -325
- package/src/codegen/naming-strategy.ts +54 -54
- package/src/codegen/typescript.ts +482 -482
- package/src/core/ast/adapters.ts +1 -1
- package/src/core/ast/builders.ts +123 -123
- package/src/core/ast/expression-builders.ts +745 -745
- package/src/core/ast/expression-nodes.ts +304 -304
- package/src/core/ast/expression-visitor.ts +212 -212
- package/src/core/ast/expression.ts +20 -20
- package/src/core/ast/helpers.ts +14 -14
- package/src/core/ast/join.ts +18 -18
- package/src/core/ast/query.ts +200 -200
- package/src/core/ddl/dialects/base-schema-dialect.ts +1 -1
- package/src/core/ddl/dialects/mssql-schema-dialect.ts +1 -1
- package/src/core/ddl/dialects/mysql-schema-dialect.ts +1 -1
- package/src/core/ddl/dialects/postgres-schema-dialect.ts +1 -1
- package/src/core/ddl/dialects/render-reference.test.ts +70 -70
- package/src/core/ddl/dialects/sqlite-schema-dialect.ts +1 -1
- package/src/core/ddl/introspect/catalogs/index.ts +5 -5
- package/src/core/ddl/introspect/catalogs/postgres.ts +146 -146
- package/src/core/ddl/introspect/context.ts +15 -15
- package/src/core/ddl/introspect/functions/postgres.ts +35 -35
- package/src/core/ddl/introspect/registry.ts +40 -40
- package/src/core/ddl/introspect/run-select.ts +35 -35
- package/src/core/ddl/introspect/utils.ts +56 -56
- package/src/core/ddl/naming-strategy.ts +16 -16
- package/src/core/ddl/schema-dialect.ts +56 -56
- package/src/core/ddl/schema-diff.ts +234 -234
- package/src/core/ddl/schema-generator.ts +1 -1
- package/src/core/ddl/schema-introspect.ts +25 -25
- package/src/core/ddl/sql-writing.ts +171 -171
- package/src/core/dialect/abstract.ts +541 -541
- package/src/core/dialect/base/cte-compiler.ts +33 -33
- package/src/core/dialect/base/function-table-formatter.ts +103 -103
- package/src/core/dialect/base/groupby-compiler.ts +21 -21
- package/src/core/dialect/base/join-compiler.ts +25 -25
- package/src/core/dialect/base/orderby-compiler.ts +35 -35
- package/src/core/dialect/base/pagination-strategy.ts +32 -32
- package/src/core/dialect/base/returning-strategy.ts +57 -57
- package/src/core/dialect/base/sql-dialect.ts +294 -294
- package/src/core/dialect/dialect-factory.ts +91 -91
- package/src/core/dialect/mssql/functions.ts +120 -120
- package/src/core/dialect/mssql/index.ts +317 -317
- package/src/core/dialect/mysql/functions.ts +103 -103
- package/src/core/dialect/mysql/index.ts +105 -105
- package/src/core/dialect/postgres/functions.ts +108 -108
- package/src/core/dialect/postgres/index.ts +91 -91
- package/src/core/dialect/sqlite/functions.ts +129 -129
- package/src/core/dialect/sqlite/index.ts +67 -67
- package/src/core/driver/database-driver.ts +19 -19
- package/src/core/driver/mssql-driver.ts +23 -23
- package/src/core/driver/mysql-driver.ts +23 -23
- package/src/core/driver/postgres-driver.ts +23 -23
- package/src/core/driver/sqlite-driver.ts +23 -23
- package/src/core/execution/db-executor.ts +92 -92
- package/src/core/execution/executors/better-sqlite3-executor.ts +90 -90
- package/src/core/execution/executors/mssql-executor.ts +184 -184
- package/src/core/execution/executors/mysql-executor.ts +88 -88
- package/src/core/execution/executors/postgres-executor.ts +26 -26
- package/src/core/execution/executors/sqlite-executor.ts +42 -42
- package/src/core/execution/pooling/pool-types.ts +30 -30
- package/src/core/execution/pooling/pool.ts +275 -275
- package/src/core/functions/array.ts +35 -35
- package/src/core/functions/control-flow.ts +79 -79
- package/src/core/functions/datetime.ts +237 -237
- package/src/core/functions/json.ts +70 -70
- package/src/core/functions/numeric.ts +297 -297
- package/src/core/functions/text.ts +334 -334
- package/src/core/functions/types.ts +33 -33
- package/src/core/hydration/types.ts +51 -51
- package/src/core/sql/sql-operator-config.ts +39 -39
- package/src/core/sql/sql.ts +152 -152
- package/src/decorators/column-decorator.ts +87 -87
- package/src/decorators/decorator-metadata.ts +143 -143
- package/src/decorators/entity.ts +93 -93
- package/src/decorators/index.ts +39 -39
- package/src/decorators/relations.ts +231 -231
- package/src/decorators/transformers/built-in/string-transformers.ts +231 -231
- package/src/decorators/transformers/transformer-decorators.ts +134 -134
- package/src/decorators/transformers/transformer-executor.ts +171 -171
- package/src/decorators/transformers/transformer-metadata.ts +73 -73
- package/src/decorators/validators/built-in/br-cep-validator.ts +55 -55
- package/src/decorators/validators/built-in/br-cnpj-validator.ts +105 -105
- package/src/decorators/validators/built-in/br-cpf-validator.ts +103 -103
- package/src/decorators/validators/country-validator-registry.ts +64 -64
- package/src/decorators/validators/country-validators-decorators.ts +203 -203
- package/src/decorators/validators/country-validators.ts +105 -105
- package/src/decorators/validators/validator-metadata.ts +59 -59
- package/src/dto/dto-types.ts +226 -226
- package/src/dto/filter-types.ts +141 -141
- package/src/dto/index.ts +95 -95
- package/src/dto/openapi/generators/base.ts +29 -29
- package/src/dto/openapi/generators/column.ts +37 -37
- package/src/dto/openapi/generators/dto.ts +91 -91
- package/src/dto/openapi/generators/filter.ts +75 -75
- package/src/dto/openapi/generators/nested-dto.ts +618 -618
- package/src/dto/openapi/generators/pagination.ts +111 -111
- package/src/dto/openapi/generators/relation-filter.ts +228 -228
- package/src/dto/openapi/index.ts +12 -12
- package/src/dto/openapi/types.ts +101 -101
- package/src/dto/openapi/utilities.ts +95 -95
- package/src/dto/pagination-utils.ts +150 -150
- package/src/dto/transform.ts +197 -197
- package/src/index.ts +86 -86
- package/src/orm/als.ts +58 -58
- package/src/orm/domain-event-bus.ts +119 -119
- package/src/orm/entity-context.ts +86 -86
- package/src/orm/entity-materializer.ts +159 -159
- package/src/orm/entity-meta.ts +97 -97
- package/src/orm/entity-registry.ts +39 -39
- package/src/orm/entity-relation-cache.ts +39 -39
- package/src/orm/execute.ts +194 -194
- package/src/orm/execution-context.ts +18 -18
- package/src/orm/hydration-context.ts +26 -26
- package/src/orm/hydration.ts +112 -112
- package/src/orm/identity-map.ts +61 -61
- package/src/orm/interceptor-pipeline.ts +16 -16
- package/src/orm/lazy-batch/belongs-to-many.ts +119 -119
- package/src/orm/lazy-batch/belongs-to.ts +108 -108
- package/src/orm/lazy-batch/has-many.ts +69 -69
- package/src/orm/lazy-batch/has-one.ts +68 -68
- package/src/orm/lazy-batch/shared.ts +125 -125
- package/src/orm/orm-session.ts +620 -620
- package/src/orm/orm.ts +34 -34
- package/src/orm/pooled-executor-factory.ts +121 -121
- package/src/orm/query-logger.ts +40 -40
- package/src/orm/relation-change-processor.ts +420 -420
- package/src/orm/relations/belongs-to.ts +143 -143
- package/src/orm/relations/has-many.ts +204 -204
- package/src/orm/relations/has-one.ts +174 -174
- package/src/orm/relations/many-to-many.ts +288 -288
- package/src/orm/relations/morph-many.ts +156 -156
- package/src/orm/relations/morph-one.ts +151 -151
- package/src/orm/relations/morph-to.ts +162 -162
- package/src/orm/runtime-types.ts +92 -92
- package/src/orm/save-graph.ts +561 -561
- package/src/orm/transaction-runner.ts +24 -24
- package/src/orm/unit-of-work.ts +426 -426
- package/src/query/index.ts +69 -69
- package/src/query/target.ts +46 -46
- package/src/query-builder/column-selector.ts +81 -81
- package/src/query-builder/delete.ts +141 -141
- package/src/query-builder/insert-query-state.ts +149 -149
- package/src/query-builder/insert.ts +117 -117
- package/src/query-builder/query-ast-service.ts +287 -287
- package/src/query-builder/query-resolution.ts +78 -78
- package/src/query-builder/raw-column-parser.ts +38 -38
- package/src/query-builder/relation-filter-utils.ts +153 -153
- package/src/query-builder/relation-join-planner.ts +10 -10
- package/src/query-builder/relation-manager.ts +87 -87
- package/src/query-builder/relation-projection-helper.ts +102 -102
- package/src/query-builder/relation-types.ts +9 -9
- package/src/query-builder/relation-utils.ts +15 -15
- package/src/query-builder/select/cte-facet.ts +40 -40
- package/src/query-builder/select/from-facet.ts +80 -80
- package/src/query-builder/select/join-facet.ts +62 -62
- package/src/query-builder/select/predicate-facet.ts +104 -104
- package/src/query-builder/select/projection-facet.ts +70 -70
- package/src/query-builder/select/relation-facet.ts +81 -81
- package/src/query-builder/select/setop-facet.ts +36 -36
- package/src/query-builder/select-helpers.ts +1 -1
- package/src/query-builder/select-query-builder-deps.ts +130 -130
- package/src/query-builder/select-query-state.ts +207 -207
- package/src/query-builder/update.ts +151 -151
- package/src/schema/column-types.ts +322 -322
- package/src/schema/table-guards.ts +37 -37
- package/src/schema/table.ts +281 -281
- package/src/tree/index.ts +13 -13
- package/src/tree/nested-set-strategy.ts +545 -545
- package/src/tree/tree-decorator.ts +397 -397
- package/src/tree/tree-manager.ts +754 -754
- package/src/tree/tree-query.ts +427 -427
- 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({
|