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,105 @@
|
|
|
1
|
+
import { EpmQueryBuilderOptions } from '@epm-query-builder/types/query-builder-types';
|
|
2
|
+
import { BaseUtilities } from '@epm-query-builder/base/BaseUtilities';
|
|
3
|
+
import { BaseSuperFilterBuilder } from '@epm-query-builder/base/BaseSuperFilterBuilder';
|
|
4
|
+
import { QueryGenerationError } from '@epm-query-builder/errors/QueryBuilderErrors';
|
|
5
|
+
|
|
6
|
+
// chooses count strategy based on presence of group by or measures
|
|
7
|
+
export class BaseCountQueryBuilder {
|
|
8
|
+
constructor(protected options: EpmQueryBuilderOptions) {}
|
|
9
|
+
|
|
10
|
+
// branches to grouped count when aggregation is involved
|
|
11
|
+
generateCountQuery(mainQuery: string): string {
|
|
12
|
+
try {
|
|
13
|
+
if (this.hasGroupBy() || BaseUtilities.hasMeasuresInOptions(this.options)) {
|
|
14
|
+
return this.generateGroupedCountQuery(mainQuery);
|
|
15
|
+
}
|
|
16
|
+
return this.generateSimpleCountQuery();
|
|
17
|
+
} catch (error) {
|
|
18
|
+
throw new QueryGenerationError(`Failed to generate count query: ${error}`, {
|
|
19
|
+
hasGroupBy: this.hasGroupBy(),
|
|
20
|
+
hasMeasures: BaseUtilities.hasMeasuresInOptions(this.options),
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// counts distinct dims when dimensions exist, else total rows
|
|
26
|
+
private generateSimpleCountQuery(): string {
|
|
27
|
+
const dimensions = BaseUtilities.extractDimensionsFromOptions(this.options);
|
|
28
|
+
const fromClause = this.buildFromClause();
|
|
29
|
+
const whereClause = this.buildWhereClause();
|
|
30
|
+
|
|
31
|
+
if (dimensions.length === 0) {
|
|
32
|
+
return `
|
|
33
|
+
SELECT COUNT(*) as total_count
|
|
34
|
+
FROM ${fromClause}
|
|
35
|
+
${whereClause}
|
|
36
|
+
`.trim();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return `
|
|
40
|
+
SELECT COUNT(DISTINCT ${dimensions.join(', ')}) as distinct_count
|
|
41
|
+
FROM ${fromClause}
|
|
42
|
+
${whereClause}
|
|
43
|
+
`.trim();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// wraps main query to count rows after aggregation
|
|
47
|
+
private generateGroupedCountQuery(mainQuery: string): string {
|
|
48
|
+
return `
|
|
49
|
+
SELECT COUNT(*) as total_count
|
|
50
|
+
FROM (
|
|
51
|
+
${mainQuery}
|
|
52
|
+
) as main_result
|
|
53
|
+
`.trim();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// beware: naive left join on id across tables; change keys if schema differs
|
|
57
|
+
protected buildFromClause(): string {
|
|
58
|
+
const tables = new Set<string>();
|
|
59
|
+
|
|
60
|
+
[
|
|
61
|
+
...(this.options.rows || []),
|
|
62
|
+
...(this.options.columns || []),
|
|
63
|
+
...(this.options.values || []),
|
|
64
|
+
].forEach((item) => {
|
|
65
|
+
if (item.tableName) {
|
|
66
|
+
tables.add(item.tableName);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
if (tables.size === 0) {
|
|
71
|
+
throw new QueryGenerationError('No tables found for count query');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (tables.size === 1) {
|
|
75
|
+
return Array.from(tables)[0];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const tableArray = Array.from(tables);
|
|
79
|
+
const baseTable = tableArray[0];
|
|
80
|
+
let fromClause = baseTable;
|
|
81
|
+
|
|
82
|
+
for (let i = 1; i < tableArray.length; i++) {
|
|
83
|
+
fromClause += ` LEFT JOIN ${tableArray[i]} ON ${baseTable}.id = ${tableArray[i]}.id`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return fromClause;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
protected buildWhereClause(): string {
|
|
90
|
+
if (!this.options.superFilters) return '';
|
|
91
|
+
const superFilterBuilder = new BaseSuperFilterBuilder(this.options);
|
|
92
|
+
const where = superFilterBuilder.buildWhereClause();
|
|
93
|
+
return where ? `WHERE ${where}` : '';
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
protected hasGroupBy(): boolean {
|
|
97
|
+
const hasRows = (this.options.rows?.length || 0) > 0;
|
|
98
|
+
const hasColumns = (this.options.columns?.length || 0) > 0;
|
|
99
|
+
return hasRows || hasColumns;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
protected hasMeasures(): boolean {
|
|
103
|
+
return (this.options.values?.length || 0) > 0;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EpmQueryBuilderOptions,
|
|
3
|
+
MeasureConfig,
|
|
4
|
+
} from '@epm-query-builder/types/query-builder-types';
|
|
5
|
+
import { BaseUtilities } from '@epm-query-builder/base/BaseUtilities';
|
|
6
|
+
|
|
7
|
+
export abstract class BaseMeasureBuilder {
|
|
8
|
+
protected options: EpmQueryBuilderOptions;
|
|
9
|
+
|
|
10
|
+
constructor(options: EpmQueryBuilderOptions) {
|
|
11
|
+
this.options = options;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
protected buildStandardAggregation(measure: MeasureConfig): string {
|
|
15
|
+
// If no aggregation type, use the measure ID directly without prefix
|
|
16
|
+
const hasAggregation = measure.aggregationType && measure.aggregationType.trim() !== '';
|
|
17
|
+
if (!hasAggregation) {
|
|
18
|
+
return measure.id;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const original = (measure.aggregationType || 'SUM').toUpperCase();
|
|
22
|
+
const aliasPrefix =
|
|
23
|
+
original === 'FIRST'
|
|
24
|
+
? 'FIRST'
|
|
25
|
+
: original === 'LAST'
|
|
26
|
+
? 'LAST'
|
|
27
|
+
: BaseUtilities.mapAggregationType(original);
|
|
28
|
+
return `${aliasPrefix}__${measure.id}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
protected buildCalculatedMeasure(measure: MeasureConfig): string {
|
|
32
|
+
return `"${measure.id}"`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
protected buildConditionalMeasure(measure: MeasureConfig): string {
|
|
36
|
+
// If no aggregation type, use the measure ID directly without prefix
|
|
37
|
+
const hasAggregation = measure.aggregationType && measure.aggregationType.trim() !== '';
|
|
38
|
+
if (!hasAggregation) {
|
|
39
|
+
return measure.id;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const original = (measure.aggregationType || 'SUM').toUpperCase();
|
|
43
|
+
const aliasPrefix =
|
|
44
|
+
original === 'FIRST'
|
|
45
|
+
? 'FIRST'
|
|
46
|
+
: original === 'LAST'
|
|
47
|
+
? 'LAST'
|
|
48
|
+
: BaseUtilities.mapAggregationType(original);
|
|
49
|
+
return `${aliasPrefix}__${measure.id}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
protected isMeasureType(measure: MeasureConfig): boolean {
|
|
53
|
+
return measure.type?.toLowerCase() === 'measure';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
protected formatMeasureAlias(measure: MeasureConfig): string {
|
|
57
|
+
if (this.isMeasureType(measure)) {
|
|
58
|
+
return `${measure.tableName}[${measure.columnName}]`;
|
|
59
|
+
}
|
|
60
|
+
// If no aggregation type, use column name without prefix
|
|
61
|
+
const hasAggregation = measure.aggregationType && measure.aggregationType.trim() !== '';
|
|
62
|
+
if (!hasAggregation) {
|
|
63
|
+
return `${measure.tableName}[${measure.columnName}]`;
|
|
64
|
+
}
|
|
65
|
+
return `${measure.aggregationType}__${measure.tableName}[${measure.columnName}]`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
protected getMeasures(): MeasureConfig[] {
|
|
69
|
+
return (this.options.values || []) as MeasureConfig[];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
protected buildMeasureExpressions(): string[] {
|
|
73
|
+
const measures = this.getMeasures();
|
|
74
|
+
|
|
75
|
+
return measures.map((measure) => {
|
|
76
|
+
if (this.isMeasureType(measure)) {
|
|
77
|
+
return this.buildCalculatedMeasure(measure);
|
|
78
|
+
}
|
|
79
|
+
return this.buildStandardAggregation(measure);
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
protected buildMeasureSelectClause(): string {
|
|
84
|
+
const expressions = this.buildMeasureExpressions();
|
|
85
|
+
return expressions.join(', ');
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EpmQueryBuilderOptions,
|
|
3
|
+
OrderByConfig,
|
|
4
|
+
RowColumnConfig,
|
|
5
|
+
} from '@epm-query-builder/types/query-builder-types';
|
|
6
|
+
import { BaseUtilities } from '@epm-query-builder/base/BaseUtilities';
|
|
7
|
+
|
|
8
|
+
// orchestrates order by strategy: rollup, custom, or defaults
|
|
9
|
+
export abstract class BaseOrderBuilder {
|
|
10
|
+
protected options: EpmQueryBuilderOptions;
|
|
11
|
+
|
|
12
|
+
constructor(options: EpmQueryBuilderOptions) {
|
|
13
|
+
this.options = options;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
buildOrderByClause(): string {
|
|
17
|
+
const orderByClauses = this.buildOrderByClauses();
|
|
18
|
+
|
|
19
|
+
if (orderByClauses.length === 0) {
|
|
20
|
+
return '';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return `ORDER BY ${orderByClauses.join(', ')}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// combines rollup, custom, and default clauses
|
|
27
|
+
protected buildOrderByClauses(): string[] {
|
|
28
|
+
const clauses: string[] = [];
|
|
29
|
+
|
|
30
|
+
if (this.hasRollupOrdering()) {
|
|
31
|
+
clauses.push(...this.buildRollupOrderClauses());
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (this.options.orderBy?.length) {
|
|
35
|
+
clauses.push(...this.buildCustomOrderClauses());
|
|
36
|
+
} else {
|
|
37
|
+
clauses.push(...this.buildDefaultOrderClauses());
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return clauses;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// uses explicit orderBy configs
|
|
44
|
+
protected buildCustomOrderClauses(): string[] {
|
|
45
|
+
if (!this.options.orderBy?.length) {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return this.options.orderBy.map((orderBy) => {
|
|
50
|
+
const column = this.resolveOrderByColumn(orderBy);
|
|
51
|
+
return `${column} ${orderBy.type}`;
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// falls back to ordering by requested dimensions
|
|
56
|
+
protected buildDefaultOrderClauses(): string[] {
|
|
57
|
+
const clauses: string[] = [];
|
|
58
|
+
|
|
59
|
+
if (this.options.rows?.length) {
|
|
60
|
+
this.options.rows.forEach((row) => {
|
|
61
|
+
const column = this.formatOrderByColumn(row);
|
|
62
|
+
clauses.push(`${column} ASC`);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (this.options.columns?.length) {
|
|
67
|
+
this.options.columns.forEach((col) => {
|
|
68
|
+
const column = this.formatOrderByColumn(col);
|
|
69
|
+
clauses.push(`${column} ASC`);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return clauses;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// adds rollup-based ordering when rollup is generated
|
|
77
|
+
protected buildRollupOrderClauses(): string[] {
|
|
78
|
+
const clauses: string[] = [];
|
|
79
|
+
|
|
80
|
+
if (this.options.rows?.length && !this.options.skipRollup) {
|
|
81
|
+
clauses.push('"IsRowGrandTotal" DESC');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (this.options.columns?.length && !this.options.skipRollup) {
|
|
85
|
+
clauses.push('"IsColumnGrandTotal" DESC');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return clauses;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
protected resolveOrderByColumn(orderBy: OrderByConfig): string {
|
|
92
|
+
if (orderBy.isColumnPresentInOutput) {
|
|
93
|
+
if (orderBy.aggregationType) {
|
|
94
|
+
return `"${orderBy.aggregationType}__${orderBy.tableName}[${orderBy.columnName}]"`;
|
|
95
|
+
}
|
|
96
|
+
return `"${orderBy.tableName}[${orderBy.columnName}]"`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const tableName = orderBy.tableName || '';
|
|
100
|
+
return `${tableName}.${orderBy.columnName}`;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
protected formatOrderByColumn(config: RowColumnConfig): string {
|
|
104
|
+
if (config.orderByRef) {
|
|
105
|
+
const tableName = config.tableName || '';
|
|
106
|
+
return `${tableName}.${config.orderByRef}`;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const tableName = config.tableName || '';
|
|
110
|
+
return `${tableName}.${config.columnName}`;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
protected handleColumnPresentInOutput(orderBy: OrderByConfig): boolean {
|
|
114
|
+
return Boolean(orderBy.isColumnPresentInOutput);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ensures correct direction and reference when column is already in output
|
|
118
|
+
protected buildOrderByWithOutput(orderBy: OrderByConfig): string {
|
|
119
|
+
if (!this.handleColumnPresentInOutput(orderBy)) {
|
|
120
|
+
return '';
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (orderBy.aggregationType) {
|
|
124
|
+
const columnRef = `"${orderBy.aggregationType}__${orderBy.tableName}[${orderBy.columnName}]"`;
|
|
125
|
+
return `${columnRef} ${this.mapSortDirection(orderBy.type)}`;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const tableName = orderBy.tableName || '';
|
|
129
|
+
const columnRef = `${tableName}.${orderBy.columnName}`;
|
|
130
|
+
const direction = this.mapSortDirection(orderBy.type);
|
|
131
|
+
|
|
132
|
+
return `${columnRef} ${direction}`;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
protected applyExpansionConfig(config: RowColumnConfig): string[] {
|
|
136
|
+
const expansions: string[] = [];
|
|
137
|
+
|
|
138
|
+
if (!config.expansionConfig) {
|
|
139
|
+
return expansions;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const expansion = config.expansionConfig;
|
|
143
|
+
const baseColumn = `${config.tableName}.${config.columnName}`;
|
|
144
|
+
|
|
145
|
+
switch (expansion.expansionType?.toLowerCase()) {
|
|
146
|
+
case 'hierarchy':
|
|
147
|
+
if (expansion.hierarchies?.length) {
|
|
148
|
+
expansion.hierarchies.forEach((hierarchy) => {
|
|
149
|
+
expansions.push(`${baseColumn}_${hierarchy}`);
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
break;
|
|
153
|
+
case 'date_parts':
|
|
154
|
+
if (expansion.items?.length) {
|
|
155
|
+
expansion.items.forEach((part) => {
|
|
156
|
+
expansions.push(
|
|
157
|
+
`EXTRACT(${part.toUpperCase()} FROM ${baseColumn}) AS ${config.columnName}_${part}`,
|
|
158
|
+
);
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
break;
|
|
162
|
+
case 'range':
|
|
163
|
+
if (expansion.start && expansion.end && expansion.step) {
|
|
164
|
+
const steps = Math.ceil(
|
|
165
|
+
(Number(expansion.end) - Number(expansion.start)) / expansion.step,
|
|
166
|
+
);
|
|
167
|
+
for (let i = 0; i <= steps; i++) {
|
|
168
|
+
const value = Number(expansion.start) + i * expansion.step;
|
|
169
|
+
expansions.push(`${value} AS ${config.columnName}_${i}`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
break;
|
|
173
|
+
default:
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return expansions;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
protected mapSortDirection(direction?: string): 'ASC' | 'DESC' {
|
|
181
|
+
return direction?.toUpperCase() === 'DESC' ? 'DESC' : 'ASC';
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
protected hasRollupOrdering(): boolean {
|
|
185
|
+
return BaseUtilities.shouldGenerateRollupForOptions(this.options);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
protected hasDimensions(): boolean {
|
|
189
|
+
return BaseUtilities.hasDimensionsInOptions(this.options);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
protected hasCustomOrderBy(): boolean {
|
|
193
|
+
return Boolean(this.options.orderBy?.length);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { EpmQueryBuilderOptions } from '@epm-query-builder/types/query-builder-types';
|
|
2
|
+
|
|
3
|
+
// pagination helpers: build limit/offset fragments and metadata
|
|
4
|
+
export abstract class BasePaginationBuilder {
|
|
5
|
+
protected options: EpmQueryBuilderOptions;
|
|
6
|
+
|
|
7
|
+
constructor(options: EpmQueryBuilderOptions) {
|
|
8
|
+
this.options = options;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// builds dialect-agnostic limit clause
|
|
12
|
+
protected buildLimitClause(): string {
|
|
13
|
+
const pagination = this.options.pagination;
|
|
14
|
+
const limit = pagination?.limit || pagination?.take;
|
|
15
|
+
if (!pagination || !limit || limit <= 0) {
|
|
16
|
+
return '';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return `LIMIT ${limit}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// builds offset clause when offset provided
|
|
23
|
+
protected buildOffsetClause(offset = 0): string {
|
|
24
|
+
if (offset <= 0) {
|
|
25
|
+
return '';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return `OFFSET ${offset}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// composes limit and offset when both exist
|
|
32
|
+
protected buildLimitOffsetClause(offset = 0): string {
|
|
33
|
+
const limitClause = this.buildLimitClause();
|
|
34
|
+
const offsetClause = this.buildOffsetClause(offset);
|
|
35
|
+
|
|
36
|
+
const clauses = [limitClause, offsetClause].filter((clause) => clause);
|
|
37
|
+
return clauses.join(' ');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// derives count selection from a base query for pagination metadata
|
|
41
|
+
protected buildCountQuery(baseQuery: string): string {
|
|
42
|
+
const fromIndex = baseQuery.toLowerCase().indexOf('from');
|
|
43
|
+
if (fromIndex === -1) {
|
|
44
|
+
return '';
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const fromClause = baseQuery.substring(fromIndex);
|
|
48
|
+
const orderByIndex = fromClause.toLowerCase().lastIndexOf('order by');
|
|
49
|
+
const limitIndex = fromClause.toLowerCase().lastIndexOf('limit');
|
|
50
|
+
|
|
51
|
+
let cleanFromClause = fromClause;
|
|
52
|
+
if (orderByIndex !== -1) {
|
|
53
|
+
cleanFromClause = fromClause.substring(0, orderByIndex).trim();
|
|
54
|
+
}
|
|
55
|
+
if (limitIndex !== -1 && (orderByIndex === -1 || limitIndex < orderByIndex)) {
|
|
56
|
+
cleanFromClause = fromClause.substring(0, limitIndex).trim();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return `SELECT COUNT(*) as total ${cleanFromClause}`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
protected hasPagination(): boolean {
|
|
63
|
+
return Boolean(
|
|
64
|
+
this.options.pagination && (this.options.pagination.limit || this.options.pagination.take),
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
protected getPaginationLimit(): number {
|
|
69
|
+
return this.options.pagination?.limit || this.options.pagination?.take || 100;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// computes basic pagination metadata for consumers
|
|
73
|
+
protected buildPaginationMetadata(
|
|
74
|
+
totalCount: number,
|
|
75
|
+
limit: number,
|
|
76
|
+
offset = 0,
|
|
77
|
+
): Record<string, unknown> {
|
|
78
|
+
const currentPage = Math.floor(offset / limit) + 1;
|
|
79
|
+
const totalPages = Math.ceil(totalCount / limit);
|
|
80
|
+
const hasNextPage = currentPage < totalPages;
|
|
81
|
+
const hasPreviousPage = currentPage > 1;
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
totalCount,
|
|
85
|
+
limit,
|
|
86
|
+
offset,
|
|
87
|
+
currentPage,
|
|
88
|
+
totalPages,
|
|
89
|
+
hasNextPage,
|
|
90
|
+
hasPreviousPage,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// base class: common helpers for dialect query builders
|
|
2
|
+
import {
|
|
3
|
+
EpmQueryBuilderOptions,
|
|
4
|
+
OrderByConfig,
|
|
5
|
+
} from '@epm-query-builder/types/query-builder-types';
|
|
6
|
+
import { BaseUtilities } from '@epm-query-builder/base/BaseUtilities';
|
|
7
|
+
import { BaseSqlBuilder } from '@epm-query-builder/base/BaseSqlBuilder';
|
|
8
|
+
|
|
9
|
+
export abstract class BaseQueryBuilder extends BaseSqlBuilder {
|
|
10
|
+
constructor(options: EpmQueryBuilderOptions) {
|
|
11
|
+
super(options);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
abstract generateQuery(): string;
|
|
15
|
+
|
|
16
|
+
protected shouldGenerateAnalyticalQuery(): boolean {
|
|
17
|
+
return BaseUtilities.shouldGenerateAnalyticalQueryForOptions(this.options);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
protected shouldGenerateRollup(): boolean {
|
|
21
|
+
return BaseUtilities.shouldGenerateRollupForOptions(this.options);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
protected hasComplexFilters(): boolean {
|
|
25
|
+
return Boolean(this.options.superFilters?.children?.length);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
protected buildBasicGroupByClause(): string {
|
|
29
|
+
return BaseUtilities.buildBasicGroupByClauseForOptions(this.options);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
protected mapSortDirection(direction?: string): 'ASC' | 'DESC' {
|
|
33
|
+
return direction?.toUpperCase() === 'DESC' ? 'DESC' : 'ASC';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
protected resolveOrderByColumn(orderBy: OrderByConfig): string {
|
|
37
|
+
if (orderBy.isColumnPresentInOutput) {
|
|
38
|
+
if (orderBy.aggregationType) {
|
|
39
|
+
return `"${orderBy.aggregationType}__${orderBy.tableName}[${orderBy.columnName}]"`;
|
|
40
|
+
}
|
|
41
|
+
return `"${orderBy.tableName}[${orderBy.columnName}]"`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const tableName = orderBy.tableName || '';
|
|
45
|
+
return `${tableName}.${orderBy.columnName}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
protected buildJoinClause(joinType: string, table: string, condition: string): string {
|
|
49
|
+
return `${joinType} ${table} ON ${condition}`;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { EpmQueryBuilderOptions } from '@epm-query-builder/types/query-builder-types';
|
|
2
|
+
import { BaseUtilities } from '@epm-query-builder/base/BaseUtilities';
|
|
3
|
+
|
|
4
|
+
// computes grouping sets and rollup indicators for rollup queries
|
|
5
|
+
export abstract class BaseRollupBuilder {
|
|
6
|
+
protected options: EpmQueryBuilderOptions;
|
|
7
|
+
|
|
8
|
+
constructor(options: EpmQueryBuilderOptions) {
|
|
9
|
+
this.options = options;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
protected shouldGenerateRollup(): boolean {
|
|
13
|
+
return BaseUtilities.shouldGenerateRollupForOptions(this.options);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
protected hasDimensions(): boolean {
|
|
17
|
+
return BaseUtilities.hasDimensionsInOptions(this.options);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// extracts dimension references for rollup
|
|
21
|
+
protected getDimensions(): string[] {
|
|
22
|
+
const rowDimensions = BaseUtilities.buildDimensionReference(this.options.rows, this.options);
|
|
23
|
+
const columnDimensions = BaseUtilities.buildDimensionReference(
|
|
24
|
+
this.options.columns,
|
|
25
|
+
this.options,
|
|
26
|
+
);
|
|
27
|
+
return [...rowDimensions, ...columnDimensions];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// builds grouping sets for subtotal/grand total computation
|
|
31
|
+
// generates hierarchical rollup combinations for rows and columns separately
|
|
32
|
+
// avoiding cross-product combinations that create unwanted blank cells
|
|
33
|
+
protected buildGroupingSets(): string {
|
|
34
|
+
const rowDimensions = BaseUtilities.buildDimensionReference(this.options.rows, this.options);
|
|
35
|
+
const columnDimensions = BaseUtilities.buildDimensionReference(
|
|
36
|
+
this.options.columns,
|
|
37
|
+
this.options,
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const allDimensions = [...rowDimensions, ...columnDimensions];
|
|
41
|
+
if (allDimensions.length === 0) {
|
|
42
|
+
return '';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const groupingSets: string[] = [];
|
|
46
|
+
const seenCombinations = new Set<string>();
|
|
47
|
+
|
|
48
|
+
const addGroupingSet = (dims: string[]) => {
|
|
49
|
+
if (dims.length === 0) {
|
|
50
|
+
if (!seenCombinations.has('()')) {
|
|
51
|
+
groupingSets.push('()');
|
|
52
|
+
seenCombinations.add('()');
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
const key = dims.join(',');
|
|
56
|
+
if (!seenCombinations.has(key)) {
|
|
57
|
+
groupingSets.push(`(${dims.join(', ')})`);
|
|
58
|
+
seenCombinations.add(key);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
addGroupingSet(allDimensions);
|
|
64
|
+
|
|
65
|
+
for (let i = 0; i < rowDimensions.length; i++) {
|
|
66
|
+
addGroupingSet(rowDimensions.slice(0, i + 1));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
for (let i = 0; i < columnDimensions.length; i++) {
|
|
70
|
+
addGroupingSet(columnDimensions.slice(0, i + 1));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const dimPrefix: string[] = [];
|
|
74
|
+
for (const rowDim of rowDimensions) {
|
|
75
|
+
dimPrefix.push(rowDim);
|
|
76
|
+
const colPrefix: string[] = [];
|
|
77
|
+
for (const colDim of columnDimensions) {
|
|
78
|
+
colPrefix.push(colDim);
|
|
79
|
+
const combined = [...dimPrefix, ...colPrefix];
|
|
80
|
+
addGroupingSet(combined);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (rowDimensions.length > 0 || columnDimensions.length > 0) {
|
|
85
|
+
addGroupingSet([]);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return `GROUPING SETS (${groupingSets.join(', ')})`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// emits boolean flags to identify totals in output rows
|
|
92
|
+
protected generateRollupIndicators(): string[] {
|
|
93
|
+
const indicators: string[] = [];
|
|
94
|
+
const rowCount = this.options.rows?.length || 0;
|
|
95
|
+
const columnCount = this.options.columns?.length || 0;
|
|
96
|
+
|
|
97
|
+
if (rowCount > 0) {
|
|
98
|
+
indicators.push(
|
|
99
|
+
'CASE WHEN GROUPING_ID() & 1 = 1 THEN true ELSE false END AS "IsRowGrandTotal"',
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
for (let i = 1; i < rowCount; i++) {
|
|
103
|
+
const mask = Math.pow(2, i);
|
|
104
|
+
indicators.push(
|
|
105
|
+
`CASE WHEN GROUPING_ID() & ${mask} = ${mask} THEN true ELSE false END AS "IsDM${i - 1}Total"`,
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (columnCount > 0) {
|
|
111
|
+
const columnMask = Math.pow(2, rowCount);
|
|
112
|
+
indicators.push(
|
|
113
|
+
`CASE WHEN GROUPING_ID() & ${columnMask} = ${columnMask} THEN true ELSE false END AS "IsColumnGrandTotal"`,
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
for (let i = 1; i < columnCount; i++) {
|
|
117
|
+
const mask = Math.pow(2, rowCount + i);
|
|
118
|
+
indicators.push(
|
|
119
|
+
`CASE WHEN GROUPING_ID() & ${mask} = ${mask} THEN true ELSE false END AS "IsDM${rowCount + i - 1}Total"`,
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return indicators;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
protected buildBasicGroupByClause(): string {
|
|
128
|
+
return BaseUtilities.buildBasicGroupByClauseForOptions(this.options);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// standard combinations generator used for grouping sets
|
|
132
|
+
protected generateCombinations<T>(array: T[], size: number): T[][] {
|
|
133
|
+
if (size === 0) return [[]];
|
|
134
|
+
if (size > array.length) return [];
|
|
135
|
+
|
|
136
|
+
const combinations: T[][] = [];
|
|
137
|
+
|
|
138
|
+
for (let i = 0; i <= array.length - size; i++) {
|
|
139
|
+
const head = array[i];
|
|
140
|
+
const tailCombinations = this.generateCombinations(array.slice(i + 1), size - 1);
|
|
141
|
+
|
|
142
|
+
for (const tail of tailCombinations) {
|
|
143
|
+
combinations.push([head, ...tail]);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return combinations;
|
|
148
|
+
}
|
|
149
|
+
}
|