dsl-to-sql 1.0.0-fabric-1p-development.1
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/.turbo/turbo-build.log +9 -0
- package/.turbo/turbo-check-types.log +4 -0
- package/.turbo/turbo-lint.log +126 -0
- package/README.md +45 -0
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +236 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +210 -0
- package/coverage/src/constants.ts.html +826 -0
- package/coverage/src/database_types.ts.html +136 -0
- package/coverage/src/epm-query-builder/EpmQueryBuilder.ts.html +166 -0
- package/coverage/src/epm-query-builder/base/BaseAdvancedAggregations.ts.html +568 -0
- package/coverage/src/epm-query-builder/base/BaseAnalyticalFunctions.ts.html +694 -0
- package/coverage/src/epm-query-builder/base/BaseCTEGenerator.ts.html +1459 -0
- package/coverage/src/epm-query-builder/base/BaseCountQueryBuilder.ts.html +400 -0
- package/coverage/src/epm-query-builder/base/BaseMeasureBuilder.ts.html +295 -0
- package/coverage/src/epm-query-builder/base/BaseOrderBuilder.ts.html +670 -0
- package/coverage/src/epm-query-builder/base/BasePaginationBuilder.ts.html +364 -0
- package/coverage/src/epm-query-builder/base/BaseQueryBuilder.ts.html +238 -0
- package/coverage/src/epm-query-builder/base/BaseRollupBuilder.ts.html +532 -0
- package/coverage/src/epm-query-builder/base/BaseSqlBuilder.ts.html +601 -0
- package/coverage/src/epm-query-builder/base/BaseSuperFilterBuilder.ts.html +1966 -0
- package/coverage/src/epm-query-builder/base/BaseUtilities.ts.html +1798 -0
- package/coverage/src/epm-query-builder/base/ColumnRefUtils.ts.html +211 -0
- package/coverage/src/epm-query-builder/base/RelationshipResolver.ts.html +706 -0
- package/coverage/src/epm-query-builder/base/SharedFilterBuilder.ts.html +1717 -0
- package/coverage/src/epm-query-builder/base/index.html +326 -0
- package/coverage/src/epm-query-builder/constants/Aggregations.ts.html +133 -0
- package/coverage/src/epm-query-builder/constants/Database.ts.html +103 -0
- package/coverage/src/epm-query-builder/constants/Source.ts.html +106 -0
- package/coverage/src/epm-query-builder/constants/index.html +146 -0
- package/coverage/src/epm-query-builder/dialects/duckdb/DuckDbAdvancedAggregations.ts.html +286 -0
- package/coverage/src/epm-query-builder/dialects/duckdb/DuckDbJoinBuilder.ts.html +280 -0
- package/coverage/src/epm-query-builder/dialects/duckdb/DuckDbMeasureBuilder.ts.html +1924 -0
- package/coverage/src/epm-query-builder/dialects/duckdb/DuckDbOrderBuilder.ts.html +769 -0
- package/coverage/src/epm-query-builder/dialects/duckdb/DuckDbPaginationBuilder.ts.html +643 -0
- package/coverage/src/epm-query-builder/dialects/duckdb/DuckDbQueryBuilder.ts.html +2644 -0
- package/coverage/src/epm-query-builder/dialects/duckdb/DuckDbRollupBuilder.ts.html +478 -0
- package/coverage/src/epm-query-builder/dialects/duckdb/DuckDbSuperFilterBuilder.ts.html +1195 -0
- package/coverage/src/epm-query-builder/dialects/duckdb/index.html +221 -0
- package/coverage/src/epm-query-builder/errors/QueryBuilderErrors.ts.html +280 -0
- package/coverage/src/epm-query-builder/errors/index.html +116 -0
- package/coverage/src/epm-query-builder/index.html +116 -0
- package/coverage/src/epm-query-builder/interfaces/IDatabaseQueryBuilder.ts.html +100 -0
- package/coverage/src/epm-query-builder/interfaces/index.html +131 -0
- package/coverage/src/epm-query-builder/interfaces/index.ts.html +88 -0
- package/coverage/src/epm-query-builder/utils/format.ts.html +151 -0
- package/coverage/src/epm-query-builder/utils/index.html +146 -0
- package/coverage/src/epm-query-builder/utils/sql.ts.html +247 -0
- package/coverage/src/epm-query-builder/utils/validation.ts.html +124 -0
- package/coverage/src/epm-query-builder/validation/QueryOptionsValidator.ts.html +631 -0
- package/coverage/src/epm-query-builder/validation/SqlQueryValidator.ts.html +475 -0
- package/coverage/src/epm-query-builder/validation/index.html +131 -0
- package/coverage/src/filters/FilterConditionBuilder.ts.html +427 -0
- package/coverage/src/filters/filter-types.ts.html +484 -0
- package/coverage/src/filters/index.html +131 -0
- package/coverage/src/index.html +176 -0
- package/coverage/src/index.ts.html +103 -0
- package/coverage/src/js-lib/JsToSqlParser.ts.html +736 -0
- package/coverage/src/js-lib/ParseContext.ts.html +532 -0
- package/coverage/src/js-lib/db/azuresql/AzureSqlCallExpressionVisitor.ts.html +196 -0
- package/coverage/src/js-lib/db/azuresql/index.html +116 -0
- package/coverage/src/js-lib/db/base/ArrayExpressionVisitor.ts.html +133 -0
- package/coverage/src/js-lib/db/base/AssignmentExpressionVisitor.ts.html +187 -0
- package/coverage/src/js-lib/db/base/BinaryExpressionVisitor.ts.html +223 -0
- package/coverage/src/js-lib/db/base/CallExpressionVisitor.ts.html +5479 -0
- package/coverage/src/js-lib/db/base/IdentifierVisitor.ts.html +283 -0
- package/coverage/src/js-lib/db/base/LiteralVisitor.ts.html +199 -0
- package/coverage/src/js-lib/db/base/MemberExpressionVisitor.ts.html +193 -0
- package/coverage/src/js-lib/db/base/ProgramVisitor.ts.html +139 -0
- package/coverage/src/js-lib/db/base/UnaryExpressionVisitor.ts.html +181 -0
- package/coverage/src/js-lib/db/base/VisitorInterface.ts.html +103 -0
- package/coverage/src/js-lib/db/base/index.html +251 -0
- package/coverage/src/js-lib/db/bigquery/BigQueryCallExpressionVisitor.ts.html +1747 -0
- package/coverage/src/js-lib/db/bigquery/index.html +116 -0
- package/coverage/src/js-lib/db/commonTransforms.ts.html +2074 -0
- package/coverage/src/js-lib/db/databricks/DatabricksCallExpressionVisitor.ts.html +1303 -0
- package/coverage/src/js-lib/db/databricks/index.html +116 -0
- package/coverage/src/js-lib/db/fabricsql/FabricSqlCallExpressionVisitor.ts.html +196 -0
- package/coverage/src/js-lib/db/fabricsql/index.html +116 -0
- package/coverage/src/js-lib/db/fabricwarehouse/FabricWarehouseCallExpressionVisitor.ts.html +292 -0
- package/coverage/src/js-lib/db/fabricwarehouse/index.html +116 -0
- package/coverage/src/js-lib/db/index.html +116 -0
- package/coverage/src/js-lib/db/postgresql/PostgreSqlCallExpressionVisitor.ts.html +985 -0
- package/coverage/src/js-lib/db/postgresql/index.html +116 -0
- package/coverage/src/js-lib/db/redshift/RedshiftCallExpressionVisitor.ts.html +685 -0
- package/coverage/src/js-lib/db/redshift/index.html +116 -0
- package/coverage/src/js-lib/db/sample/SampleCallExpressionVisitor.ts.html +196 -0
- package/coverage/src/js-lib/db/sample/index.html +116 -0
- package/coverage/src/js-lib/db/snowflake/SnowflakeCallExpressionVisitor.ts.html +1447 -0
- package/coverage/src/js-lib/db/snowflake/index.html +116 -0
- package/coverage/src/js-lib/db/validator/FormulaValidator.ts.html +4162 -0
- package/coverage/src/js-lib/db/validator/index.html +116 -0
- package/coverage/src/js-lib/index.html +131 -0
- package/coverage/src/js-lib/objects/BaseObject.ts.html +169 -0
- package/coverage/src/js-lib/objects/DateObject.ts.html +169 -0
- package/coverage/src/js-lib/objects/PctObject.ts.html +178 -0
- package/coverage/src/js-lib/objects/index.html +146 -0
- package/coverage/src/query-builder/PaginationBuilder.ts.html +142 -0
- package/coverage/src/query-builder/QueryBuilder.ts.html +3118 -0
- package/coverage/src/query-builder/SuperFilterBuilder.ts.html +1969 -0
- package/coverage/src/query-builder/index.html +146 -0
- package/coverage/src/runtime_var.ts.html +109 -0
- package/coverage/src/sql-lib/binary_expr.ts.html +133 -0
- package/coverage/src/sql-lib/case.ts.html +133 -0
- package/coverage/src/sql-lib/column.ts.html +139 -0
- package/coverage/src/sql-lib/else.ts.html +124 -0
- package/coverage/src/sql-lib/function.ts.html +112 -0
- package/coverage/src/sql-lib/index.html +251 -0
- package/coverage/src/sql-lib/join.ts.html +127 -0
- package/coverage/src/sql-lib/literal.ts.html +130 -0
- package/coverage/src/sql-lib/select.ts.html +547 -0
- package/coverage/src/sql-lib/unary_expr.ts.html +112 -0
- package/coverage/src/sql-lib/when.ts.html +130 -0
- package/coverage/src/sql_query_gen.ts.html +535 -0
- package/coverage/src/superFilter/DateFilterFactory.ts.html +625 -0
- package/coverage/src/superFilter/dateFunction.ts.html +193 -0
- package/coverage/src/superFilter/index.html +131 -0
- package/coverage/src/utils.ts.html +571 -0
- package/dist/index.cjs +8440 -0
- package/dist/index.d.cts +927 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +927 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8387 -0
- package/dist/index.js.map +1 -0
- package/eslint.config.js +4 -0
- package/jest.config.ts +44 -0
- package/package.json +45 -0
- package/src/constants.ts +247 -0
- package/src/epm-query-builder/EpmQueryBuilder.ts +27 -0
- package/src/epm-query-builder/base/BaseAdvancedAggregations.ts +161 -0
- package/src/epm-query-builder/base/BaseAnalyticalFunctions.ts +203 -0
- package/src/epm-query-builder/base/BaseCTEGenerator.ts +458 -0
- package/src/epm-query-builder/base/BaseCountQueryBuilder.ts +105 -0
- package/src/epm-query-builder/base/BaseMeasureBuilder.ts +87 -0
- package/src/epm-query-builder/base/BaseOrderBuilder.ts +195 -0
- package/src/epm-query-builder/base/BasePaginationBuilder.ts +93 -0
- package/src/epm-query-builder/base/BaseQueryBuilder.ts +51 -0
- package/src/epm-query-builder/base/BaseRollupBuilder.ts +149 -0
- package/src/epm-query-builder/base/BaseSqlBuilder.ts +172 -0
- package/src/epm-query-builder/base/BaseSuperFilterBuilder.ts +627 -0
- package/src/epm-query-builder/base/BaseUtilities.ts +571 -0
- package/src/epm-query-builder/base/ColumnRefUtils.ts +42 -0
- package/src/epm-query-builder/base/RelationshipResolver.ts +207 -0
- package/src/epm-query-builder/base/SharedFilterBuilder.ts +544 -0
- package/src/epm-query-builder/constants/Aggregations.ts +16 -0
- package/src/epm-query-builder/constants/Database.ts +6 -0
- package/src/epm-query-builder/constants/Source.ts +7 -0
- package/src/epm-query-builder/dialects/duckdb/DuckDbAdvancedAggregations.ts +67 -0
- package/src/epm-query-builder/dialects/duckdb/DuckDbJoinBuilder.ts +65 -0
- package/src/epm-query-builder/dialects/duckdb/DuckDbMeasureBuilder.ts +626 -0
- package/src/epm-query-builder/dialects/duckdb/DuckDbOrderBuilder.ts +228 -0
- package/src/epm-query-builder/dialects/duckdb/DuckDbPaginationBuilder.ts +186 -0
- package/src/epm-query-builder/dialects/duckdb/DuckDbQueryBuilder.ts +853 -0
- package/src/epm-query-builder/dialects/duckdb/DuckDbRollupBuilder.ts +131 -0
- package/src/epm-query-builder/dialects/duckdb/DuckDbSuperFilterBuilder.ts +370 -0
- package/src/epm-query-builder/errors/QueryBuilderErrors.ts +65 -0
- package/src/epm-query-builder/interfaces/IDatabaseQueryBuilder.ts +5 -0
- package/src/epm-query-builder/interfaces/index.ts +1 -0
- package/src/epm-query-builder/types/query-builder-types.d.ts +289 -0
- package/src/epm-query-builder/utils/format.ts +22 -0
- package/src/epm-query-builder/utils/sql.ts +54 -0
- package/src/epm-query-builder/utils/validation.ts +13 -0
- package/src/epm-query-builder/validation/QueryOptionsValidator.ts +182 -0
- package/src/epm-query-builder/validation/SqlQueryValidator.ts +130 -0
- package/src/filters/FilterConditionBuilder.ts +114 -0
- package/src/filters/filter-types.ts +133 -0
- package/src/index.ts +10 -0
- package/src/js-lib/JsToSqlParser.ts +217 -0
- package/src/js-lib/ParseContext.ts +149 -0
- package/src/js-lib/db/base/ArrayExpressionVisitor.ts +16 -0
- package/src/js-lib/db/base/AssignmentExpressionVisitor.ts +34 -0
- package/src/js-lib/db/base/BinaryExpressionVisitor.ts +46 -0
- package/src/js-lib/db/base/CallExpressionVisitor.ts +1798 -0
- package/src/js-lib/db/base/IdentifierVisitor.ts +66 -0
- package/src/js-lib/db/base/LiteralVisitor.ts +38 -0
- package/src/js-lib/db/base/MemberExpressionVisitor.ts +36 -0
- package/src/js-lib/db/base/ProgramVisitor.ts +18 -0
- package/src/js-lib/db/base/UnaryExpressionVisitor.ts +32 -0
- package/src/js-lib/db/base/VisitorInterface.ts +6 -0
- package/src/js-lib/db/validator/FormulaValidator.ts +1235 -0
- package/src/js-lib/objects/BaseObject.ts +28 -0
- package/src/js-lib/objects/DateObject.ts +28 -0
- package/src/js-lib/objects/PctObject.ts +31 -0
- package/src/query-builder/PaginationBuilder.ts +19 -0
- package/src/query-builder/QueryBuilder.ts +1035 -0
- package/src/query-builder/SuperFilterBuilder.ts +628 -0
- package/src/runtime_var.ts +8 -0
- package/src/sql-lib/binary_expr.ts +16 -0
- package/src/sql-lib/case.ts +16 -0
- package/src/sql-lib/column.ts +18 -0
- package/src/sql-lib/else.ts +13 -0
- package/src/sql-lib/function.ts +9 -0
- package/src/sql-lib/join.ts +14 -0
- package/src/sql-lib/literal.ts +15 -0
- package/src/sql-lib/select.ts +154 -0
- package/src/sql-lib/unary_expr.ts +9 -0
- package/src/sql-lib/when.ts +15 -0
- package/src/sql-types.d.ts +565 -0
- package/src/sql_query_gen.ts +150 -0
- package/src/superFilter/DateFilterFactory.ts +180 -0
- package/src/superFilter/dateFunction.ts +36 -0
- package/src/utils.ts +354 -0
- package/test-output/report/junit.xml +329 -0
- package/tests/JsToSqlParser.test.ts +163 -0
- package/tests/QueryBuilder.test.ts +1320 -0
- package/tests/js-lib/CallExpressionVisitor.test.ts +820 -0
- package/tests/mocks/MockQueryResolver.ts +14 -0
- package/tests/sanity.test.ts +146 -0
- package/tests/sql-lib/binary_expr.test.ts +75 -0
- package/tests/sql-lib/case.test.ts +117 -0
- package/tests/sql-lib/column.test.ts +87 -0
- package/tests/sql-lib/else.test.ts +56 -0
- package/tests/sql-lib/function.test.ts +96 -0
- package/tests/sql-lib/literal.test.ts +75 -0
- package/tests/sql-lib/select.test.ts +245 -0
- package/tests/sql-lib/unary_expr.test.ts +32 -0
- package/tests/utils.test.ts +13 -0
- package/tsconfig.json +24 -0
- package/tsdown.config.ts +23 -0
|
@@ -0,0 +1,1035 @@
|
|
|
1
|
+
import {
|
|
2
|
+
IMDMColumnConfigWithParsedMeta,
|
|
3
|
+
MDM_COLUMN_TYPE,
|
|
4
|
+
ResolvedExpressionType,
|
|
5
|
+
CteExpression,
|
|
6
|
+
} from '../js-lib/ParseContext';
|
|
7
|
+
import {
|
|
8
|
+
FilterConfig,
|
|
9
|
+
JoinExpressions,
|
|
10
|
+
Rule,
|
|
11
|
+
RuleGroup,
|
|
12
|
+
ISuperFilterConfig,
|
|
13
|
+
IGroupFilter,
|
|
14
|
+
IManualFilterConfig,
|
|
15
|
+
} from '../filters/filter-types';
|
|
16
|
+
import { RUNTIME_TABLE_NAME } from '../runtime_var';
|
|
17
|
+
import { PaginationBuilder, createPaginationBuilder } from './PaginationBuilder';
|
|
18
|
+
import { From, RawSqlExpression, Select } from '../sql-types';
|
|
19
|
+
import { SelectBuilder } from '../sql-lib/select';
|
|
20
|
+
import { toColumn } from '../sql-lib/column';
|
|
21
|
+
import { processSelect } from '../sql_query_gen';
|
|
22
|
+
import {
|
|
23
|
+
getCurrentDate,
|
|
24
|
+
getCurrentDateTime,
|
|
25
|
+
getEntityNameFormatter,
|
|
26
|
+
getFormattedTableName,
|
|
27
|
+
getJoinExpressions,
|
|
28
|
+
resolveRuntimeVariables,
|
|
29
|
+
stripWhereClause,
|
|
30
|
+
} from '../utils';
|
|
31
|
+
import { SuperFilterBuilder } from './SuperFilterBuilder';
|
|
32
|
+
|
|
33
|
+
export type SortConfig = { column: string; order: 'ASC' | 'DESC' }[];
|
|
34
|
+
|
|
35
|
+
export type PaginationConfig = { skip: number; take: number };
|
|
36
|
+
|
|
37
|
+
export type FilterResolverParams = {
|
|
38
|
+
filterConfig: FilterConfig;
|
|
39
|
+
schema?: IColumnSchemaItem[];
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export interface QueryResolver {
|
|
43
|
+
filterResolver(params: FilterResolverParams): string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type RuntimeVariables = {
|
|
47
|
+
$RUNTIME_LOGGEDIN_NAME: string;
|
|
48
|
+
$RUNTIME_LOGGEDIN_EMAIL: string;
|
|
49
|
+
$RUNTIME_CURRENT_DATE?: string;
|
|
50
|
+
$RUNTIME_CURRENT_DATETIME?: string;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export type QueryBuilderOptions = {
|
|
54
|
+
selectedColumns: string[];
|
|
55
|
+
databaseDetails: Record<string, any>;
|
|
56
|
+
primaryKeyColumns: string[];
|
|
57
|
+
filterConfig?: FilterConfig;
|
|
58
|
+
pagination?: PaginationConfig;
|
|
59
|
+
sortConfig?: SortConfig;
|
|
60
|
+
searchExpression?: string;
|
|
61
|
+
distinct?: boolean;
|
|
62
|
+
columnConfigMap: Record<string, IMDMColumnConfigWithParsedMeta>;
|
|
63
|
+
queryResolver: QueryResolver; // instance of BaseDB in database-service-link
|
|
64
|
+
runtimeVariables: RuntimeVariables;
|
|
65
|
+
schema?: IColumnSchemaItem[];
|
|
66
|
+
includeFinalCTESelectQuery?: boolean;
|
|
67
|
+
finalResponseAsObject?: boolean;
|
|
68
|
+
superFilters?: ISuperFilterConfig;
|
|
69
|
+
skipCountForRankingFilter?: boolean;
|
|
70
|
+
joinClauses?: JoinExpressions;
|
|
71
|
+
manualFilterOptions?: IManualFilterConfig[];
|
|
72
|
+
enableLastSelectCTEWrap?: boolean;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
type BuildState = {
|
|
76
|
+
pendingFilterConfig: FilterConfig;
|
|
77
|
+
pendingSortConfig: SortConfig;
|
|
78
|
+
isSortPending: boolean;
|
|
79
|
+
isPaginationPending: boolean;
|
|
80
|
+
isSearchPending: boolean;
|
|
81
|
+
hasFormulaColumns: boolean;
|
|
82
|
+
isSuperFilterPending: boolean;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
type BuildOptions = Partial<Omit<QueryBuilderOptions, 'tableName' | 'queryResolver' | 'schema'>>;
|
|
86
|
+
|
|
87
|
+
type MainCteExpression = {
|
|
88
|
+
type: ResolvedExpressionType.CTE;
|
|
89
|
+
cteName: string;
|
|
90
|
+
value: Select;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export type FinalQueryResult = {
|
|
94
|
+
finalQuery: string;
|
|
95
|
+
finalCteName?: string; // For group by feat need to pass cte name
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
type CalculationOrderResult = {
|
|
99
|
+
nativeColumns: Set<string>;
|
|
100
|
+
formulaColumnsByDependencyLevel: IMDMColumnConfigWithParsedMeta[][];
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export enum DEFAULT_VALUE_TYPE {
|
|
104
|
+
MANUAL = 'MANUAL',
|
|
105
|
+
DERIVED = 'DERIVED',
|
|
106
|
+
NONE = 'NONE',
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
type IColumnSchemaItem = {
|
|
110
|
+
name: string;
|
|
111
|
+
type: string;
|
|
112
|
+
defaultValue?: string | number;
|
|
113
|
+
isNullable?: boolean;
|
|
114
|
+
isIdentity?: boolean;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export const MAX_STRING_TYPE = 'nvarchar(max)';
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Steps:
|
|
121
|
+
* 1. Separate out filters for native columns and formula columns
|
|
122
|
+
* 2. Apply filters for native columns in the main CTE. If Native columns have sorting config then -
|
|
123
|
+
* - If SortConfig exists for Formula columns with cte, do not apply sorting
|
|
124
|
+
* - If SortConfig exists for Formula columns without cte, apply sorting
|
|
125
|
+
* - If SortConfig does not exist for Formula columns, apply sorting
|
|
126
|
+
* 3. Apply filters for formula columns in the CTE followed by the column specific CTEs due to alias not supported in where clause
|
|
127
|
+
* 4. Apply SortConfig and pagination in the CTE with last filter
|
|
128
|
+
* 5. Perform final projection and apply SortConfig and Pagination, if still exists
|
|
129
|
+
*/
|
|
130
|
+
export class QueryBuilder {
|
|
131
|
+
private readonly paginationBuilder: PaginationBuilder;
|
|
132
|
+
private readonly options: QueryBuilderOptions;
|
|
133
|
+
|
|
134
|
+
constructor(options: QueryBuilderOptions) {
|
|
135
|
+
this.options = options;
|
|
136
|
+
this.paginationBuilder = createPaginationBuilder();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
build(options: BuildOptions = {}): FinalQueryResult {
|
|
140
|
+
const combinedOptions = { ...this.options, ...options };
|
|
141
|
+
const impl = new QueryBuilderImpl(combinedOptions, this.paginationBuilder);
|
|
142
|
+
return impl.build();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
buildCountQuery(options: BuildOptions = {}): FinalQueryResult {
|
|
146
|
+
const combinedOptions = {
|
|
147
|
+
...this.options,
|
|
148
|
+
...options,
|
|
149
|
+
sortConfig: undefined, // not required for count query
|
|
150
|
+
pagination: undefined, // not required for count query
|
|
151
|
+
};
|
|
152
|
+
const impl = new QueryBuilderImpl(combinedOptions, this.paginationBuilder);
|
|
153
|
+
return impl.build(true);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Implementation class for QueryBuilder
|
|
159
|
+
*
|
|
160
|
+
* @private exposed for testing
|
|
161
|
+
*/
|
|
162
|
+
export class QueryBuilderImpl {
|
|
163
|
+
private readonly paginationBuilder: PaginationBuilder;
|
|
164
|
+
private readonly options: QueryBuilderOptions;
|
|
165
|
+
private buildState: BuildState;
|
|
166
|
+
private readonly entityNameFormatter: (name: string, type?: string) => string;
|
|
167
|
+
|
|
168
|
+
constructor(options: QueryBuilderOptions, paginationBuilder: PaginationBuilder) {
|
|
169
|
+
this.options = {
|
|
170
|
+
...options,
|
|
171
|
+
searchExpression: options.searchExpression
|
|
172
|
+
? stripWhereClause(options.searchExpression)
|
|
173
|
+
: undefined,
|
|
174
|
+
sortConfig: this.getRelevantSortConfig(options.sortConfig, new Set(options.selectedColumns)),
|
|
175
|
+
};
|
|
176
|
+
this.paginationBuilder = paginationBuilder;
|
|
177
|
+
this.buildState = {
|
|
178
|
+
pendingFilterConfig: {
|
|
179
|
+
...(options.filterConfig ?? { combinator: 'and', not: false }),
|
|
180
|
+
rules: [],
|
|
181
|
+
},
|
|
182
|
+
hasFormulaColumns: false,
|
|
183
|
+
pendingSortConfig: [],
|
|
184
|
+
isSortPending: true,
|
|
185
|
+
isPaginationPending: true,
|
|
186
|
+
isSearchPending: options.searchExpression !== undefined,
|
|
187
|
+
isSuperFilterPending: options.superFilters !== undefined,
|
|
188
|
+
};
|
|
189
|
+
this.entityNameFormatter = getEntityNameFormatter();
|
|
190
|
+
// Set runtime variables with current date/time values
|
|
191
|
+
Object.assign(this.options.runtimeVariables, {
|
|
192
|
+
$RUNTIME_CURRENT_DATE: getCurrentDate(),
|
|
193
|
+
$RUNTIME_CURRENT_DATETIME: getCurrentDateTime(),
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
build(isCountQuery = false): FinalQueryResult {
|
|
198
|
+
const { joinClauses, superFilters } = this.options;
|
|
199
|
+
const { joinRelation = [] } = joinClauses || {};
|
|
200
|
+
const requiredColumns = this.filterRequiredColumns();
|
|
201
|
+
this.addColumnsFromFilterConfig(requiredColumns);
|
|
202
|
+
const { nativeColumns, formulaColumnsByDependencyLevel } =
|
|
203
|
+
this.getCalculationOrder(requiredColumns);
|
|
204
|
+
this.buildState.hasFormulaColumns = formulaColumnsByDependencyLevel.length > 0;
|
|
205
|
+
this.buildState.isSuperFilterPending = superFilters !== undefined;
|
|
206
|
+
|
|
207
|
+
// build main CTE and intermediate CTE for formula columns join relation
|
|
208
|
+
const mainCteFormulas = joinRelation.length > 0 ? [] : formulaColumnsByDependencyLevel;
|
|
209
|
+
const columnExprs = this.retrieveColumnExprsInFront(mainCteFormulas);
|
|
210
|
+
const mainCTE = this.buildMainCTE(nativeColumns, columnExprs);
|
|
211
|
+
const intermediateCTE = this.buildIntermediateCTE(
|
|
212
|
+
mainCTE.cteName,
|
|
213
|
+
formulaColumnsByDependencyLevel,
|
|
214
|
+
);
|
|
215
|
+
return this.buildFinalProjection(mainCTE, intermediateCTE, isCountQuery);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Returns the sort config with only the columns that are part of the requested columns
|
|
220
|
+
*/
|
|
221
|
+
private getRelevantSortConfig(
|
|
222
|
+
sortConfig: SortConfig | undefined,
|
|
223
|
+
requestedColumns: Set<string>,
|
|
224
|
+
): SortConfig | undefined {
|
|
225
|
+
return sortConfig?.filter((s) => requestedColumns.has(s.column));
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Adds columns used in the filter config to the set of all columns
|
|
230
|
+
*/
|
|
231
|
+
private addColumnsFromFilterConfig(requiredColumns: Set<string>): void {
|
|
232
|
+
const { filterConfig } = this.options;
|
|
233
|
+
|
|
234
|
+
if (!filterConfig) return;
|
|
235
|
+
|
|
236
|
+
const traverse = (rule: Rule | RuleGroup) => {
|
|
237
|
+
if ('column' in rule) {
|
|
238
|
+
requiredColumns.add(rule.column);
|
|
239
|
+
} else if ('rules' in rule) {
|
|
240
|
+
rule.rules.forEach(traverse);
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
filterConfig.rules.forEach(traverse);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
private retrieveColumnExprsInFront(
|
|
247
|
+
calcOrderMembers: IMDMColumnConfigWithParsedMeta[][],
|
|
248
|
+
): IMDMColumnConfigWithParsedMeta[] {
|
|
249
|
+
if (calcOrderMembers.length === 0) return [];
|
|
250
|
+
|
|
251
|
+
let lastColumnExprIndex = -1;
|
|
252
|
+
const firstCalcOrderMembers = calcOrderMembers[0];
|
|
253
|
+
for (let i = 0; i < firstCalcOrderMembers.length; i++) {
|
|
254
|
+
if (
|
|
255
|
+
firstCalcOrderMembers[i].columnMeta.sqlQueryProps?.resolvedExpression.type ===
|
|
256
|
+
ResolvedExpressionType.COLUMN
|
|
257
|
+
) {
|
|
258
|
+
lastColumnExprIndex = i;
|
|
259
|
+
} else {
|
|
260
|
+
break;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (lastColumnExprIndex === -1) {
|
|
265
|
+
return [];
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const columnExprsInFront = firstCalcOrderMembers.splice(0, lastColumnExprIndex + 1);
|
|
269
|
+
if (firstCalcOrderMembers.length === 0) {
|
|
270
|
+
calcOrderMembers.shift();
|
|
271
|
+
}
|
|
272
|
+
return columnExprsInFront;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
private buildMainCTE(
|
|
276
|
+
nativeColumns: Set<string>,
|
|
277
|
+
columnExprs: IMDMColumnConfigWithParsedMeta[],
|
|
278
|
+
): MainCteExpression {
|
|
279
|
+
const columnsInMainCTE = new Set([...nativeColumns, ...columnExprs.map((c) => c.columnName)]);
|
|
280
|
+
const selectBuilder = new SelectBuilder();
|
|
281
|
+
const { joinClauses } = this.options;
|
|
282
|
+
const { alias } = joinClauses || {};
|
|
283
|
+
const tableAlias = alias ? `${this.entityNameFormatter(alias)}.` : '';
|
|
284
|
+
|
|
285
|
+
const columns = Array.from(nativeColumns).map((c) => {
|
|
286
|
+
const schemaMatch = this.options.schema?.find((s) => s.name === c);
|
|
287
|
+
return toColumn({
|
|
288
|
+
type: 'sql_expr',
|
|
289
|
+
value: `${tableAlias}` + this.entityNameFormatter(c, schemaMatch?.type ?? ''),
|
|
290
|
+
meta: { rawColumnName: c },
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
if (columnExprs.length) {
|
|
295
|
+
columnExprs.forEach((c) => {
|
|
296
|
+
const schemaMatch = this.options.schema?.find((s) => s.name === c.columnName);
|
|
297
|
+
let resolvedExpressionValue =
|
|
298
|
+
c.columnMeta.sqlQueryProps?.resolvedExpression?.value?.join(', ');
|
|
299
|
+
if (resolvedExpressionValue) {
|
|
300
|
+
resolvedExpressionValue = resolveRuntimeVariables(
|
|
301
|
+
resolvedExpressionValue,
|
|
302
|
+
this.options.runtimeVariables,
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
columns.push(
|
|
306
|
+
toColumn(
|
|
307
|
+
{
|
|
308
|
+
type: 'sql_expr',
|
|
309
|
+
value: resolvedExpressionValue ?? '',
|
|
310
|
+
meta: { rawColumnName: c.columnName },
|
|
311
|
+
},
|
|
312
|
+
this.entityNameFormatter(c.columnName, schemaMatch?.type ?? ''),
|
|
313
|
+
),
|
|
314
|
+
);
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const formattedTableName = getFormattedTableName(this.options.databaseDetails);
|
|
319
|
+
selectBuilder.addColumns(columns);
|
|
320
|
+
selectBuilder.addFrom([
|
|
321
|
+
{
|
|
322
|
+
type: 'from',
|
|
323
|
+
table: `${formattedTableName}${alias ? ` AS ${this.entityNameFormatter(alias)}` : ''}`,
|
|
324
|
+
} as From,
|
|
325
|
+
]);
|
|
326
|
+
|
|
327
|
+
// Build filter condition
|
|
328
|
+
const where = [];
|
|
329
|
+
const filterCondition = this.getFilterExpression(columnsInMainCTE);
|
|
330
|
+
if (filterCondition) {
|
|
331
|
+
where.push(filterCondition);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (
|
|
335
|
+
this.buildState.isSearchPending &&
|
|
336
|
+
this.buildState.isSuperFilterPending === false &&
|
|
337
|
+
this.buildState.hasFormulaColumns === false
|
|
338
|
+
) {
|
|
339
|
+
this.buildState.isSearchPending = false;
|
|
340
|
+
where.push(this.options.searchExpression);
|
|
341
|
+
}
|
|
342
|
+
if (where.length) {
|
|
343
|
+
selectBuilder.addWhere({
|
|
344
|
+
type: 'sql_expr',
|
|
345
|
+
value: where.join(' AND '),
|
|
346
|
+
} as RawSqlExpression);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Build sort expression
|
|
350
|
+
const sortExpression = this.getSortExpression(columnsInMainCTE);
|
|
351
|
+
if (sortExpression) {
|
|
352
|
+
selectBuilder.addOrderby([{ type: 'sql_expr', value: sortExpression } as RawSqlExpression]);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Build pagination expression
|
|
356
|
+
const paginationExpression = this.getPaginationExpression();
|
|
357
|
+
if (paginationExpression) {
|
|
358
|
+
selectBuilder.addLimit({
|
|
359
|
+
type: 'sql_expr',
|
|
360
|
+
value: paginationExpression,
|
|
361
|
+
} as RawSqlExpression);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return {
|
|
365
|
+
type: ResolvedExpressionType.CTE,
|
|
366
|
+
cteName: 'cte_main',
|
|
367
|
+
value: selectBuilder.build(),
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Returns the filter expression for the selected columns. If column type is formula,
|
|
373
|
+
* stores the rules in pending filter config and process it in the next call to getFilterExpression
|
|
374
|
+
* else processes immediately.
|
|
375
|
+
*
|
|
376
|
+
* Note - Pass empty set to process any pending filter config
|
|
377
|
+
*/
|
|
378
|
+
private getFilterExpression(columns: Set<string>): string | undefined {
|
|
379
|
+
const { filterConfig, columnConfigMap, queryResolver, databaseDetails } = this.options;
|
|
380
|
+
if (!filterConfig) {
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// extract pending filter config
|
|
385
|
+
const filterConfigToProcess = this.buildState.pendingFilterConfig;
|
|
386
|
+
|
|
387
|
+
// Reset pending filter config
|
|
388
|
+
this.buildState.pendingFilterConfig = {
|
|
389
|
+
rules: [],
|
|
390
|
+
combinator: filterConfig.combinator,
|
|
391
|
+
not: filterConfig.not,
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
if (columns.size) {
|
|
395
|
+
const nativeColumnRules: (Rule | RuleGroup)[] = [];
|
|
396
|
+
const formulaColumnRules: (Rule | RuleGroup)[] = [];
|
|
397
|
+
// Get rules for selected columns from original filter config
|
|
398
|
+
filterConfig.rules.forEach((rule) => {
|
|
399
|
+
const columnName = (rule as Rule).column ?? (rule as RuleGroup).rules[0].column;
|
|
400
|
+
if (columns.has(columnName)) {
|
|
401
|
+
if (columnConfigMap[columnName].columnType === MDM_COLUMN_TYPE.FORMULA) {
|
|
402
|
+
formulaColumnRules.push(rule);
|
|
403
|
+
|
|
404
|
+
const schema = this.options.schema ?? [];
|
|
405
|
+
const exists = schema.some((col) => col.name === columnName);
|
|
406
|
+
if (!exists) {
|
|
407
|
+
schema.push({
|
|
408
|
+
name: columnName,
|
|
409
|
+
type: MAX_STRING_TYPE,
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
} else {
|
|
413
|
+
nativeColumnRules.push(rule);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
// if column type is formula, store the rules in pending filter config else process immediately
|
|
419
|
+
if (formulaColumnRules.length) {
|
|
420
|
+
this.buildState.pendingFilterConfig.rules = formulaColumnRules;
|
|
421
|
+
}
|
|
422
|
+
if (nativeColumnRules.length) {
|
|
423
|
+
filterConfigToProcess.rules = filterConfigToProcess.rules.concat(nativeColumnRules);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if (filterConfigToProcess.rules.length) {
|
|
428
|
+
const resolvedFilterQuery = queryResolver.filterResolver({
|
|
429
|
+
filterConfig: filterConfigToProcess,
|
|
430
|
+
schema: this.options.schema,
|
|
431
|
+
});
|
|
432
|
+
return stripWhereClause(resolvedFilterQuery);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Returns the sort expression if each column in the sort config is part of selected columns
|
|
438
|
+
*/
|
|
439
|
+
private getSortExpression(columns: Set<string>): string | undefined {
|
|
440
|
+
const { sortConfig, joinClauses } = this.options;
|
|
441
|
+
const { alias } = joinClauses || {};
|
|
442
|
+
if (!sortConfig || sortConfig.length === 0) {
|
|
443
|
+
this.buildState.isSortPending = false;
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
if (this.buildState.isSortPending === false) {
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const selectedSortColumns = sortConfig.filter((s) => columns.has(s.column));
|
|
452
|
+
this.buildState.pendingSortConfig = (this.buildState.pendingSortConfig ?? []).concat(
|
|
453
|
+
selectedSortColumns,
|
|
454
|
+
);
|
|
455
|
+
|
|
456
|
+
const hasPendingFilters = this.buildState.pendingFilterConfig.rules.length > 0;
|
|
457
|
+
if (
|
|
458
|
+
this.buildState.pendingSortConfig.length === sortConfig.length &&
|
|
459
|
+
!hasPendingFilters &&
|
|
460
|
+
!this.buildState.isSearchPending &&
|
|
461
|
+
!this.buildState.isSuperFilterPending
|
|
462
|
+
) {
|
|
463
|
+
this.buildState.isSortPending = false;
|
|
464
|
+
return `${sortConfig.map((s) => `${alias && !this.buildState.isSuperFilterPending ? `${this.entityNameFormatter(alias)}.` : ''}${this.entityNameFormatter(s.column)} ${s.order}`).join(', ')}`;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Returns the pagination expression if the pagination config is set and sort is not pending.
|
|
470
|
+
* It must be called after getSortExpression.
|
|
471
|
+
*/
|
|
472
|
+
private getPaginationExpression(): string | undefined {
|
|
473
|
+
const { pagination } = this.options;
|
|
474
|
+
if (!pagination) {
|
|
475
|
+
this.buildState.isPaginationPending = false;
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// If pagination is already processed, return
|
|
480
|
+
if (this.buildState.isPaginationPending === false) {
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// If sort is not processed, throw error
|
|
485
|
+
if (this.buildState.pendingSortConfig === undefined) {
|
|
486
|
+
throw new Error('getSortExpression() must be called before getPaginationExpression()');
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// If sort is not complete, return
|
|
490
|
+
if (this.buildState.isSortPending || this.buildState.isSearchPending) {
|
|
491
|
+
this.buildState.isPaginationPending = true;
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// If pagination can be completed, return
|
|
496
|
+
this.buildState.isPaginationPending = false;
|
|
497
|
+
return this.paginationBuilder.generateQuery(pagination.skip, pagination.take);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* Builds the intermediate CTEs for the formula columns
|
|
502
|
+
* 1. Replace table name with previous cte name
|
|
503
|
+
* 2. Apply filter condition
|
|
504
|
+
* 3. Apply sort expression, if applicable
|
|
505
|
+
* 4. Apply pagination, if applicable
|
|
506
|
+
* 5. Return CTEs/Column expressions
|
|
507
|
+
*/
|
|
508
|
+
private buildIntermediateCTE(
|
|
509
|
+
previousCTEName: string,
|
|
510
|
+
formulaColumnsInCalcOrder: IMDMColumnConfigWithParsedMeta[][],
|
|
511
|
+
): CteExpression {
|
|
512
|
+
let parentCTEName = previousCTEName;
|
|
513
|
+
const tableNameRegex = new RegExp(`\\${RUNTIME_TABLE_NAME}`, 'g');
|
|
514
|
+
|
|
515
|
+
const cteParts: string[] = [];
|
|
516
|
+
formulaColumnsInCalcOrder.forEach((stageMembers) => {
|
|
517
|
+
const pendingColumns: IMDMColumnConfigWithParsedMeta[] = [];
|
|
518
|
+
|
|
519
|
+
stageMembers.forEach((column) => {
|
|
520
|
+
const {
|
|
521
|
+
columnName,
|
|
522
|
+
columnMeta: { sqlQueryProps },
|
|
523
|
+
} = column;
|
|
524
|
+
const isCteExpression =
|
|
525
|
+
sqlQueryProps?.resolvedExpression?.type === ResolvedExpressionType.CTE;
|
|
526
|
+
const intermediateParts = [];
|
|
527
|
+
|
|
528
|
+
if (isCteExpression) {
|
|
529
|
+
// add staging cte to process any pending filters, sort, pagination
|
|
530
|
+
const stagingCte = this.addStagingCTE(parentCTEName, pendingColumns);
|
|
531
|
+
if (stagingCte) {
|
|
532
|
+
pendingColumns.length = 0;
|
|
533
|
+
parentCTEName = stagingCte!.cteName;
|
|
534
|
+
intermediateParts.push(stagingCte.expr);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// process current column cte
|
|
538
|
+
const sqlParts = this.buildSqlQueryParts([columnName]);
|
|
539
|
+
const expr = sqlQueryProps.resolvedExpression as CteExpression;
|
|
540
|
+
let resolvedCte = expr.value.join(', ').replace(tableNameRegex, parentCTEName);
|
|
541
|
+
if (resolvedCte) {
|
|
542
|
+
resolvedCte = resolveRuntimeVariables(resolvedCte, this.options.runtimeVariables);
|
|
543
|
+
}
|
|
544
|
+
parentCTEName = expr.cteName;
|
|
545
|
+
intermediateParts.push(resolvedCte);
|
|
546
|
+
if (sqlParts.length) {
|
|
547
|
+
const cteName = `cte_post_${parentCTEName}`;
|
|
548
|
+
const postCte = `${cteName} AS (SELECT * FROM ${parentCTEName}${sqlParts.length ? ' ' + sqlParts.join(' ') : ''})`;
|
|
549
|
+
intermediateParts.push(postCte);
|
|
550
|
+
parentCTEName = cteName;
|
|
551
|
+
}
|
|
552
|
+
cteParts.push(intermediateParts.join(', '));
|
|
553
|
+
} else {
|
|
554
|
+
pendingColumns.push(column);
|
|
555
|
+
}
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
// add staging cte if there are pending columns at the end of stage.
|
|
559
|
+
if (pendingColumns.length) {
|
|
560
|
+
const stagingCte = this.addStagingCTE(parentCTEName, pendingColumns);
|
|
561
|
+
pendingColumns.length = 0;
|
|
562
|
+
if (stagingCte) {
|
|
563
|
+
cteParts.push(stagingCte.expr);
|
|
564
|
+
parentCTEName = stagingCte.cteName;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
return { type: ResolvedExpressionType.CTE, cteName: parentCTEName, value: cteParts };
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
private addStagingCTE(
|
|
573
|
+
parentCTEName: string,
|
|
574
|
+
columnConfigs: IMDMColumnConfigWithParsedMeta[],
|
|
575
|
+
): { cteName: string; expr: string } | undefined {
|
|
576
|
+
const pendingColumnsSet = new Set(columnConfigs.map((config) => config.columnName));
|
|
577
|
+
const sqlParts = this.buildSqlQueryParts(Array.from(pendingColumnsSet));
|
|
578
|
+
|
|
579
|
+
if (sqlParts.length !== 0 || columnConfigs.length !== 0) {
|
|
580
|
+
const cteName = `cte_staging_${parentCTEName}`;
|
|
581
|
+
const columnsToProject = [
|
|
582
|
+
'*',
|
|
583
|
+
...columnConfigs.map((config) => {
|
|
584
|
+
let resolvedExpressionValue =
|
|
585
|
+
config.columnMeta.sqlQueryProps?.resolvedExpression?.value?.join(', ');
|
|
586
|
+
if (resolvedExpressionValue) {
|
|
587
|
+
resolvedExpressionValue = resolveRuntimeVariables(
|
|
588
|
+
resolvedExpressionValue,
|
|
589
|
+
this.options.runtimeVariables,
|
|
590
|
+
);
|
|
591
|
+
}
|
|
592
|
+
return `${resolvedExpressionValue} AS ${this.entityNameFormatter(config.columnName)}`;
|
|
593
|
+
}),
|
|
594
|
+
].join(', ');
|
|
595
|
+
const stagingCte = `${cteName} AS (SELECT ${columnsToProject} FROM ${parentCTEName}${sqlParts.length ? ' ' + sqlParts.join(' ') : ''})`;
|
|
596
|
+
return { cteName, expr: stagingCte };
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
private buildFinalProjection(
|
|
601
|
+
mainCTE: MainCteExpression,
|
|
602
|
+
intermediateCTE: CteExpression,
|
|
603
|
+
isCountQuery: boolean = false,
|
|
604
|
+
): FinalQueryResult {
|
|
605
|
+
const {
|
|
606
|
+
distinct,
|
|
607
|
+
selectedColumns,
|
|
608
|
+
includeFinalCTESelectQuery = true,
|
|
609
|
+
finalResponseAsObject = false,
|
|
610
|
+
joinClauses,
|
|
611
|
+
enableLastSelectCTEWrap = false,
|
|
612
|
+
} = this.options;
|
|
613
|
+
const { joinRelation, alias } = joinClauses || {};
|
|
614
|
+
const mainTableAlias = alias ? `${this.entityNameFormatter(alias)}` : '';
|
|
615
|
+
|
|
616
|
+
// final query is select from main cte and intermediate cte if exists and isFilterPending is false
|
|
617
|
+
const finalSqlParts = this.buildState.isSuperFilterPending ? false : true;
|
|
618
|
+
const sqlParts = this.buildSqlQueryParts([], finalSqlParts);
|
|
619
|
+
|
|
620
|
+
if (
|
|
621
|
+
sqlParts.length === 0 &&
|
|
622
|
+
intermediateCTE.value.length === 0 &&
|
|
623
|
+
!this.buildState.isSuperFilterPending
|
|
624
|
+
) {
|
|
625
|
+
if (joinRelation?.length) {
|
|
626
|
+
mainCTE.value.from = this.applyJoinExpressionToMainCTE(mainCTE.value);
|
|
627
|
+
}
|
|
628
|
+
if (distinct) {
|
|
629
|
+
this.applyDistinctExpressionToMainCTE(mainCTE);
|
|
630
|
+
}
|
|
631
|
+
const query = isCountQuery
|
|
632
|
+
? this.applyCountExpressionToMainCTE(mainCTE)
|
|
633
|
+
: processSelect(mainCTE.value);
|
|
634
|
+
return {
|
|
635
|
+
finalQuery: query,
|
|
636
|
+
};
|
|
637
|
+
} else {
|
|
638
|
+
let currentCteName = mainCTE.cteName;
|
|
639
|
+
const cteParts = [`cte_main AS (${processSelect(mainCTE.value)})`];
|
|
640
|
+
if (intermediateCTE.value.length > 0) {
|
|
641
|
+
currentCteName = intermediateCTE.cteName;
|
|
642
|
+
cteParts.push(intermediateCTE.value.join(', '));
|
|
643
|
+
}
|
|
644
|
+
let tableName = currentCteName;
|
|
645
|
+
if (joinRelation?.length) {
|
|
646
|
+
const joinExpr = getJoinExpressions(
|
|
647
|
+
joinClauses as JoinExpressions,
|
|
648
|
+
this.options?.databaseDetails,
|
|
649
|
+
).join(' ');
|
|
650
|
+
sqlParts.splice(0, 0, joinExpr);
|
|
651
|
+
}
|
|
652
|
+
let column = '*';
|
|
653
|
+
if (distinct) {
|
|
654
|
+
tableName = `${tableName}${mainTableAlias ? ` AS ${mainTableAlias}` : ''}`;
|
|
655
|
+
|
|
656
|
+
column = `DISTINCT ${selectedColumns
|
|
657
|
+
.map((c) => {
|
|
658
|
+
const schemaMatch = this.options.schema?.find((s) => s.name === c);
|
|
659
|
+
return (
|
|
660
|
+
`${mainTableAlias ? `${mainTableAlias}.` : ''}` +
|
|
661
|
+
this.entityNameFormatter(c, schemaMatch?.type ?? '')
|
|
662
|
+
);
|
|
663
|
+
})
|
|
664
|
+
.join(', ')}`;
|
|
665
|
+
}
|
|
666
|
+
if (isCountQuery) {
|
|
667
|
+
if (distinct) {
|
|
668
|
+
const distinctCountCte = this.getDistinctCountCte(currentCteName, sqlParts);
|
|
669
|
+
sqlParts.length = 0;
|
|
670
|
+
tableName = distinctCountCte.cteName;
|
|
671
|
+
cteParts.push(distinctCountCte.value.join(', '));
|
|
672
|
+
column = `COUNT(*) AS row_count`;
|
|
673
|
+
} else {
|
|
674
|
+
if (joinRelation?.length) {
|
|
675
|
+
const primaryKeyColumns = this.options.primaryKeyColumns?.[0];
|
|
676
|
+
if (mainTableAlias && primaryKeyColumns) {
|
|
677
|
+
column = `DISTINCT ${mainTableAlias}.${this.entityNameFormatter(primaryKeyColumns)}`;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
column = `COUNT(${column}) AS row_count`;
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
if (!distinct) {
|
|
684
|
+
tableName = `${tableName}${mainTableAlias ? ` AS ${mainTableAlias}` : ''}`;
|
|
685
|
+
}
|
|
686
|
+
let selectedColumn =
|
|
687
|
+
column === '*' && mainTableAlias ? `DISTINCT ${mainTableAlias}.*` : column;
|
|
688
|
+
|
|
689
|
+
if (isCountQuery && this.buildState.isSuperFilterPending) {
|
|
690
|
+
selectedColumn = mainTableAlias && !distinct ? `DISTINCT ${mainTableAlias}.*` : '*';
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
const selectedProjection = `SELECT ${selectedColumn} FROM ${tableName}${sqlParts.length ? ' ' + sqlParts.join(' ') : ''}`;
|
|
694
|
+
|
|
695
|
+
let finalProjection = '';
|
|
696
|
+
if (this.buildState.isSuperFilterPending || enableLastSelectCTEWrap) {
|
|
697
|
+
const { cteDefinition, finalQuery, finalCteName } = this.buildFinalQueryComponents(
|
|
698
|
+
currentCteName,
|
|
699
|
+
selectedProjection,
|
|
700
|
+
isCountQuery,
|
|
701
|
+
distinct,
|
|
702
|
+
);
|
|
703
|
+
currentCteName = finalCteName;
|
|
704
|
+
cteParts.push(cteDefinition);
|
|
705
|
+
finalProjection = finalQuery;
|
|
706
|
+
} else {
|
|
707
|
+
finalProjection = selectedProjection;
|
|
708
|
+
}
|
|
709
|
+
const cteJoins = enableLastSelectCTEWrap
|
|
710
|
+
? cteParts.join(', ')
|
|
711
|
+
: `WITH ${cteParts.join(', ')}`;
|
|
712
|
+
let finalQuery = cteJoins;
|
|
713
|
+
if (includeFinalCTESelectQuery) {
|
|
714
|
+
finalQuery += ` ${finalProjection}`;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
if (finalResponseAsObject) {
|
|
718
|
+
return {
|
|
719
|
+
finalQuery: finalQuery,
|
|
720
|
+
finalCteName: currentCteName,
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
return {
|
|
725
|
+
finalQuery,
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
private applyDistinctExpressionToMainCTE(mainCTE: MainCteExpression) {
|
|
731
|
+
const { selectedColumns } = this.options;
|
|
732
|
+
if (mainCTE.value.columns.length > selectedColumns.length) {
|
|
733
|
+
mainCTE.value.columns = mainCTE.value.columns.filter((c) => {
|
|
734
|
+
return selectedColumns.includes(c.expr.meta?.rawColumnName);
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
mainCTE.value.distinct = 'DISTINCT';
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
private applyCountExpressionToMainCTE(mainCTE: MainCteExpression): string {
|
|
741
|
+
if (mainCTE.value.distinct) {
|
|
742
|
+
const mainCteResult = `WITH cte_main AS (${processSelect(mainCTE.value)})`;
|
|
743
|
+
return `${mainCteResult} SELECT COUNT(*) AS row_count FROM cte_main`;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
const columnValue =
|
|
747
|
+
mainCTE.value.columns.length > 1 ? '*' : `${mainCTE.value.columns[0].expr.value}`;
|
|
748
|
+
mainCTE.value.columns[0].expr.value = `COUNT(${columnValue})`;
|
|
749
|
+
mainCTE.value.columns[0].as = `row_count`;
|
|
750
|
+
mainCTE.value.columns.length = 1;
|
|
751
|
+
return processSelect(mainCTE.value);
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
private getDistinctCountCte(parentCTEName: string, sqlParts: string[]): CteExpression {
|
|
755
|
+
const { selectedColumns, joinClauses } = this.options;
|
|
756
|
+
const { alias } = joinClauses || {};
|
|
757
|
+
const cteName = `cte_distinct_${parentCTEName}`;
|
|
758
|
+
const fromName = `${parentCTEName}${alias ? ` AS ${this.entityNameFormatter(alias)}` : ''}`;
|
|
759
|
+
return {
|
|
760
|
+
type: ResolvedExpressionType.CTE,
|
|
761
|
+
cteName,
|
|
762
|
+
value: [
|
|
763
|
+
`${cteName} AS (SELECT DISTINCT ${selectedColumns.map((c) => `${alias ? `${this.entityNameFormatter(alias)}.` : ''}` + this.entityNameFormatter(c)).join(', ')} FROM ${fromName}${sqlParts.length ? ' ' + sqlParts.join(' ') : ''})`,
|
|
764
|
+
],
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
private buildSqlQueryParts(columns: string[], isFinalProjection = false): string[] {
|
|
769
|
+
const sqlParts = [];
|
|
770
|
+
|
|
771
|
+
const where = [];
|
|
772
|
+
const filterCondition = this.getFilterExpression(new Set(columns));
|
|
773
|
+
if (filterCondition) {
|
|
774
|
+
where.push(filterCondition);
|
|
775
|
+
}
|
|
776
|
+
if (columns.length === 0 && this.buildState.isSuperFilterPending) {
|
|
777
|
+
const superFilterCondition = this.getSuperFilterExpression(isFinalProjection);
|
|
778
|
+
if (superFilterCondition) {
|
|
779
|
+
where.push(superFilterCondition);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
if (columns.length === 0 && this.buildState.isSearchPending) {
|
|
783
|
+
this.buildState.isSearchPending = false;
|
|
784
|
+
where.push(this.options.searchExpression);
|
|
785
|
+
}
|
|
786
|
+
if (where.length) {
|
|
787
|
+
sqlParts.push(`WHERE ${where.join(' AND ')}`);
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
const sortExpression = this.getSortExpression(new Set(columns));
|
|
791
|
+
if (sortExpression) {
|
|
792
|
+
sqlParts.push(`ORDER BY ${sortExpression}`);
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
const paginationExpression = this.getPaginationExpression();
|
|
796
|
+
if (paginationExpression) {
|
|
797
|
+
sqlParts.push(paginationExpression);
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
return sqlParts;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
/**
|
|
804
|
+
* Returns the calculation order for the given columns
|
|
805
|
+
* @private exposed for testing
|
|
806
|
+
*/
|
|
807
|
+
public getCalculationOrder(columns: Set<string>): CalculationOrderResult {
|
|
808
|
+
const state = {
|
|
809
|
+
dependencyLevel: -1,
|
|
810
|
+
dependencyLevelMap: new Map<string, number>(),
|
|
811
|
+
nativeColumns: new Set<string>(),
|
|
812
|
+
visited: new Set<string>(),
|
|
813
|
+
};
|
|
814
|
+
|
|
815
|
+
// trigger dependency tracing for each column
|
|
816
|
+
columns.forEach((columnName) => {
|
|
817
|
+
state.dependencyLevel = -1; // reset dependency level for each column
|
|
818
|
+
this.dfs(columnName, state);
|
|
819
|
+
});
|
|
820
|
+
|
|
821
|
+
// build output
|
|
822
|
+
const formulaColumnsByDependencyLevel: IMDMColumnConfigWithParsedMeta[][] = [];
|
|
823
|
+
for (const [columnName, level] of state.dependencyLevelMap) {
|
|
824
|
+
const columnConfig = this.options.columnConfigMap[columnName];
|
|
825
|
+
if (level >= formulaColumnsByDependencyLevel.length) {
|
|
826
|
+
formulaColumnsByDependencyLevel.push([]);
|
|
827
|
+
}
|
|
828
|
+
formulaColumnsByDependencyLevel[level].push(columnConfig);
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
return {
|
|
832
|
+
nativeColumns: state.nativeColumns,
|
|
833
|
+
formulaColumnsByDependencyLevel,
|
|
834
|
+
};
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
private dfs(
|
|
838
|
+
columnName: string,
|
|
839
|
+
state: {
|
|
840
|
+
dependencyLevel: number;
|
|
841
|
+
dependencyLevelMap: Map<string, number>;
|
|
842
|
+
nativeColumns: Set<string>;
|
|
843
|
+
visited: Set<string>;
|
|
844
|
+
},
|
|
845
|
+
) {
|
|
846
|
+
// If dependency level is already resolved, return
|
|
847
|
+
const level = state.dependencyLevelMap.get(columnName);
|
|
848
|
+
if (level !== undefined) {
|
|
849
|
+
if (level > state.dependencyLevel) {
|
|
850
|
+
state.dependencyLevel = level;
|
|
851
|
+
}
|
|
852
|
+
return;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
if (state.visited.has(columnName)) {
|
|
856
|
+
throw new Error(`Cycle detected in formula dependencies: ${columnName}`);
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
const columnConfig = this.options.columnConfigMap[columnName];
|
|
860
|
+
const { columnType, columnMeta } = columnConfig;
|
|
861
|
+
const { sqlQueryProps, isCustomColumn, deps, defaultValueType } = columnMeta;
|
|
862
|
+
|
|
863
|
+
const isNativeColumn =
|
|
864
|
+
columnType !== MDM_COLUMN_TYPE.FORMULA && defaultValueType !== DEFAULT_VALUE_TYPE.DERIVED;
|
|
865
|
+
const isFormulaDbColumn =
|
|
866
|
+
columnType === MDM_COLUMN_TYPE.FORMULA && !sqlQueryProps && !isCustomColumn && !deps;
|
|
867
|
+
const isDerivedDbColumn =
|
|
868
|
+
defaultValueType === DEFAULT_VALUE_TYPE.DERIVED && !sqlQueryProps && !deps;
|
|
869
|
+
|
|
870
|
+
if (isNativeColumn || isFormulaDbColumn || isDerivedDbColumn) {
|
|
871
|
+
state.nativeColumns.add(columnName);
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
// Skip processing to maintain backward compatibility with older formula visual columns
|
|
876
|
+
if (columnType === MDM_COLUMN_TYPE.FORMULA && isCustomColumn && !sqlQueryProps) {
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
state.visited.add(columnName);
|
|
881
|
+
deps?.forEach((colName) => {
|
|
882
|
+
this.dfs(colName, state);
|
|
883
|
+
});
|
|
884
|
+
|
|
885
|
+
state.visited.delete(columnName);
|
|
886
|
+
state.dependencyLevelMap.set(columnName, ++state.dependencyLevel);
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
/**
|
|
890
|
+
* Applies the join positions from the query builder to the given Select AST.
|
|
891
|
+
* The join positions are added to the FROM list of the AST.
|
|
892
|
+
*
|
|
893
|
+
* @param {Select} ast - The Select AST to apply the join positions to.
|
|
894
|
+
* @returns {string[]} - An empty array, for consistency with other methods.
|
|
895
|
+
*/
|
|
896
|
+
private applyJoinExpressionToMainCTE(ast: Select): From[] {
|
|
897
|
+
const { joinClauses, databaseDetails } = this.options;
|
|
898
|
+
const joinExpressions = getJoinExpressions(joinClauses as JoinExpressions, databaseDetails);
|
|
899
|
+
const joins: From[] = joinExpressions.map((j) => {
|
|
900
|
+
const raw: RawSqlExpression = { type: 'sql_expr', value: j };
|
|
901
|
+
return raw as unknown as From;
|
|
902
|
+
});
|
|
903
|
+
if (joins.length) {
|
|
904
|
+
ast.distinct = 'DISTINCT';
|
|
905
|
+
}
|
|
906
|
+
const joinFroms = ast.from as From[];
|
|
907
|
+
// set the FROM list as base table + joins
|
|
908
|
+
return joinFroms?.concat(joins);
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
/**
|
|
912
|
+
* Gets the super filter expression.
|
|
913
|
+
* If super filters are present and the pending filter config is not empty,
|
|
914
|
+
* it will process the super filter condition and return the result.
|
|
915
|
+
* If the super filter expression is empty, it will return an empty string.
|
|
916
|
+
* @returns {string} - The super filter expression.
|
|
917
|
+
*/
|
|
918
|
+
private getSuperFilterExpression(isFinalProjection?: boolean): string {
|
|
919
|
+
if (!this.options.superFilters || !this.options.superFilters.children.length) return '';
|
|
920
|
+
const { superFilters, manualFilterOptions } = this.options;
|
|
921
|
+
|
|
922
|
+
const { children } = superFilters;
|
|
923
|
+
const { rankingFilters, nonRankingFilters } = children.reduce(
|
|
924
|
+
(acc, child) => {
|
|
925
|
+
if ('groupId' in child && 'children' in child) {
|
|
926
|
+
acc.nonRankingFilters.push(child);
|
|
927
|
+
} else if ('filters' in child) {
|
|
928
|
+
const filterType = (child as { filters: IGroupFilter }).filters.filterType;
|
|
929
|
+
if (filterType === 'RANKING') {
|
|
930
|
+
acc.rankingFilters.push(child);
|
|
931
|
+
} else {
|
|
932
|
+
acc.nonRankingFilters.push(child);
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
return acc;
|
|
936
|
+
},
|
|
937
|
+
{
|
|
938
|
+
rankingFilters: [] as typeof children,
|
|
939
|
+
nonRankingFilters: [] as typeof children,
|
|
940
|
+
},
|
|
941
|
+
);
|
|
942
|
+
|
|
943
|
+
let activeFilters = { ...superFilters, children: nonRankingFilters };
|
|
944
|
+
let joinClauses = this.options.joinClauses;
|
|
945
|
+
|
|
946
|
+
// Process ranking filters if pending and available
|
|
947
|
+
if (rankingFilters.length && isFinalProjection && this.buildState.isSuperFilterPending) {
|
|
948
|
+
activeFilters = { ...superFilters, children: rankingFilters };
|
|
949
|
+
joinClauses = { alias: '', joinRelation: [] };
|
|
950
|
+
}
|
|
951
|
+
if (rankingFilters.length === 0) {
|
|
952
|
+
this.buildState.isSuperFilterPending = false;
|
|
953
|
+
}
|
|
954
|
+
if (activeFilters.children.length) {
|
|
955
|
+
const sqlQueryBuilder = new SuperFilterBuilder(
|
|
956
|
+
activeFilters,
|
|
957
|
+
this.options.databaseDetails,
|
|
958
|
+
this.options.skipCountForRankingFilter,
|
|
959
|
+
joinClauses,
|
|
960
|
+
manualFilterOptions,
|
|
961
|
+
);
|
|
962
|
+
return sqlQueryBuilder.build();
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
return '';
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
private buildFinalQueryComponents(
|
|
969
|
+
currentCteName: string,
|
|
970
|
+
selectProjection: string,
|
|
971
|
+
isCountQuery: boolean,
|
|
972
|
+
distinct: boolean,
|
|
973
|
+
): { cteDefinition: string; finalQuery: string; finalCteName: string } {
|
|
974
|
+
const { selectedColumns, enableLastSelectCTEWrap } = this.options;
|
|
975
|
+
let selectQuery = '*';
|
|
976
|
+
if (distinct && !isCountQuery) {
|
|
977
|
+
selectQuery = `DISTINCT ${selectedColumns
|
|
978
|
+
.map((c) => {
|
|
979
|
+
const schemaMatch = this.options.schema?.find((s) => s.name === c);
|
|
980
|
+
return this.entityNameFormatter(c, schemaMatch?.type ?? '');
|
|
981
|
+
})
|
|
982
|
+
.join(', ')}`;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
let finalCteName = `${currentCteName}_main_cte`;
|
|
986
|
+
let cteDefinition = `${finalCteName} AS (${selectProjection})`;
|
|
987
|
+
|
|
988
|
+
const columns = isCountQuery ? `COUNT(${selectQuery}) AS row_count` : `${selectQuery}`;
|
|
989
|
+
const sqlParts = this.buildSqlQueryParts([], true);
|
|
990
|
+
|
|
991
|
+
const finalQuery = `SELECT ${columns} FROM ${finalCteName}${sqlParts.length ? ' ' + sqlParts.join(' ') : ''}`;
|
|
992
|
+
|
|
993
|
+
if (enableLastSelectCTEWrap && this.buildState.isSuperFilterPending) {
|
|
994
|
+
finalCteName = `${finalCteName}_main_cte`;
|
|
995
|
+
cteDefinition = ` ${cteDefinition},${finalCteName} AS (${finalQuery})`;
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
this.buildState.isSuperFilterPending = false;
|
|
999
|
+
|
|
1000
|
+
return { cteDefinition, finalQuery, finalCteName };
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
private filterRequiredColumns(): Set<string> {
|
|
1004
|
+
const {
|
|
1005
|
+
selectedColumns,
|
|
1006
|
+
superFilters,
|
|
1007
|
+
primaryKeyColumns,
|
|
1008
|
+
sortConfig = [],
|
|
1009
|
+
joinClauses,
|
|
1010
|
+
} = this.options;
|
|
1011
|
+
const requiredColumn = new Set<string>(selectedColumns);
|
|
1012
|
+
if (superFilters?.children?.length) {
|
|
1013
|
+
// Collect all columnIds from superFilters
|
|
1014
|
+
(superFilters?.children as { filters: IGroupFilter }[])?.forEach((child) => {
|
|
1015
|
+
const filterColumn = child.filters?.columnIds?.[0];
|
|
1016
|
+
if (filterColumn) requiredColumn.add(filterColumn);
|
|
1017
|
+
child?.filters?.rankingFilter?.forEach?.((rank) => {
|
|
1018
|
+
if (rank?.rankByColumnName) requiredColumn.add(rank.rankByColumnName);
|
|
1019
|
+
});
|
|
1020
|
+
});
|
|
1021
|
+
primaryKeyColumns.forEach((col) => {
|
|
1022
|
+
requiredColumn.add(col);
|
|
1023
|
+
});
|
|
1024
|
+
sortConfig.forEach((col) => {
|
|
1025
|
+
requiredColumn.add(col?.column);
|
|
1026
|
+
});
|
|
1027
|
+
}
|
|
1028
|
+
// Add all columnIds from joinRelation
|
|
1029
|
+
for (const { id } of joinClauses?.joinRelation ?? []) {
|
|
1030
|
+
if (id != null) requiredColumn.add(id);
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
return requiredColumn;
|
|
1034
|
+
}
|
|
1035
|
+
}
|