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,571 @@
1
+ // helpers: shared utilities used across dialects and builders
2
+ import {
3
+ EpmQueryBuilderOptions,
4
+ RowColumnConfig,
5
+ OrderByConfig,
6
+ } from '@epm-query-builder/types/query-builder-types';
7
+ import * as ColumnRefUtils from '@epm-query-builder/base/ColumnRefUtils';
8
+ import { mapAggregationType as mapAgg } from '@epm-query-builder/constants/Aggregations';
9
+ import { SOURCE_TYPES } from '@epm-query-builder/constants/Source';
10
+ import * as Validation from '@epm-query-builder/utils/validation';
11
+ import * as Format from '@epm-query-builder/utils/format';
12
+ import * as Sql from '@epm-query-builder/utils/sql';
13
+
14
+ export class BaseUtilities {
15
+ static buildSqlSelect(
16
+ items: string[],
17
+ tableName: string,
18
+ whereClause?: string,
19
+ groupBy?: string[],
20
+ orderBy?: string,
21
+ limit?: string,
22
+ ): string {
23
+ return Sql.buildSqlSelect(items, tableName, whereClause, groupBy, orderBy, limit);
24
+ }
25
+
26
+ static formatColumnWithAlias(column: string, alias: string): string {
27
+ return `${column} AS "${alias}"`;
28
+ }
29
+
30
+ static buildCTETemplate(name: string, query: string): string {
31
+ return `${name} AS (${query})`;
32
+ }
33
+
34
+ static validateRequiredFields(obj: unknown, fields: string[]): boolean {
35
+ return Validation.validateRequiredFields(obj, fields);
36
+ }
37
+
38
+ static combineConditions(conditions: string[], operator: 'AND' | 'OR' = 'AND'): string {
39
+ return Sql.combineConditions(conditions, operator);
40
+ }
41
+
42
+ static formatValue(value: string | number | boolean): string {
43
+ return Format.formatValue(value);
44
+ }
45
+
46
+ static stringToDate(value: string): string {
47
+ if (!value) return 'NULL';
48
+
49
+ const dateFormats = [
50
+ /^\d{4}-\d{2}-\d{2}$/, // YYYY-MM-DD
51
+ /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/, // YYYY-MM-DD HH:mm:ss
52
+ /^\d{2}\/\d{2}\/\d{4}$/, // MM/DD/YYYY
53
+ /^\d{2}-\d{2}-\d{4}$/, // MM-DD-YYYY
54
+ ];
55
+
56
+ if (dateFormats.some((format) => format.test(value))) {
57
+ return `DATE('${value}')`;
58
+ }
59
+
60
+ return `'${value.replace(/'/g, "''")}'`;
61
+ }
62
+
63
+ static formatValueForDataType(value: string | number | boolean, dataType: string): string {
64
+ if (!dataType) {
65
+ return this.formatValue(value);
66
+ }
67
+
68
+ switch (dataType.toLowerCase()) {
69
+ case 'string':
70
+ return `'${String(value).replace(/'/g, "''")}'`;
71
+ case 'date':
72
+ case 'datetime':
73
+ return this.stringToDate(String(value));
74
+ case 'number':
75
+ case 'decimal':
76
+ case 'integer':
77
+ return String(value);
78
+ case 'boolean':
79
+ return String(value).toLowerCase() === 'true' ? 'TRUE' : 'FALSE';
80
+ default:
81
+ return this.formatValue(value);
82
+ }
83
+ }
84
+
85
+ static buildColumnReference(
86
+ tableName: string | undefined,
87
+ columnName: string,
88
+ useQuotes = true,
89
+ ): string {
90
+ if (!tableName) return columnName;
91
+
92
+ if (useQuotes) {
93
+ return `"${tableName}"."${columnName}"`;
94
+ }
95
+ return `${tableName}.${columnName}`;
96
+ }
97
+
98
+ static extractTableNameFromId(columnId: string): string | null {
99
+ return ColumnRefUtils.getTableNameFromId(columnId);
100
+ }
101
+
102
+ static getEffectiveColumnName(config: RowColumnConfig): string {
103
+ const actualColumnId = this.extractColumnIdFromHierarchy(config.id);
104
+ return actualColumnId;
105
+ }
106
+
107
+ static extractColumnIdFromHierarchy(columnId: string): string {
108
+ return ColumnRefUtils.extractColumnIdFromHierarchy(columnId);
109
+ }
110
+
111
+ static extractTableNameFromColumnId(columnId: string, columnName: string): string {
112
+ return ColumnRefUtils.extractTableNameFromColumnId(columnId, columnName);
113
+ }
114
+
115
+ static formColumnId(tableName: string, columnName: string): string {
116
+ if (!tableName || !columnName) return columnName || '';
117
+ return `"${tableName}"."${columnName}"`;
118
+ }
119
+
120
+ static isValidTableName(tableName: string): boolean {
121
+ return /^[a-zA-Z0-9_[\]().\s]+$/.test(tableName);
122
+ }
123
+
124
+ static getEffectiveTableName(config: RowColumnConfig, options: EpmQueryBuilderOptions): string {
125
+ return (
126
+ config.tableName ||
127
+ BaseUtilities.extractTableNameFromId(config.id) ||
128
+ BaseUtilities.getDefaultTableName(options)
129
+ );
130
+ }
131
+
132
+ static getDefaultTableName(options: EpmQueryBuilderOptions): string {
133
+ const allItems = [
134
+ ...(options.rows || []),
135
+ ...(options.columns || []),
136
+ ...(options.values || []),
137
+ ];
138
+
139
+ for (const item of allItems) {
140
+ if (item.tableName) return item.tableName;
141
+ }
142
+
143
+ for (const item of allItems) {
144
+ const tableFromId = BaseUtilities.extractTableNameFromId(item.id);
145
+ if (tableFromId) return tableFromId;
146
+ }
147
+
148
+ const tablesFromFilters = this.extractTableNames(options);
149
+ if (tablesFromFilters.length > 0) {
150
+ return tablesFromFilters[0];
151
+ }
152
+
153
+ return '';
154
+ }
155
+
156
+ static buildDistinctSelect(
157
+ columns: string[],
158
+ tableName: string,
159
+ whereClause?: string,
160
+ orderBy?: string,
161
+ ): string {
162
+ return Sql.buildDistinctSelect(columns, tableName, whereClause, orderBy);
163
+ }
164
+
165
+ static buildCountQuery(tableName: string, whereClause?: string): string {
166
+ let query = `SELECT COUNT(*) FROM ${tableName}`;
167
+
168
+ if (whereClause) {
169
+ query += ` WHERE ${whereClause}`;
170
+ }
171
+
172
+ return query;
173
+ }
174
+
175
+ static sanitizeIdentifier(identifier: string): string {
176
+ return Format.sanitizeIdentifier(identifier);
177
+ }
178
+
179
+ static isEmptyOrWhitespace(value: string | undefined | null): boolean {
180
+ return Validation.isEmptyOrWhitespace(value);
181
+ }
182
+
183
+ static isValidIdentifier(identifier: string): boolean {
184
+ return Validation.isValidIdentifier(identifier);
185
+ }
186
+
187
+ static ensureArray<T>(value: T | T[] | undefined): T[] {
188
+ if (!value) return [];
189
+ return Array.isArray(value) ? value : [value];
190
+ }
191
+
192
+ static hasValue<T>(value: T | undefined | null): value is T {
193
+ return value !== undefined && value !== null;
194
+ }
195
+
196
+ static buildOrderByClause(orderItems: OrderByConfig[]): string {
197
+ if (!orderItems?.length) return '';
198
+
199
+ const clauses = orderItems.map((item) => {
200
+ const direction = item.type?.toUpperCase() === 'DESC' ? 'DESC' : 'ASC';
201
+ return `${item.columnName} ${direction}`;
202
+ });
203
+
204
+ return `ORDER BY ${clauses.join(', ')}`;
205
+ }
206
+
207
+ static extractUniqueTableNames(options: EpmQueryBuilderOptions): string[] {
208
+ const tables = new Set<string>();
209
+
210
+ [...(options.rows || []), ...(options.columns || []), ...(options.values || [])].forEach(
211
+ (item) => {
212
+ if (!item) return;
213
+
214
+ const directTableName = item.tableName;
215
+
216
+ const inferredTableName =
217
+ !directTableName && item.id ? BaseUtilities.getTableNameFromId(item.id) : null;
218
+
219
+ const effectiveTableName = directTableName || inferredTableName;
220
+ if (effectiveTableName) {
221
+ tables.add(effectiveTableName);
222
+ }
223
+ },
224
+ );
225
+
226
+ if (options.superFilters?.children) {
227
+ options.superFilters.children.forEach((child) => {
228
+ if (child.filters?.tableNames) {
229
+ child.filters.tableNames.forEach((tableName) => {
230
+ if (tableName) tables.add(tableName);
231
+ });
232
+ }
233
+ });
234
+ }
235
+
236
+ return Array.from(tables);
237
+ }
238
+
239
+ static buildLimitClause(offset?: number, limit?: number): string {
240
+ return Sql.buildLimitClause(offset, limit);
241
+ }
242
+
243
+ static buildMonthOrderKey(columnExpression: string): string {
244
+ const expr = `TRIM(CAST(${columnExpression} AS VARCHAR))`;
245
+ return `COALESCE(
246
+ EXTRACT(MONTH FROM TRY_STRPTIME(${expr}, '%b')),
247
+ EXTRACT(MONTH FROM TRY_STRPTIME(${expr}, '%B')),
248
+ EXTRACT(MONTH FROM TRY_STRPTIME(LOWER(${expr}), '%b')),
249
+ EXTRACT(MONTH FROM TRY_STRPTIME(LOWER(${expr}), '%B')),
250
+ TRY_CAST(${columnExpression} AS INTEGER),
251
+ 13
252
+ )`;
253
+ }
254
+
255
+ static buildQuarterOrderKey(columnExpression: string): string {
256
+ const expr = `TRIM(LOWER(CAST(${columnExpression} AS VARCHAR)))`;
257
+ return `COALESCE(
258
+ EXTRACT(QUARTER FROM TRY_STRPTIME(${expr}, '%b')),
259
+ EXTRACT(QUARTER FROM TRY_STRPTIME(${expr}, '%B')),
260
+ CASE WHEN ${expr} LIKE 'q1%' THEN 1 WHEN ${expr} LIKE 'q2%' THEN 2 WHEN ${expr} LIKE 'q3%' THEN 3 WHEN ${expr} LIKE 'q4%' THEN 4 ELSE NULL END,
261
+ TRY_CAST(${columnExpression} AS INTEGER),
262
+ 5
263
+ )`;
264
+ }
265
+
266
+ static buildMeasureExpression(
267
+ tableName: string,
268
+ columnName: string,
269
+ aggregationType?: string,
270
+ ): string {
271
+ if (!aggregationType || aggregationType === 'NONE') {
272
+ return `"${tableName}"."${columnName}"`;
273
+ }
274
+
275
+ const mappedAggregation = this.mapAggregationType(aggregationType);
276
+ return `${mappedAggregation}("${tableName}"."${columnName}")`;
277
+ }
278
+
279
+ static mapAggregationType(aggregationType: string): string {
280
+ return mapAgg(aggregationType);
281
+ }
282
+
283
+ static buildWhereCondition(conditions: string[], operator: 'AND' | 'OR' = 'AND'): string {
284
+ const validConditions = conditions.filter((condition) => condition && condition.trim());
285
+
286
+ if (validConditions.length === 0) return '';
287
+ if (validConditions.length === 1) return validConditions[0];
288
+
289
+ return `(${validConditions.join(` ${operator} `)})`;
290
+ }
291
+
292
+ static getTableNameFromId(columnId: string): string | null {
293
+ return ColumnRefUtils.getTableNameFromId(columnId);
294
+ }
295
+
296
+ static getTableNameFromColumnId(columnId: string, columnName?: string): string {
297
+ if (!columnId) return '';
298
+ if (columnName) {
299
+ return this.extractTableNameFromColumnId(columnId, columnName);
300
+ }
301
+ return this.getTableNameFromId(columnId) || '';
302
+ }
303
+
304
+ static sanitizeHierarchyPrefix(columnId: string): string {
305
+ return ColumnRefUtils.extractColumnIdFromHierarchy(columnId);
306
+ }
307
+
308
+ static extractTableNames(options: EpmQueryBuilderOptions): string[] {
309
+ if (!options.superFilters?.children) {
310
+ return [];
311
+ }
312
+ const tableNames = new Set<string>();
313
+ options.superFilters.children.forEach((child) => {
314
+ if (child.filters?.tableNames) {
315
+ child.filters.tableNames.forEach((tableName) => {
316
+ tableNames.add(tableName);
317
+ });
318
+ }
319
+ });
320
+ return Array.from(tableNames);
321
+ }
322
+
323
+ static formatColumnReferenceForDb(
324
+ config: RowColumnConfig,
325
+ options: EpmQueryBuilderOptions,
326
+ ): string {
327
+ const source = this.getSource(options);
328
+ const actualColumnId = this.extractColumnIdFromHierarchy(config.id);
329
+ const idForDb = source === SOURCE_TYPES.BLEND ? config.id : actualColumnId;
330
+ if (options.databaseDetails?.databaseType === 'duckdb') {
331
+ const useDot = this.shouldUseDotNotation(options);
332
+ if (useDot) {
333
+ const tableName =
334
+ config.tableName ||
335
+ this.getTableNameFromId(config.id) ||
336
+ this.getDefaultTableNameFromOptions(options) ||
337
+ this.extractTableNames(options)[0] ||
338
+ '';
339
+ const effectiveColumnName = idForDb;
340
+ if (tableName) {
341
+ return this.buildColumnReference(tableName, effectiveColumnName);
342
+ }
343
+ return `"${effectiveColumnName}"`;
344
+ }
345
+ return `"${idForDb}"`;
346
+ }
347
+ const tableName =
348
+ config.tableName ||
349
+ this.getTableNameFromId(config.id) ||
350
+ this.extractTableNames(options)[0] ||
351
+ '';
352
+ const effectiveColumnName = idForDb;
353
+ return `${tableName}.${effectiveColumnName}`;
354
+ }
355
+
356
+ static buildDimensionReference(
357
+ items: RowColumnConfig[] | undefined,
358
+ options: EpmQueryBuilderOptions,
359
+ ): string[] {
360
+ return items?.map((item) => this.formatColumnReferenceForDb(item, options)) || [];
361
+ }
362
+
363
+ static getDefaultTableNameFromOptions(options: EpmQueryBuilderOptions): string {
364
+ const rows = options.rows || [];
365
+ const columns = options.columns || [];
366
+ const values = options.values || [];
367
+ const allItems = [...rows, ...columns, ...values];
368
+ if (allItems.length > 0 && allItems[0].tableName) {
369
+ return allItems[0].tableName;
370
+ }
371
+ const tablesFromFilters = this.extractTableNames(options);
372
+ if (tablesFromFilters.length > 0) {
373
+ return tablesFromFilters[0];
374
+ }
375
+ return '';
376
+ }
377
+
378
+ static formatTableColumnReference(tableName: string, columnName: string): string {
379
+ return `"${tableName}"."${columnName}"`;
380
+ }
381
+
382
+ static sanitizeIdentifierString(identifier: string): string {
383
+ return identifier.replace(/[^a-zA-Z0-9_]/g, '_');
384
+ }
385
+
386
+ static isValidTableNameString(tableName: string): string | boolean {
387
+ return /^[a-zA-Z0-9_[\]().\s]+$/.test(tableName);
388
+ }
389
+
390
+ static formatTableNameForDb(tableName: string, databaseType?: string): string {
391
+ if (databaseType === 'duckdb' || tableName.includes(' ') || tableName.includes('-')) {
392
+ return `"${tableName}"`;
393
+ }
394
+ return tableName;
395
+ }
396
+
397
+ static shouldUseDotNotation(options: EpmQueryBuilderOptions): boolean {
398
+ const src = this.getSource(options);
399
+ return src === SOURCE_TYPES.BLEND;
400
+ }
401
+
402
+ static getSource(options: EpmQueryBuilderOptions): string {
403
+ const source =
404
+ (options as unknown as { config?: { source?: string } })?.config?.source ||
405
+ (options as unknown as { source?: string })?.source ||
406
+ this.safeGet<string>(
407
+ options.databaseDetails as unknown as Record<string, unknown>,
408
+ 'source',
409
+ '',
410
+ ) ||
411
+ '';
412
+ return String(source || '').toLowerCase();
413
+ }
414
+
415
+ static buildFilterConditionForOperator(
416
+ column: string,
417
+ operator: string,
418
+ value: string | number | boolean,
419
+ ): string {
420
+ const formattedValue = typeof value === 'string' ? BaseUtilities.formatValue(value) : value;
421
+ switch (operator.toUpperCase()) {
422
+ case 'IS_EQUAL_TO':
423
+ case 'IS':
424
+ return `${column} = ${formattedValue}`;
425
+ case 'IS_NOT_EQUAL_TO':
426
+ case 'IS_NOT':
427
+ return `${column} != ${formattedValue}`;
428
+ case 'IS_GREATER_THAN':
429
+ return `${column} > ${formattedValue}`;
430
+ case 'IS_GREATER_THAN_OR_EQUAL_TO':
431
+ return `${column} >= ${formattedValue}`;
432
+ case 'IS_LESS_THAN':
433
+ return `${column} < ${formattedValue}`;
434
+ case 'IS_LESS_THAN_OR_EQUAL_TO':
435
+ return `${column} <= ${formattedValue}`;
436
+ case 'IS_BLANK':
437
+ return `${column} IS NULL`;
438
+ case 'IS_NOT_BLANK':
439
+ return `${column} IS NOT NULL`;
440
+ default:
441
+ return `${column} = ${formattedValue}`;
442
+ }
443
+ }
444
+
445
+ static normalizeComparisonOperator(operator: string): string {
446
+ const canonical = (operator || '')
447
+ .toLowerCase()
448
+ .replace(/[^a-z0-9]+/g, '_')
449
+ .replace(/^_+|_+$/g, '');
450
+ switch (canonical) {
451
+ case 'is':
452
+ case 'equals':
453
+ case 'is_equal_to':
454
+ return '=';
455
+ case 'is_not':
456
+ case 'not_equals':
457
+ case 'is_not_equal_to':
458
+ return '!=';
459
+ case 'is_greater_than':
460
+ return '>';
461
+ case 'is_greater_than_or_equal_to':
462
+ return '>=';
463
+ case 'is_less_than':
464
+ return '<';
465
+ case 'is_less_than_or_equal_to':
466
+ return '<=';
467
+ case 'is_blank':
468
+ return 'IS NULL';
469
+ case 'is_not_blank':
470
+ return 'IS NOT NULL';
471
+ default:
472
+ return '=';
473
+ }
474
+ }
475
+
476
+ static validateRequiredField<T>(
477
+ value: T | undefined | null,
478
+ fieldName: string,
479
+ context?: string,
480
+ ): T {
481
+ if (value === undefined || value === null) {
482
+ const contextMsg = context ? ` in ${context}` : '';
483
+ throw new Error(`Required field '${fieldName}' is missing${contextMsg}`);
484
+ }
485
+ return value;
486
+ }
487
+
488
+ static safeGet<T>(
489
+ obj: Record<string, unknown> | undefined,
490
+ key: string,
491
+ defaultValue?: T,
492
+ ): T | undefined {
493
+ if (!obj || !(key in obj)) {
494
+ return defaultValue;
495
+ }
496
+ return obj[key] as T;
497
+ }
498
+
499
+ static extractDimensionsFromOptions(options: EpmQueryBuilderOptions): string[] {
500
+ const rowDimensions = this.buildDimensionReference(options.rows, options);
501
+ const columnDimensions = this.buildDimensionReference(options.columns, options);
502
+ return [...rowDimensions, ...columnDimensions];
503
+ }
504
+
505
+ static extractMeasuresFromOptions(options: EpmQueryBuilderOptions): unknown[] {
506
+ return options.values || [];
507
+ }
508
+
509
+ static getSelectedColumnsFromOptions(options: EpmQueryBuilderOptions): string[] {
510
+ if (options.rows?.length) {
511
+ return options.rows.map((row) => row.columnName);
512
+ }
513
+ return options.selectedColumns || [];
514
+ }
515
+
516
+ static hasDimensionsInOptions(options: EpmQueryBuilderOptions): boolean {
517
+ const rowCount = options.rows?.length || 0;
518
+ const columnCount = options.columns?.length || 0;
519
+ return rowCount + columnCount > 0;
520
+ }
521
+
522
+ static hasMeasuresInOptions(options: EpmQueryBuilderOptions): boolean {
523
+ return (options.values?.length || 0) > 0;
524
+ }
525
+
526
+ static shouldGenerateRollupForOptions(options: EpmQueryBuilderOptions): boolean {
527
+ return !options.skipRollup && this.hasDimensionsInOptions(options);
528
+ }
529
+
530
+ static shouldGenerateAnalyticalQueryForOptions(options: EpmQueryBuilderOptions): boolean {
531
+ return (
532
+ this.hasMeasuresInOptions(options) ||
533
+ (this.shouldGenerateRollupForOptions(options) && this.hasMeasuresInOptions(options))
534
+ );
535
+ }
536
+
537
+ static formatColumnNameWithFallbackString(columnName: string, columnId: string): string {
538
+ const actualColumnId = this.extractColumnIdFromHierarchy(columnId);
539
+ return columnName || actualColumnId;
540
+ }
541
+
542
+ static buildBasicGroupByClauseForOptions(options: EpmQueryBuilderOptions): string {
543
+ const dimensions = this.extractDimensionsFromOptions(options);
544
+ return dimensions.length > 0 ? `GROUP BY ${dimensions.join(', ')}` : '';
545
+ }
546
+
547
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
548
+ static generateConcatenatedIdMeasure(dimensionMappings: Record<string, any>): [string, string] {
549
+ if (!dimensionMappings || Object.keys(dimensionMappings).length === 0) {
550
+ return ['', ''];
551
+ }
552
+
553
+ const targetTable = Object.keys(dimensionMappings)[0];
554
+ const mappings = dimensionMappings[targetTable];
555
+
556
+ if (!mappings || mappings.length === 0) {
557
+ return ['', targetTable];
558
+ }
559
+
560
+ const concatenatedColumns = mappings
561
+ .map(
562
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
563
+ (mapping: any) => `COALESCE("${mapping.targetTable}"."${mapping.targetField}", '')`,
564
+ )
565
+ .join(" || '|' || ");
566
+
567
+ const measure = `MEASURE '${targetTable}'[CONCATENATED_IDS] = ${concatenatedColumns}`;
568
+
569
+ return [measure, targetTable];
570
+ }
571
+ }
@@ -0,0 +1,42 @@
1
+ export function extractColumnIdFromHierarchy(columnId: string): string {
2
+ if (columnId.startsWith('[H] ')) {
3
+ return columnId.substring(4);
4
+ }
5
+ if (columnId.includes('~||~')) {
6
+ const parts = columnId.split('~||~');
7
+ return parts.length > 1 ? parts[1] : columnId;
8
+ }
9
+ return columnId;
10
+ }
11
+
12
+ export function getTableNameFromId(columnId: string): string | null {
13
+ const actualColumnId = extractColumnIdFromHierarchy(columnId);
14
+ const lastBracketIndex = actualColumnId.lastIndexOf('[');
15
+ if (lastBracketIndex > 0) {
16
+ return actualColumnId.substring(0, lastBracketIndex).trim();
17
+ }
18
+ return null;
19
+ }
20
+
21
+ export function extractTableNameFromColumnId(columnId: string, columnName: string): string {
22
+ if (!columnId || !columnName) {
23
+ return '';
24
+ }
25
+ const sanitizedColumnId = extractColumnIdFromHierarchy(columnId);
26
+ const columnNamePattern = `[${columnName}]`;
27
+ const index = sanitizedColumnId.lastIndexOf(columnNamePattern);
28
+ if (index !== -1) {
29
+ return sanitizedColumnId.substring(0, index);
30
+ }
31
+ return sanitizedColumnId;
32
+ }
33
+
34
+ export function getColumnNameFromId(columnId: string): string {
35
+ const actualColumnId = extractColumnIdFromHierarchy(columnId);
36
+ const start = actualColumnId.lastIndexOf('[');
37
+ const end = actualColumnId.lastIndexOf(']');
38
+ if (start >= 0 && end > start) {
39
+ return actualColumnId.substring(start + 1, end);
40
+ }
41
+ return actualColumnId;
42
+ }