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.
Files changed (225) hide show
  1. package/.turbo/turbo-build.log +9 -0
  2. package/.turbo/turbo-check-types.log +4 -0
  3. package/.turbo/turbo-lint.log +126 -0
  4. package/README.md +45 -0
  5. package/coverage/base.css +224 -0
  6. package/coverage/block-navigation.js +87 -0
  7. package/coverage/favicon.png +0 -0
  8. package/coverage/index.html +236 -0
  9. package/coverage/prettify.css +1 -0
  10. package/coverage/prettify.js +2 -0
  11. package/coverage/sort-arrow-sprite.png +0 -0
  12. package/coverage/sorter.js +210 -0
  13. package/coverage/src/constants.ts.html +826 -0
  14. package/coverage/src/database_types.ts.html +136 -0
  15. package/coverage/src/epm-query-builder/EpmQueryBuilder.ts.html +166 -0
  16. package/coverage/src/epm-query-builder/base/BaseAdvancedAggregations.ts.html +568 -0
  17. package/coverage/src/epm-query-builder/base/BaseAnalyticalFunctions.ts.html +694 -0
  18. package/coverage/src/epm-query-builder/base/BaseCTEGenerator.ts.html +1459 -0
  19. package/coverage/src/epm-query-builder/base/BaseCountQueryBuilder.ts.html +400 -0
  20. package/coverage/src/epm-query-builder/base/BaseMeasureBuilder.ts.html +295 -0
  21. package/coverage/src/epm-query-builder/base/BaseOrderBuilder.ts.html +670 -0
  22. package/coverage/src/epm-query-builder/base/BasePaginationBuilder.ts.html +364 -0
  23. package/coverage/src/epm-query-builder/base/BaseQueryBuilder.ts.html +238 -0
  24. package/coverage/src/epm-query-builder/base/BaseRollupBuilder.ts.html +532 -0
  25. package/coverage/src/epm-query-builder/base/BaseSqlBuilder.ts.html +601 -0
  26. package/coverage/src/epm-query-builder/base/BaseSuperFilterBuilder.ts.html +1966 -0
  27. package/coverage/src/epm-query-builder/base/BaseUtilities.ts.html +1798 -0
  28. package/coverage/src/epm-query-builder/base/ColumnRefUtils.ts.html +211 -0
  29. package/coverage/src/epm-query-builder/base/RelationshipResolver.ts.html +706 -0
  30. package/coverage/src/epm-query-builder/base/SharedFilterBuilder.ts.html +1717 -0
  31. package/coverage/src/epm-query-builder/base/index.html +326 -0
  32. package/coverage/src/epm-query-builder/constants/Aggregations.ts.html +133 -0
  33. package/coverage/src/epm-query-builder/constants/Database.ts.html +103 -0
  34. package/coverage/src/epm-query-builder/constants/Source.ts.html +106 -0
  35. package/coverage/src/epm-query-builder/constants/index.html +146 -0
  36. package/coverage/src/epm-query-builder/dialects/duckdb/DuckDbAdvancedAggregations.ts.html +286 -0
  37. package/coverage/src/epm-query-builder/dialects/duckdb/DuckDbJoinBuilder.ts.html +280 -0
  38. package/coverage/src/epm-query-builder/dialects/duckdb/DuckDbMeasureBuilder.ts.html +1924 -0
  39. package/coverage/src/epm-query-builder/dialects/duckdb/DuckDbOrderBuilder.ts.html +769 -0
  40. package/coverage/src/epm-query-builder/dialects/duckdb/DuckDbPaginationBuilder.ts.html +643 -0
  41. package/coverage/src/epm-query-builder/dialects/duckdb/DuckDbQueryBuilder.ts.html +2644 -0
  42. package/coverage/src/epm-query-builder/dialects/duckdb/DuckDbRollupBuilder.ts.html +478 -0
  43. package/coverage/src/epm-query-builder/dialects/duckdb/DuckDbSuperFilterBuilder.ts.html +1195 -0
  44. package/coverage/src/epm-query-builder/dialects/duckdb/index.html +221 -0
  45. package/coverage/src/epm-query-builder/errors/QueryBuilderErrors.ts.html +280 -0
  46. package/coverage/src/epm-query-builder/errors/index.html +116 -0
  47. package/coverage/src/epm-query-builder/index.html +116 -0
  48. package/coverage/src/epm-query-builder/interfaces/IDatabaseQueryBuilder.ts.html +100 -0
  49. package/coverage/src/epm-query-builder/interfaces/index.html +131 -0
  50. package/coverage/src/epm-query-builder/interfaces/index.ts.html +88 -0
  51. package/coverage/src/epm-query-builder/utils/format.ts.html +151 -0
  52. package/coverage/src/epm-query-builder/utils/index.html +146 -0
  53. package/coverage/src/epm-query-builder/utils/sql.ts.html +247 -0
  54. package/coverage/src/epm-query-builder/utils/validation.ts.html +124 -0
  55. package/coverage/src/epm-query-builder/validation/QueryOptionsValidator.ts.html +631 -0
  56. package/coverage/src/epm-query-builder/validation/SqlQueryValidator.ts.html +475 -0
  57. package/coverage/src/epm-query-builder/validation/index.html +131 -0
  58. package/coverage/src/filters/FilterConditionBuilder.ts.html +427 -0
  59. package/coverage/src/filters/filter-types.ts.html +484 -0
  60. package/coverage/src/filters/index.html +131 -0
  61. package/coverage/src/index.html +176 -0
  62. package/coverage/src/index.ts.html +103 -0
  63. package/coverage/src/js-lib/JsToSqlParser.ts.html +736 -0
  64. package/coverage/src/js-lib/ParseContext.ts.html +532 -0
  65. package/coverage/src/js-lib/db/azuresql/AzureSqlCallExpressionVisitor.ts.html +196 -0
  66. package/coverage/src/js-lib/db/azuresql/index.html +116 -0
  67. package/coverage/src/js-lib/db/base/ArrayExpressionVisitor.ts.html +133 -0
  68. package/coverage/src/js-lib/db/base/AssignmentExpressionVisitor.ts.html +187 -0
  69. package/coverage/src/js-lib/db/base/BinaryExpressionVisitor.ts.html +223 -0
  70. package/coverage/src/js-lib/db/base/CallExpressionVisitor.ts.html +5479 -0
  71. package/coverage/src/js-lib/db/base/IdentifierVisitor.ts.html +283 -0
  72. package/coverage/src/js-lib/db/base/LiteralVisitor.ts.html +199 -0
  73. package/coverage/src/js-lib/db/base/MemberExpressionVisitor.ts.html +193 -0
  74. package/coverage/src/js-lib/db/base/ProgramVisitor.ts.html +139 -0
  75. package/coverage/src/js-lib/db/base/UnaryExpressionVisitor.ts.html +181 -0
  76. package/coverage/src/js-lib/db/base/VisitorInterface.ts.html +103 -0
  77. package/coverage/src/js-lib/db/base/index.html +251 -0
  78. package/coverage/src/js-lib/db/bigquery/BigQueryCallExpressionVisitor.ts.html +1747 -0
  79. package/coverage/src/js-lib/db/bigquery/index.html +116 -0
  80. package/coverage/src/js-lib/db/commonTransforms.ts.html +2074 -0
  81. package/coverage/src/js-lib/db/databricks/DatabricksCallExpressionVisitor.ts.html +1303 -0
  82. package/coverage/src/js-lib/db/databricks/index.html +116 -0
  83. package/coverage/src/js-lib/db/fabricsql/FabricSqlCallExpressionVisitor.ts.html +196 -0
  84. package/coverage/src/js-lib/db/fabricsql/index.html +116 -0
  85. package/coverage/src/js-lib/db/fabricwarehouse/FabricWarehouseCallExpressionVisitor.ts.html +292 -0
  86. package/coverage/src/js-lib/db/fabricwarehouse/index.html +116 -0
  87. package/coverage/src/js-lib/db/index.html +116 -0
  88. package/coverage/src/js-lib/db/postgresql/PostgreSqlCallExpressionVisitor.ts.html +985 -0
  89. package/coverage/src/js-lib/db/postgresql/index.html +116 -0
  90. package/coverage/src/js-lib/db/redshift/RedshiftCallExpressionVisitor.ts.html +685 -0
  91. package/coverage/src/js-lib/db/redshift/index.html +116 -0
  92. package/coverage/src/js-lib/db/sample/SampleCallExpressionVisitor.ts.html +196 -0
  93. package/coverage/src/js-lib/db/sample/index.html +116 -0
  94. package/coverage/src/js-lib/db/snowflake/SnowflakeCallExpressionVisitor.ts.html +1447 -0
  95. package/coverage/src/js-lib/db/snowflake/index.html +116 -0
  96. package/coverage/src/js-lib/db/validator/FormulaValidator.ts.html +4162 -0
  97. package/coverage/src/js-lib/db/validator/index.html +116 -0
  98. package/coverage/src/js-lib/index.html +131 -0
  99. package/coverage/src/js-lib/objects/BaseObject.ts.html +169 -0
  100. package/coverage/src/js-lib/objects/DateObject.ts.html +169 -0
  101. package/coverage/src/js-lib/objects/PctObject.ts.html +178 -0
  102. package/coverage/src/js-lib/objects/index.html +146 -0
  103. package/coverage/src/query-builder/PaginationBuilder.ts.html +142 -0
  104. package/coverage/src/query-builder/QueryBuilder.ts.html +3118 -0
  105. package/coverage/src/query-builder/SuperFilterBuilder.ts.html +1969 -0
  106. package/coverage/src/query-builder/index.html +146 -0
  107. package/coverage/src/runtime_var.ts.html +109 -0
  108. package/coverage/src/sql-lib/binary_expr.ts.html +133 -0
  109. package/coverage/src/sql-lib/case.ts.html +133 -0
  110. package/coverage/src/sql-lib/column.ts.html +139 -0
  111. package/coverage/src/sql-lib/else.ts.html +124 -0
  112. package/coverage/src/sql-lib/function.ts.html +112 -0
  113. package/coverage/src/sql-lib/index.html +251 -0
  114. package/coverage/src/sql-lib/join.ts.html +127 -0
  115. package/coverage/src/sql-lib/literal.ts.html +130 -0
  116. package/coverage/src/sql-lib/select.ts.html +547 -0
  117. package/coverage/src/sql-lib/unary_expr.ts.html +112 -0
  118. package/coverage/src/sql-lib/when.ts.html +130 -0
  119. package/coverage/src/sql_query_gen.ts.html +535 -0
  120. package/coverage/src/superFilter/DateFilterFactory.ts.html +625 -0
  121. package/coverage/src/superFilter/dateFunction.ts.html +193 -0
  122. package/coverage/src/superFilter/index.html +131 -0
  123. package/coverage/src/utils.ts.html +571 -0
  124. package/dist/index.cjs +8440 -0
  125. package/dist/index.d.cts +927 -0
  126. package/dist/index.d.cts.map +1 -0
  127. package/dist/index.d.ts +927 -0
  128. package/dist/index.d.ts.map +1 -0
  129. package/dist/index.js +8387 -0
  130. package/dist/index.js.map +1 -0
  131. package/eslint.config.js +4 -0
  132. package/jest.config.ts +44 -0
  133. package/package.json +45 -0
  134. package/src/constants.ts +247 -0
  135. package/src/epm-query-builder/EpmQueryBuilder.ts +27 -0
  136. package/src/epm-query-builder/base/BaseAdvancedAggregations.ts +161 -0
  137. package/src/epm-query-builder/base/BaseAnalyticalFunctions.ts +203 -0
  138. package/src/epm-query-builder/base/BaseCTEGenerator.ts +458 -0
  139. package/src/epm-query-builder/base/BaseCountQueryBuilder.ts +105 -0
  140. package/src/epm-query-builder/base/BaseMeasureBuilder.ts +87 -0
  141. package/src/epm-query-builder/base/BaseOrderBuilder.ts +195 -0
  142. package/src/epm-query-builder/base/BasePaginationBuilder.ts +93 -0
  143. package/src/epm-query-builder/base/BaseQueryBuilder.ts +51 -0
  144. package/src/epm-query-builder/base/BaseRollupBuilder.ts +149 -0
  145. package/src/epm-query-builder/base/BaseSqlBuilder.ts +172 -0
  146. package/src/epm-query-builder/base/BaseSuperFilterBuilder.ts +627 -0
  147. package/src/epm-query-builder/base/BaseUtilities.ts +571 -0
  148. package/src/epm-query-builder/base/ColumnRefUtils.ts +42 -0
  149. package/src/epm-query-builder/base/RelationshipResolver.ts +207 -0
  150. package/src/epm-query-builder/base/SharedFilterBuilder.ts +544 -0
  151. package/src/epm-query-builder/constants/Aggregations.ts +16 -0
  152. package/src/epm-query-builder/constants/Database.ts +6 -0
  153. package/src/epm-query-builder/constants/Source.ts +7 -0
  154. package/src/epm-query-builder/dialects/duckdb/DuckDbAdvancedAggregations.ts +67 -0
  155. package/src/epm-query-builder/dialects/duckdb/DuckDbJoinBuilder.ts +65 -0
  156. package/src/epm-query-builder/dialects/duckdb/DuckDbMeasureBuilder.ts +626 -0
  157. package/src/epm-query-builder/dialects/duckdb/DuckDbOrderBuilder.ts +228 -0
  158. package/src/epm-query-builder/dialects/duckdb/DuckDbPaginationBuilder.ts +186 -0
  159. package/src/epm-query-builder/dialects/duckdb/DuckDbQueryBuilder.ts +853 -0
  160. package/src/epm-query-builder/dialects/duckdb/DuckDbRollupBuilder.ts +131 -0
  161. package/src/epm-query-builder/dialects/duckdb/DuckDbSuperFilterBuilder.ts +370 -0
  162. package/src/epm-query-builder/errors/QueryBuilderErrors.ts +65 -0
  163. package/src/epm-query-builder/interfaces/IDatabaseQueryBuilder.ts +5 -0
  164. package/src/epm-query-builder/interfaces/index.ts +1 -0
  165. package/src/epm-query-builder/types/query-builder-types.d.ts +289 -0
  166. package/src/epm-query-builder/utils/format.ts +22 -0
  167. package/src/epm-query-builder/utils/sql.ts +54 -0
  168. package/src/epm-query-builder/utils/validation.ts +13 -0
  169. package/src/epm-query-builder/validation/QueryOptionsValidator.ts +182 -0
  170. package/src/epm-query-builder/validation/SqlQueryValidator.ts +130 -0
  171. package/src/filters/FilterConditionBuilder.ts +114 -0
  172. package/src/filters/filter-types.ts +133 -0
  173. package/src/index.ts +10 -0
  174. package/src/js-lib/JsToSqlParser.ts +217 -0
  175. package/src/js-lib/ParseContext.ts +149 -0
  176. package/src/js-lib/db/base/ArrayExpressionVisitor.ts +16 -0
  177. package/src/js-lib/db/base/AssignmentExpressionVisitor.ts +34 -0
  178. package/src/js-lib/db/base/BinaryExpressionVisitor.ts +46 -0
  179. package/src/js-lib/db/base/CallExpressionVisitor.ts +1798 -0
  180. package/src/js-lib/db/base/IdentifierVisitor.ts +66 -0
  181. package/src/js-lib/db/base/LiteralVisitor.ts +38 -0
  182. package/src/js-lib/db/base/MemberExpressionVisitor.ts +36 -0
  183. package/src/js-lib/db/base/ProgramVisitor.ts +18 -0
  184. package/src/js-lib/db/base/UnaryExpressionVisitor.ts +32 -0
  185. package/src/js-lib/db/base/VisitorInterface.ts +6 -0
  186. package/src/js-lib/db/validator/FormulaValidator.ts +1235 -0
  187. package/src/js-lib/objects/BaseObject.ts +28 -0
  188. package/src/js-lib/objects/DateObject.ts +28 -0
  189. package/src/js-lib/objects/PctObject.ts +31 -0
  190. package/src/query-builder/PaginationBuilder.ts +19 -0
  191. package/src/query-builder/QueryBuilder.ts +1035 -0
  192. package/src/query-builder/SuperFilterBuilder.ts +628 -0
  193. package/src/runtime_var.ts +8 -0
  194. package/src/sql-lib/binary_expr.ts +16 -0
  195. package/src/sql-lib/case.ts +16 -0
  196. package/src/sql-lib/column.ts +18 -0
  197. package/src/sql-lib/else.ts +13 -0
  198. package/src/sql-lib/function.ts +9 -0
  199. package/src/sql-lib/join.ts +14 -0
  200. package/src/sql-lib/literal.ts +15 -0
  201. package/src/sql-lib/select.ts +154 -0
  202. package/src/sql-lib/unary_expr.ts +9 -0
  203. package/src/sql-lib/when.ts +15 -0
  204. package/src/sql-types.d.ts +565 -0
  205. package/src/sql_query_gen.ts +150 -0
  206. package/src/superFilter/DateFilterFactory.ts +180 -0
  207. package/src/superFilter/dateFunction.ts +36 -0
  208. package/src/utils.ts +354 -0
  209. package/test-output/report/junit.xml +329 -0
  210. package/tests/JsToSqlParser.test.ts +163 -0
  211. package/tests/QueryBuilder.test.ts +1320 -0
  212. package/tests/js-lib/CallExpressionVisitor.test.ts +820 -0
  213. package/tests/mocks/MockQueryResolver.ts +14 -0
  214. package/tests/sanity.test.ts +146 -0
  215. package/tests/sql-lib/binary_expr.test.ts +75 -0
  216. package/tests/sql-lib/case.test.ts +117 -0
  217. package/tests/sql-lib/column.test.ts +87 -0
  218. package/tests/sql-lib/else.test.ts +56 -0
  219. package/tests/sql-lib/function.test.ts +96 -0
  220. package/tests/sql-lib/literal.test.ts +75 -0
  221. package/tests/sql-lib/select.test.ts +245 -0
  222. package/tests/sql-lib/unary_expr.test.ts +32 -0
  223. package/tests/utils.test.ts +13 -0
  224. package/tsconfig.json +24 -0
  225. package/tsdown.config.ts +23 -0
@@ -0,0 +1,203 @@
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 class BaseAnalyticalFunctions {
8
+ protected options: EpmQueryBuilderOptions;
9
+
10
+ constructor(options: EpmQueryBuilderOptions) {
11
+ this.options = options;
12
+ }
13
+
14
+ buildWindowFunction(measure: MeasureConfig, windowSpec?: string): string {
15
+ const baseAggregation = this.buildBasicAggregation(measure);
16
+ const partitionBy = this.extractPartitionColumns();
17
+ const orderBy = this.extractOrderColumns();
18
+
19
+ let windowClause = 'OVER (';
20
+
21
+ if (partitionBy.length > 0) {
22
+ windowClause += `PARTITION BY ${partitionBy.join(', ')} `;
23
+ }
24
+
25
+ if (orderBy.length > 0) {
26
+ windowClause += `ORDER BY ${orderBy.join(', ')} `;
27
+ }
28
+
29
+ if (windowSpec) {
30
+ windowClause += windowSpec;
31
+ }
32
+
33
+ windowClause += ')';
34
+
35
+ return `${baseAggregation} ${windowClause}`;
36
+ }
37
+
38
+ buildRankingFunction(
39
+ measure: MeasureConfig,
40
+ rankType: 'ROW_NUMBER' | 'RANK' | 'DENSE_RANK' | 'PERCENT_RANK' = 'ROW_NUMBER',
41
+ ): string {
42
+ const orderBy = this.extractOrderColumns();
43
+ const partitionBy = this.extractPartitionColumns();
44
+
45
+ let windowClause = 'OVER (';
46
+
47
+ if (partitionBy.length > 0) {
48
+ windowClause += `PARTITION BY ${partitionBy.join(', ')} `;
49
+ }
50
+
51
+ if (orderBy.length > 0) {
52
+ windowClause += `ORDER BY ${orderBy.join(', ')}`;
53
+ }
54
+
55
+ windowClause += ')';
56
+
57
+ return `${rankType}() ${windowClause}`;
58
+ }
59
+
60
+ buildLagLeadFunction(
61
+ measure: MeasureConfig,
62
+ functionType: 'LAG' | 'LEAD',
63
+ offset = 1,
64
+ defaultValue?: string,
65
+ ): string {
66
+ const columnRef = BaseUtilities.buildColumnReference(
67
+ measure.tableName,
68
+ measure.columnName || measure.id,
69
+ );
70
+ const partitionBy = this.extractPartitionColumns();
71
+ const orderBy = this.extractOrderColumns();
72
+
73
+ let windowClause = 'OVER (';
74
+
75
+ if (partitionBy.length > 0) {
76
+ windowClause += `PARTITION BY ${partitionBy.join(', ')} `;
77
+ }
78
+
79
+ if (orderBy.length > 0) {
80
+ windowClause += `ORDER BY ${orderBy.join(', ')}`;
81
+ }
82
+
83
+ windowClause += ')';
84
+
85
+ const params = [columnRef, offset.toString()];
86
+ if (defaultValue !== undefined) {
87
+ params.push(defaultValue);
88
+ }
89
+
90
+ return `${functionType}(${params.join(', ')}) ${windowClause}`;
91
+ }
92
+
93
+ buildPercentileFunction(measure: MeasureConfig, percentile: number, continuous = true): string {
94
+ const columnRef = BaseUtilities.buildColumnReference(
95
+ measure.tableName,
96
+ measure.columnName || measure.id,
97
+ );
98
+ const functionName = continuous ? 'PERCENTILE_CONT' : 'PERCENTILE_DISC';
99
+
100
+ return `${functionName}(${percentile}) WITHIN GROUP (ORDER BY ${columnRef})`;
101
+ }
102
+
103
+ buildNtileFunction(measure: MeasureConfig, buckets: number): string {
104
+ const partitionBy = this.extractPartitionColumns();
105
+ const orderBy = this.extractOrderColumns();
106
+
107
+ let windowClause = 'OVER (';
108
+
109
+ if (partitionBy.length > 0) {
110
+ windowClause += `PARTITION BY ${partitionBy.join(', ')} `;
111
+ }
112
+
113
+ if (orderBy.length > 0) {
114
+ windowClause += `ORDER BY ${orderBy.join(', ')}`;
115
+ }
116
+
117
+ windowClause += ')';
118
+
119
+ return `NTILE(${buckets}) ${windowClause}`;
120
+ }
121
+
122
+ buildMovingAggregation(
123
+ measure: MeasureConfig,
124
+ aggregationType: string,
125
+ windowSize: number,
126
+ ): string {
127
+ const columnRef = BaseUtilities.buildColumnReference(
128
+ measure.tableName,
129
+ measure.columnName || measure.id,
130
+ );
131
+ const partitionBy = this.extractPartitionColumns();
132
+ const orderBy = this.extractOrderColumns();
133
+
134
+ let windowClause = 'OVER (';
135
+
136
+ if (partitionBy.length > 0) {
137
+ windowClause += `PARTITION BY ${partitionBy.join(', ')} `;
138
+ }
139
+
140
+ if (orderBy.length > 0) {
141
+ windowClause += `ORDER BY ${orderBy.join(', ')} `;
142
+ }
143
+
144
+ windowClause += `ROWS ${windowSize} PRECEDING)`;
145
+
146
+ return `${aggregationType.toUpperCase()}(${columnRef}) ${windowClause}`;
147
+ }
148
+
149
+ buildCumulativeAggregation(measure: MeasureConfig, aggregationType: string): string {
150
+ const columnRef = BaseUtilities.buildColumnReference(
151
+ measure.tableName,
152
+ measure.columnName || measure.id,
153
+ );
154
+ const partitionBy = this.extractPartitionColumns();
155
+ const orderBy = this.extractOrderColumns();
156
+
157
+ let windowClause = 'OVER (';
158
+
159
+ if (partitionBy.length > 0) {
160
+ windowClause += `PARTITION BY ${partitionBy.join(', ')} `;
161
+ }
162
+
163
+ if (orderBy.length > 0) {
164
+ windowClause += `ORDER BY ${orderBy.join(', ')} `;
165
+ }
166
+
167
+ windowClause += 'ROWS UNBOUNDED PRECEDING)';
168
+
169
+ return `${aggregationType.toUpperCase()}(${columnRef}) ${windowClause}`;
170
+ }
171
+
172
+ protected buildBasicAggregation(measure: MeasureConfig): string {
173
+ const columnRef = BaseUtilities.buildColumnReference(
174
+ measure.tableName,
175
+ measure.columnName || measure.id,
176
+ );
177
+ const aggregationType = measure.aggregationType || 'SUM';
178
+
179
+ return `${aggregationType.toUpperCase()}(${columnRef})`;
180
+ }
181
+
182
+ protected extractPartitionColumns(): string[] {
183
+ const rows = this.options.rows || [];
184
+ return rows.map((row) =>
185
+ BaseUtilities.buildColumnReference(row.tableName, row.columnName || row.id),
186
+ );
187
+ }
188
+
189
+ protected extractOrderColumns(): string[] {
190
+ if (!this.options.orderBy?.length) {
191
+ return [];
192
+ }
193
+
194
+ return this.options.orderBy.map((order) => {
195
+ const direction = order.type === 'DESC' ? 'DESC' : 'ASC';
196
+ const column = BaseUtilities.buildColumnReference(
197
+ order.tableName,
198
+ order.columnName || order.id,
199
+ );
200
+ return `${column} ${direction}`;
201
+ });
202
+ }
203
+ }
@@ -0,0 +1,458 @@
1
+ import {
2
+ EpmQueryBuilderOptions,
3
+ FilterConfig,
4
+ RankingConfig,
5
+ } from '@epm-query-builder/types/query-builder-types';
6
+ import { BaseUtilities } from '@epm-query-builder/base/BaseUtilities';
7
+ import { SharedFilterBuilder } from '@epm-query-builder/base/SharedFilterBuilder';
8
+ import { mapAggregationType as mapAgg } from '@epm-query-builder/constants/Aggregations';
9
+
10
+ export abstract class BaseCTEGenerator {
11
+ protected options: EpmQueryBuilderOptions;
12
+
13
+ constructor(options: EpmQueryBuilderOptions) {
14
+ this.options = options;
15
+ }
16
+
17
+ protected generateCTE(name: string, query: string): string {
18
+ return BaseUtilities.buildCTETemplate(name, query);
19
+ }
20
+
21
+ protected buildFilterCTE(filter: FilterConfig, counter: number): string {
22
+ const variableName = `filter_${counter}`;
23
+ const specificCTE = this.buildSpecificFilterCTE(filter, variableName);
24
+
25
+ if (!specificCTE) return '';
26
+
27
+ return this.generateCTE(variableName, specificCTE);
28
+ }
29
+
30
+ protected buildBasicFilterCTE(filter: FilterConfig, _variableName?: string): string {
31
+ void _variableName;
32
+ if (!filter.columnNames?.length || !filter.tableNames?.length) {
33
+ return '';
34
+ }
35
+
36
+ const column = filter.columnNames[0];
37
+ const tableName = filter.tableNames[0];
38
+ const condition = SharedFilterBuilder.buildFilterCondition(filter);
39
+
40
+ if (!condition) return '';
41
+
42
+ return BaseUtilities.buildDistinctSelect([column], tableName, condition);
43
+ }
44
+
45
+ protected buildTupleFilterCTE(filter: FilterConfig, _variableName: string): string {
46
+ void _variableName;
47
+ if (!filter.columnNames?.length || filter.columnNames.length < 2) {
48
+ return this.buildBasicFilterCTE(filter);
49
+ }
50
+
51
+ const tableName = filter.tableNames?.[0] || '';
52
+ const columns = filter.columnNames;
53
+
54
+ if (!filter.valuesFilter?.length) return '';
55
+
56
+ const tupleConditions = filter.valuesFilter
57
+ .map((vf) => {
58
+ const ids = Array.isArray(vf.id) ? vf.id : [vf.id];
59
+ if (ids.length !== columns.length) return null;
60
+
61
+ const columnValuePairs = columns
62
+ .map((col, idx) => `${col} = ${BaseUtilities.formatValue(ids[idx].toString())}`)
63
+ .join(' AND ');
64
+
65
+ return `(${columnValuePairs})`;
66
+ })
67
+ .filter((condition): condition is string => Boolean(condition));
68
+
69
+ if (!tupleConditions.length) return '';
70
+
71
+ const tupleClause = BaseUtilities.combineConditions(tupleConditions, 'OR');
72
+ const isExclude = filter.valuesFilterApplicationType?.toUpperCase() === 'EXCLUDE';
73
+ const finalCondition = isExclude ? `NOT (${tupleClause})` : `(${tupleClause})`;
74
+
75
+ return BaseUtilities.buildDistinctSelect(columns, tableName, finalCondition);
76
+ }
77
+
78
+ protected buildComplexFilterCTE(filter: FilterConfig, variableName: string): string {
79
+ const filterType = filter.filterType?.toUpperCase();
80
+
81
+ if (filterType === 'TUPLE') {
82
+ return this.buildTupleFilterCTE(filter, variableName);
83
+ }
84
+
85
+ const condition = SharedFilterBuilder.buildFilterCondition(filter);
86
+ if (!condition) return '';
87
+
88
+ const column = filter.columnNames?.[0];
89
+ const tableName = filter.tableNames?.[0] || '';
90
+ if (!column) return '';
91
+ return BaseUtilities.buildDistinctSelect([column], tableName, condition);
92
+ }
93
+
94
+ generateFilterCTEs(): string {
95
+ if (!this.options.superFilters?.children) {
96
+ return '';
97
+ }
98
+
99
+ const filterCTEs: string[] = [];
100
+ let variableCounter = 1;
101
+
102
+ this.options.superFilters.children.forEach((child) => {
103
+ const filter = child.filters;
104
+ if (!filter) return;
105
+
106
+ if (this.isComplexFilter(filter)) {
107
+ const cte = this.buildFilterCTE(filter, variableCounter++);
108
+ if (cte) {
109
+ filterCTEs.push(cte);
110
+ }
111
+ }
112
+ });
113
+
114
+ return filterCTEs.length > 0 ? `WITH ${filterCTEs.join(',\n')}` : '';
115
+ }
116
+
117
+ protected buildRankingFilterCTE(filter: FilterConfig, _variableName: string): string {
118
+ void _variableName;
119
+ const rankingConfigs =
120
+ filter.rankingFilter || (filter.rankingConfig ? [filter.rankingConfig] : []);
121
+
122
+ if (!rankingConfigs.length || !filter.columnNames?.length) {
123
+ return '';
124
+ }
125
+
126
+ const column = filter.columnNames[0];
127
+ const tableName = filter.tableNames?.[0] || '';
128
+
129
+ if (rankingConfigs.length === 1) {
130
+ return this.buildSingleRankingCTE(filter, rankingConfigs[0], column, tableName);
131
+ }
132
+
133
+ const cteQueries = rankingConfigs
134
+ .map((rankingConfig) => this.buildSingleRankingCTE(filter, rankingConfig, column, tableName))
135
+ .filter((query) => query);
136
+
137
+ if (!cteQueries.length) return '';
138
+
139
+ const operator = filter.logicalOperator?.toUpperCase() === 'OR' ? 'UNION' : 'INTERSECT';
140
+ return cteQueries.join(` ${operator} `);
141
+ }
142
+
143
+ private buildSingleRankingCTE(
144
+ filter: FilterConfig,
145
+ rankingConfig: RankingConfig,
146
+ column: string,
147
+ tableName: string,
148
+ ): string {
149
+ const rankByColumnId =
150
+ rankingConfig.rankByColumnId ||
151
+ `${rankingConfig.rankByTableName || ''}[${rankingConfig.rankByColumnName || column}]`;
152
+ const actualColumnId = BaseUtilities.extractColumnIdFromHierarchy(rankByColumnId);
153
+ const rankColumn = actualColumnId;
154
+ const rankTable = rankingConfig.rankByTableName || tableName;
155
+ const aggregationType = this.mapAggregationType(rankingConfig.rankByAggregationType || 'SUM');
156
+
157
+ const topNValue = this.calculateTopNValue(
158
+ rankingConfig.value,
159
+ filter.distinctRowsCount || 1000000,
160
+ rankingConfig.isPercentage || false,
161
+ );
162
+
163
+ const rankByRef = rankTable ? `"${rankTable}"."${rankColumn}"` : `"${rankColumn}"`;
164
+
165
+ if (rankingConfig.type === 'BOTH') {
166
+ const topValue = Math.ceil(topNValue / 2);
167
+ const bottomValue = Math.floor(topNValue / 2);
168
+
169
+ const topQuery = this.buildRankingSelectQuery(
170
+ column,
171
+ tableName,
172
+ rankByRef,
173
+ aggregationType,
174
+ 'DESC',
175
+ topValue,
176
+ );
177
+
178
+ const bottomQuery = this.buildRankingSelectQuery(
179
+ column,
180
+ tableName,
181
+ rankByRef,
182
+ aggregationType,
183
+ 'ASC',
184
+ bottomValue,
185
+ );
186
+
187
+ return `(${topQuery}) UNION ALL (${bottomQuery})`;
188
+ }
189
+
190
+ const orderDirection = rankingConfig.type === 'TOP' ? 'DESC' : 'ASC';
191
+
192
+ return this.buildRankingSelectQuery(
193
+ column,
194
+ tableName,
195
+ rankByRef,
196
+ aggregationType,
197
+ orderDirection,
198
+ topNValue,
199
+ rankingConfig.isPercentage,
200
+ );
201
+ }
202
+
203
+ private buildRankingSelectQuery(
204
+ column: string,
205
+ tableName: string,
206
+ rankByRef: string,
207
+ aggregationType: string,
208
+ orderDirection: string,
209
+ topNValue: number,
210
+ isPercentage?: boolean,
211
+ ): string {
212
+ if (isPercentage) {
213
+ const percentileValue = topNValue / 100;
214
+ const condition = `PERCENT_RANK() OVER (ORDER BY ${aggregationType}(${rankByRef}) ${orderDirection}) <= ${percentileValue}`;
215
+ return BaseUtilities.buildDistinctSelect([`"${column}"`], `"${tableName}"`, condition);
216
+ } else {
217
+ const orderByClause = `${aggregationType}(${rankByRef}) ${orderDirection} LIMIT ${topNValue}`;
218
+ return BaseUtilities.buildDistinctSelect(
219
+ [`"${column}"`],
220
+ `"${tableName}"`,
221
+ undefined,
222
+ orderByClause,
223
+ );
224
+ }
225
+ }
226
+
227
+ private mapAggregationType(aggregationType: string): string {
228
+ return mapAgg(aggregationType);
229
+ }
230
+
231
+ protected buildSearchFilterCTE(filter: FilterConfig, _variableName: string): string {
232
+ void _variableName;
233
+ if (!BaseUtilities.validateRequiredFields(filter, ['columnNames', 'searchConditions'])) {
234
+ return '';
235
+ }
236
+
237
+ const column = filter.columnNames![0];
238
+ const tableName = filter.tableNames?.[0] || '';
239
+
240
+ const conditions = filter.searchConditions!.map((condition) => {
241
+ const canonical = (condition.operator || '')
242
+ .toLowerCase()
243
+ .replace(/[^a-z0-9]+/g, '_')
244
+ .replace(/^_+|_+$/g, '');
245
+ const rawValue = String(condition.value ?? '');
246
+ const escapedValue = rawValue.replace(/'/g, "''");
247
+
248
+ switch (canonical) {
249
+ case 'contains':
250
+ return `LOWER(${column}) LIKE LOWER('%${escapedValue}%')`;
251
+ case 'does_not_contain':
252
+ case 'not_contains':
253
+ case 'not_contain':
254
+ return `LOWER(${column}) NOT LIKE LOWER('%${escapedValue}%')`;
255
+ case 'starts_with':
256
+ return `LOWER(${column}) LIKE LOWER('${escapedValue}%')`;
257
+ case 'does_not_start_with':
258
+ case 'not_starts_with':
259
+ case 'not_start_with':
260
+ return `LOWER(${column}) NOT LIKE LOWER('${escapedValue}%')`;
261
+ case 'ends_with':
262
+ return `LOWER(${column}) LIKE LOWER('%${escapedValue}')`;
263
+ case 'does_not_end_with':
264
+ case 'not_ends_with':
265
+ case 'not_end_with':
266
+ return `LOWER(${column}) NOT LIKE LOWER('%${escapedValue}')`;
267
+ case 'equals':
268
+ case 'is_equal_to':
269
+ case 'is':
270
+ return `${column} = ${BaseUtilities.formatValue(rawValue)}`;
271
+ case 'not_equals':
272
+ case 'is_not_equal_to':
273
+ case 'is_not':
274
+ return `${column} != ${BaseUtilities.formatValue(rawValue)}`;
275
+ case 'is_blank':
276
+ case 'blank':
277
+ return `${column} IS NULL`;
278
+ case 'is_not_blank':
279
+ case 'not_blank':
280
+ return `${column} IS NOT NULL`;
281
+ case 'is_empty':
282
+ return `TRIM(${column}) = ''`;
283
+ case 'is_not_empty':
284
+ return `TRIM(${column}) != ''`;
285
+ default:
286
+ return `LOWER(${column}) LIKE LOWER('%${escapedValue}%')`;
287
+ }
288
+ });
289
+
290
+ const whereCondition = BaseUtilities.combineConditions(conditions, 'AND');
291
+ return BaseUtilities.buildDistinctSelect([column], tableName, whereCondition);
292
+ }
293
+
294
+ protected buildBlankFilterCTE(filter: FilterConfig, _variableName: string): string {
295
+ void _variableName;
296
+ if (!BaseUtilities.validateRequiredFields(filter, ['columnNames', 'blankFilterConfig'])) {
297
+ return '';
298
+ }
299
+
300
+ const column = filter.columnNames![0];
301
+ const tableName = filter.tableNames?.[0] || '';
302
+ const blankConfig = filter.blankFilterConfig!;
303
+
304
+ let condition: string;
305
+ if (blankConfig.operator === 'IS_BLANK') {
306
+ if (blankConfig.treatEmptyStringAsBlank) {
307
+ condition = `(${column} IS NULL OR TRIM(${column}) = '')`;
308
+ } else {
309
+ condition = `${column} IS NULL`;
310
+ }
311
+ } else {
312
+ if (blankConfig.treatEmptyStringAsBlank) {
313
+ condition = `(${column} IS NOT NULL AND TRIM(${column}) != '')`;
314
+ } else {
315
+ condition = `${column} IS NOT NULL`;
316
+ }
317
+ }
318
+
319
+ return BaseUtilities.buildDistinctSelect([column], tableName, condition);
320
+ }
321
+
322
+ protected buildRelativePeriodFilterCTE(filter: FilterConfig, _variableName: string): string {
323
+ void _variableName;
324
+ if (!BaseUtilities.validateRequiredFields(filter, ['columnNames', 'relativePeriodFilter'])) {
325
+ return '';
326
+ }
327
+
328
+ const column = filter.columnNames![0];
329
+ const tableName = filter.tableNames?.[0] || '';
330
+ const relativePeriod = filter.relativePeriodFilter!;
331
+
332
+ const condition = this.buildRelativePeriodCondition(column, relativePeriod);
333
+ return BaseUtilities.buildDistinctSelect([column], tableName, condition);
334
+ }
335
+
336
+ protected buildDateHierarchyFilterCTE(filter: FilterConfig, _variableName: string): string {
337
+ void _variableName;
338
+ if (!BaseUtilities.validateRequiredFields(filter, ['columnNames', 'dateHierarchyConfig'])) {
339
+ return '';
340
+ }
341
+
342
+ const column = filter.columnNames![0];
343
+ const tableName = filter.tableNames?.[0] || '';
344
+ const hierarchyConfig = filter.dateHierarchyConfig!;
345
+
346
+ let hierarchyFunction: string;
347
+ switch (hierarchyConfig.level) {
348
+ case 'YEAR':
349
+ hierarchyFunction = `EXTRACT(YEAR FROM ${column})`;
350
+ break;
351
+ case 'QUARTER':
352
+ hierarchyFunction = `EXTRACT(QUARTER FROM ${column})`;
353
+ break;
354
+ case 'MONTH':
355
+ hierarchyFunction = `EXTRACT(MONTH FROM ${column})`;
356
+ break;
357
+ case 'DAY':
358
+ hierarchyFunction = `EXTRACT(DAY FROM ${column})`;
359
+ break;
360
+ default:
361
+ hierarchyFunction = column;
362
+ }
363
+
364
+ const selectColumn = `${hierarchyFunction} AS ${column}_${hierarchyConfig.level.toLowerCase()}`;
365
+ return BaseUtilities.buildDistinctSelect([selectColumn], tableName);
366
+ }
367
+
368
+ protected buildFieldParameterFilterCTE(filter: FilterConfig, _variableName: string): string {
369
+ void _variableName;
370
+ if (!filter.fieldParameterMapping) {
371
+ return '';
372
+ }
373
+
374
+ const mapping = filter.fieldParameterMapping;
375
+ const tableName = mapping.targetTable;
376
+
377
+ if (!tableName || !mapping.targetField) return '';
378
+
379
+ return BaseUtilities.buildDistinctSelect([mapping.targetField], tableName);
380
+ }
381
+
382
+ protected buildRelativePeriodCondition(
383
+ column: string,
384
+ config: { duration: number; period: string; relativeDateFilter: string },
385
+ ): string {
386
+ const now = 'CURRENT_DATE';
387
+ const duration = config.duration;
388
+ const period = config.period;
389
+
390
+ switch (config.relativeDateFilter) {
391
+ case 'IS_IN_THE_LAST':
392
+ switch (period) {
393
+ case 'DAYS':
394
+ return `${column} >= ${now} - INTERVAL '${duration} days'`;
395
+ case 'WEEKS':
396
+ return `${column} >= ${now} - INTERVAL '${duration} weeks'`;
397
+ case 'MONTHS':
398
+ return `${column} >= ${now} - INTERVAL '${duration} months'`;
399
+ case 'YEARS':
400
+ return `${column} >= ${now} - INTERVAL '${duration} years'`;
401
+ default:
402
+ return `${column} >= ${now} - INTERVAL '${duration} days'`;
403
+ }
404
+ case 'IS_IN_THE_NEXT':
405
+ switch (period) {
406
+ case 'DAYS':
407
+ return `${column} <= ${now} + INTERVAL '${duration} days'`;
408
+ case 'WEEKS':
409
+ return `${column} <= ${now} + INTERVAL '${duration} weeks'`;
410
+ case 'MONTHS':
411
+ return `${column} <= ${now} + INTERVAL '${duration} months'`;
412
+ case 'YEARS':
413
+ return `${column} <= ${now} + INTERVAL '${duration} years'`;
414
+ default:
415
+ return `${column} <= ${now} + INTERVAL '${duration} days'`;
416
+ }
417
+ case 'IS_IN_THIS':
418
+ switch (period) {
419
+ case 'DAYS':
420
+ return `DATE_TRUNC('day', ${column}) = DATE_TRUNC('day', ${now})`;
421
+ case 'WEEKS':
422
+ return `DATE_TRUNC('week', ${column}) = DATE_TRUNC('week', ${now})`;
423
+ case 'MONTHS':
424
+ return `DATE_TRUNC('month', ${column}) = DATE_TRUNC('month', ${now})`;
425
+ case 'YEARS':
426
+ return `DATE_TRUNC('year', ${column}) = DATE_TRUNC('year', ${now})`;
427
+ default:
428
+ return `DATE_TRUNC('day', ${column}) = DATE_TRUNC('day', ${now})`;
429
+ }
430
+ default:
431
+ return `${column} = ${now}`;
432
+ }
433
+ }
434
+
435
+ private calculateTopNValue(value: number, totalCount: number, isPercentage: boolean): number {
436
+ if (totalCount === 0 || !isPercentage) {
437
+ return value;
438
+ }
439
+ return Math.floor((value * totalCount) / 100);
440
+ }
441
+
442
+ protected isComplexFilter(filter: FilterConfig): boolean {
443
+ return Boolean(
444
+ filter.filterType === 'TUPLE' ||
445
+ filter.filterType === 'RANKING' ||
446
+ filter.filterType === 'SEARCH' ||
447
+ filter.filterType === 'BLANK' ||
448
+ filter.filterType === 'RELATIVE_PERIOD' ||
449
+ filter.filterType === 'DATE_HIERARCHY' ||
450
+ filter.filterType === 'FIELD_PARAMETER' ||
451
+ filter.isCrossHighlight ||
452
+ (filter.filterType === 'RANGE' && filter.rangeFilter && filter.rangeFilter.length > 1) ||
453
+ (filter.filterType === 'VALUES' && filter.valuesFilter && filter.valuesFilter.length > 10),
454
+ );
455
+ }
456
+
457
+ abstract buildSpecificFilterCTE(filter: FilterConfig, variableName: string): string;
458
+ }