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,628 @@
1
+ import {
2
+ JoinExpressions,
3
+ RangeOperator,
4
+ ISuperFilterConfig,
5
+ IGroupFilter,
6
+ IManualFilterConfig,
7
+ IManualFilterOption,
8
+ } from '../filters/filter-types';
9
+ import { DateFilterFactory } from '../superFilter/DateFilterFactory';
10
+ import { getEntityNameFormatter, getFormattedTableName } from '../utils';
11
+ import { MAX_STRING_TYPE } from './QueryBuilder';
12
+
13
+ export enum EDateRangeRelativeFilterCondition {
14
+ IS_IN_THE_LAST = 'IS_IN_THE_LAST',
15
+ IS_IN_THIS = 'IS_IN_THIS',
16
+ IS_IN_THE_NEXT = 'IS_IN_THE_NEXT',
17
+ }
18
+
19
+ export enum EDateRangeRelativePeriodFilterCondition {
20
+ DAYS = 'DAYS',
21
+ WEEKS = 'WEEKS',
22
+ CALENDER_WEEKS = 'CALENDER_WEEKS',
23
+ MONTHS = 'MONTHS',
24
+ CALENDER_MONTHS = 'CALENDER_MONTHS',
25
+ YEARS = 'YEARS',
26
+ CALENDER_YEARS = 'CALENDER_YEARS',
27
+ HOURS = 'HOURS',
28
+ MINUTES = 'MINUTES',
29
+ CALENDER_QUARTERS = 'CALENDER_QUARTERS',
30
+ FISCAL_QUARTER = 'FISCAL_QUARTER',
31
+ FISCAL_YEAR = 'FISCAL_YEAR',
32
+ QUARTERS = 'QUARTERS',
33
+ }
34
+
35
+ const rangeFilterOperator: Record<RangeOperator, string> = {
36
+ // common operators
37
+ IS: '=',
38
+ IS_NOT: '!=',
39
+ IS_BLANK: 'IS',
40
+ IS_NOT_BLANK: 'IS NOT',
41
+ //date specific operators
42
+ IS_AFTER: '>',
43
+ IS_BEFORE: '<',
44
+ IS_ON_OR_AFTER: '>=',
45
+ IS_ON_OR_BEFORE: '<=',
46
+ //numeric specific operators
47
+ IS_LESS_THAN: '<',
48
+ IS_LESS_THAN_OR_EQUAL_TO: '<=',
49
+ IS_GREATER_THAN: '>',
50
+ IS_GREATER_THAN_OR_EQUAL_TO: '>=',
51
+ //character specific operators
52
+ CONTAINS: 'LIKE',
53
+ DOES_NOT_CONTAIN: 'NOT LIKE',
54
+ STARTS_WITH: 'LIKE',
55
+ DOES_NOT_START_WITH: 'NOT LIKE',
56
+ };
57
+
58
+ const characterRangeFilterOperator = [
59
+ 'CONTAINS',
60
+ 'DOES_NOT_CONTAIN',
61
+ 'STARTS_WITH',
62
+ 'DOES_NOT_START_WITH',
63
+ ];
64
+
65
+ export const negativeCharacterOperators: RangeOperator[] = [
66
+ 'DOES_NOT_CONTAIN',
67
+ 'DOES_NOT_START_WITH',
68
+ 'IS_NOT',
69
+ ];
70
+
71
+ export const startsWithCharacterOperators: RangeOperator[] = ['STARTS_WITH', 'DOES_NOT_START_WITH'];
72
+
73
+ export const containsCharacterOperators: RangeOperator[] = ['CONTAINS', 'DOES_NOT_CONTAIN'];
74
+
75
+ const SUPER_FILTER_NUMERIC_SUPPORTED_DATA_TYPES = [
76
+ 'Int64',
77
+ 'Decimal',
78
+ 'Double',
79
+ 'Integer',
80
+ 'Number',
81
+ ];
82
+
83
+ export enum EFilterDataType {
84
+ BOOLEAN = 'boolean',
85
+ DATE = 'date',
86
+ NUMBER = 'number',
87
+ STRING = 'string',
88
+ }
89
+
90
+ // Base class for super filter builder
91
+ export class SuperFilterBuilder {
92
+ private superFilter: ISuperFilterConfig;
93
+ private databaseDetails: any;
94
+ private skipCountForRankingFilter: boolean;
95
+ private joinClauses: JoinExpressions;
96
+ private manualFilterOptions: IManualFilterConfig[];
97
+
98
+ // new: the selected (last) ranking filter across the whole payload
99
+ private lastRankingFilter: IGroupFilter | null = null;
100
+
101
+ constructor(
102
+ superFilter: ISuperFilterConfig,
103
+ databaseDetails: any,
104
+ skipCountForRankingFilter: boolean = false,
105
+ joinClauses: JoinExpressions = { alias: '', joinRelation: [] },
106
+ manualFilterOptions: IManualFilterConfig[] = [],
107
+ ) {
108
+ this.superFilter = superFilter;
109
+ this.databaseDetails = databaseDetails;
110
+ this.skipCountForRankingFilter = skipCountForRankingFilter;
111
+ this.joinClauses = joinClauses;
112
+ this.manualFilterOptions = manualFilterOptions;
113
+
114
+ // find the last ranking filter in the whole payload and store a reference
115
+ this.lastRankingFilter = this.findLastRankingFilter(this.superFilter);
116
+ }
117
+
118
+ /**
119
+ * Entry point: build SQL condition string
120
+ */
121
+ public build(): string {
122
+ const whereCondition = this.processGroup(this.superFilter, true, this.lastRankingFilter);
123
+
124
+ //appending ranking filter at the end of the where condition.
125
+ if (this.lastRankingFilter && !this.skipCountForRankingFilter) {
126
+ // wrap existing where in parentheses to pass as a single expression to ranking
127
+ const existingWhere = whereCondition ? `(${whereCondition})` : '';
128
+ const rankingCondition = this.processRankingFilter(this.lastRankingFilter);
129
+
130
+ if (!whereCondition) {
131
+ // no other filters, return ranking condition
132
+ return rankingCondition;
133
+ }
134
+
135
+ // ensure ranking condition is appended at the end
136
+ return `${existingWhere} AND ${rankingCondition}`;
137
+ }
138
+
139
+ return whereCondition;
140
+ }
141
+
142
+ /**
143
+ * Recursively process group and children
144
+ * - skips all ranking filters during this pass
145
+ * - preserves order of all other filters
146
+ */
147
+ private processGroup(
148
+ group: any,
149
+ isRoot = false,
150
+ lastRankingToSkip: IGroupFilter | null = null,
151
+ ): string {
152
+ if (!group?.children) throw new Error('No childrens found');
153
+
154
+ const parts: string[] = [];
155
+
156
+ for (const child of group.children) {
157
+ if ('groupId' in child && 'children' in child) {
158
+ const nested = this.processGroup(child, false, lastRankingToSkip);
159
+ if (nested) parts.push(nested);
160
+ } else if ('filters' in child) {
161
+ const filter = child.filters as IGroupFilter;
162
+
163
+ if (filter.sourceType?.[0] !== 'powerTables') throw new Error('Unsupported source type');
164
+
165
+ if (filter.filterType === 'RANKING') continue;
166
+
167
+ const condition = this.processFilter(filter);
168
+ if (condition) parts.push(condition);
169
+ const searchCondition = this.processSearchFilter(filter);
170
+ if (searchCondition) parts.push(searchCondition);
171
+ }
172
+ }
173
+
174
+ if (parts.length === 0) return '';
175
+
176
+ const joined = parts.join(` ${group.condition ?? 'AND'} `);
177
+
178
+ return isRoot ? joined : `(${joined})`;
179
+ }
180
+
181
+ /**
182
+ * Find the last ranking filter in the whole superFilter (depth-first, left-to-right traversal)
183
+ */
184
+ private findLastRankingFilter(group: any): IGroupFilter | null {
185
+ if (!group?.children) return null;
186
+
187
+ let last: IGroupFilter | null = null;
188
+
189
+ for (const child of group.children) {
190
+ if ('groupId' in child && 'children' in child) {
191
+ const found = this.findLastRankingFilter(child);
192
+ if (found) last = found;
193
+ } else if ('filters' in child) {
194
+ if (child.filters.filterType === 'RANKING') {
195
+ last = child.filters as IGroupFilter;
196
+ }
197
+ }
198
+ }
199
+
200
+ return last;
201
+ }
202
+
203
+ /**
204
+ * Decide which filter type to handle
205
+ */
206
+ private processFilter(filter: IGroupFilter): string {
207
+ switch (filter.filterType) {
208
+ case 'VALUES':
209
+ return this.processValuesFilter(filter);
210
+ case 'RANGE':
211
+ return this.processRangeFilter(filter);
212
+ case 'DATE': {
213
+ const column = this.getColumnName(filter);
214
+ return DateFilterFactory.build(column, filter);
215
+ }
216
+ case 'RANKING':
217
+ // ranking filters are intentionally ignored here; they are processed in build()
218
+ return '';
219
+ default:
220
+ if (!filter.searchConfig) {
221
+ throw new Error(`Unsupported filter type: ${filter.filterType}`);
222
+ }
223
+ return '';
224
+ }
225
+ }
226
+
227
+ /**
228
+ * Handle VALUES filter => column IN/NOT IN (...)
229
+ */
230
+ private processValuesFilter(filter: IGroupFilter): string {
231
+ if (!filter.valuesFilter || filter.valuesFilter.length === 0)
232
+ throw new Error('Values filter not found');
233
+ const column = this.getColumnName(filter);
234
+ const values = filter.valuesFilter.flatMap((v) => v.id);
235
+
236
+ const include = filter.valuesFilterApplicationType === 'include';
237
+ const nonBlankValues = values.filter((v) => v !== '(Blank)' && v !== null);
238
+ const hasBlank = values.length !== nonBlankValues.length;
239
+
240
+ const formattedValues = nonBlankValues
241
+ .map((v) => this.formatValue(v, this.getFilterDataType(filter)))
242
+ .join(', ');
243
+
244
+ const parts = [];
245
+ if (nonBlankValues.length) {
246
+ parts.push(`${column} ${include ? 'IN' : 'NOT IN'} (${formattedValues})`);
247
+ }
248
+ if (hasBlank) {
249
+ let baseFilter = `${column} IS ${include ? '' : 'NOT '}NULL`;
250
+
251
+ if (filter.dataType?.[0] === EFilterDataType.STRING) {
252
+ baseFilter = `${baseFilter} OR ${column} = ''`;
253
+ }
254
+ parts.push(baseFilter);
255
+ }
256
+
257
+ return `(${parts.join(include ? ' OR ' : ' AND')})`;
258
+ }
259
+
260
+ /**
261
+ * Handle RANGE filter => column op value AND/OR column op value
262
+ */
263
+
264
+ private processRangeFilter(filter: IGroupFilter): string {
265
+ if (!filter.rangeFilter || filter.rangeFilter.length === 0)
266
+ throw new Error('Range filter not found');
267
+
268
+ const column = this.getColumnName(filter);
269
+ const { rangeFilter } = filter;
270
+
271
+ const allExpressions: string[] = [];
272
+
273
+ const columnName = filter.columnNames?.[0] ?? filter.columnIds?.[0];
274
+ const manualConfig = this.manualFilterOptions?.find((opt) => opt.column === columnName);
275
+
276
+ for (const range of rangeFilter) {
277
+ const parts: string[] = [];
278
+ const hasFrom = range.from !== undefined && range.fromCondition !== undefined;
279
+ const hasTo = range.to !== undefined && range.toCondition !== undefined;
280
+
281
+ const dataType = this.getFilterDataType(filter);
282
+
283
+ // Handle "from"
284
+ if (hasFrom) {
285
+ const op = rangeFilterOperator[range.fromCondition as RangeOperator];
286
+ const isCharacterFilter = characterRangeFilterOperator.includes(
287
+ range.fromCondition as RangeOperator,
288
+ );
289
+
290
+ if (isCharacterFilter) {
291
+ if (manualConfig) {
292
+ const manualMatch = this.getManualFilterMatch(
293
+ range.from,
294
+ filter,
295
+ range.fromCondition as RangeOperator,
296
+ manualConfig,
297
+ );
298
+ parts.push(manualMatch);
299
+ } else {
300
+ parts.push(
301
+ this.formatRelativeCharacterFilter(
302
+ column,
303
+ range.fromCondition as RangeOperator,
304
+
305
+ range.from,
306
+ ),
307
+ );
308
+ }
309
+ } else {
310
+ const formattedValue = this.formatValue(range.from, dataType);
311
+ let baseFilter = `${column} ${op} ${formattedValue}`;
312
+ const { isBlankOperator, isNotBlankOperator } = this.getOperatorFlags(
313
+ range.fromCondition as RangeOperator,
314
+ );
315
+ if (isBlankOperator && dataType === EFilterDataType.STRING) {
316
+ baseFilter = `${baseFilter} OR ${column} = ''`;
317
+ }
318
+ if (isNotBlankOperator && dataType === EFilterDataType.STRING) {
319
+ baseFilter = `${baseFilter} AND ${column} <> ''`;
320
+ }
321
+ parts.push(baseFilter);
322
+ }
323
+ }
324
+
325
+ // Handle "to"
326
+ if (hasTo) {
327
+ const op = rangeFilterOperator[range.toCondition as RangeOperator];
328
+ const isCharacterFilter = characterRangeFilterOperator.includes(
329
+ range.toCondition as RangeOperator,
330
+ );
331
+
332
+ if (isCharacterFilter) {
333
+ if (manualConfig) {
334
+ const manualMatch = this.getManualFilterMatch(
335
+ range.to,
336
+ filter,
337
+ range.toCondition as RangeOperator,
338
+ manualConfig,
339
+ );
340
+ parts.push(manualMatch);
341
+ } else {
342
+ parts.push(
343
+ this.formatRelativeCharacterFilter(
344
+ column,
345
+ range.toCondition as RangeOperator,
346
+ range.to,
347
+ ),
348
+ );
349
+ }
350
+ } else {
351
+ const formattedValue = this.formatValue(range.to, dataType);
352
+ let baseFilter = `${column} ${op} ${formattedValue}`;
353
+
354
+ const { isBlankOperator, isNotBlankOperator } = this.getOperatorFlags(
355
+ range.fromCondition as RangeOperator,
356
+ );
357
+ if (isBlankOperator && dataType === EFilterDataType.STRING) {
358
+ baseFilter = `${baseFilter} OR ${column} = ''`;
359
+ }
360
+ if (isNotBlankOperator && dataType === EFilterDataType.STRING) {
361
+ baseFilter = `${baseFilter} AND ${column} <> ''`;
362
+ }
363
+ parts.push(baseFilter);
364
+ }
365
+ }
366
+
367
+ if (parts.length > 0) {
368
+ // If both exist and this range specifies a logicalOperator join with that
369
+ if (hasFrom && hasTo && range.logicalOperator) {
370
+ allExpressions.push(`(${parts.join(` ${range.logicalOperator.toUpperCase()} `)})`);
371
+ } else if (parts.length === 1) {
372
+ allExpressions.push(parts[0]);
373
+ }
374
+ }
375
+ }
376
+
377
+ if (allExpressions.length === 0) return '';
378
+
379
+ // Top-level join fallback to filter.logicalOperator OR default AND
380
+ return `(${allExpressions.join(` ${filter.logicalOperator?.toUpperCase() ?? 'AND'} `)})`;
381
+ }
382
+
383
+ /**
384
+ * Handle RANKING filter (top/bottom/both)
385
+ */
386
+
387
+ private processRankingFilter(filter: IGroupFilter): string {
388
+ if (!filter.rankingFilter || filter.rankingFilter.length === 0) {
389
+ throw new Error('Ranking filter not found');
390
+ }
391
+
392
+ const { rankingFilter } = filter;
393
+ const subQueries: string[] = [];
394
+ const dataType = filter.dataType?.[0];
395
+ const formatEntity = getEntityNameFormatter();
396
+
397
+ // Step 1: Build table name without alias
398
+ const tableName = getFormattedTableName(this.databaseDetails);
399
+
400
+ // Step 2: Derive safe column names
401
+ const targetColumn = this.getColumnName(filter);
402
+
403
+ for (const rank of rankingFilter) {
404
+ const { type, value, isPercentage, rankByColumnId } = rank;
405
+
406
+ const rankByColumn = formatEntity(rankByColumnId, dataType);
407
+ const limitCondition = isPercentage
408
+ ? `CAST(ROUND(COUNT(*) * ${value} / 100.0) AS INT)`
409
+ : `${value}`;
410
+
411
+ const whereParts: string[] = [`${rankByColumn} IS NOT NULL`];
412
+ if (targetColumn !== rankByColumn) {
413
+ whereParts.push(`${targetColumn} IS NOT NULL`);
414
+ }
415
+ const finalWhere = whereParts.length > 0 ? `WHERE ${whereParts.join(' AND ')}` : '';
416
+
417
+ // Build TOP subquery if needed
418
+ if (type === 'TOP' || type === 'BOTH') {
419
+ subQueries.push(`
420
+ ${targetColumn} IN (
421
+ SELECT ranked.${targetColumn}
422
+ FROM (
423
+ SELECT ${targetColumn},
424
+ DENSE_RANK() OVER (ORDER BY ${rankByColumn} DESC) AS rn
425
+ FROM ${tableName}
426
+ ${finalWhere}
427
+ ) ranked
428
+ WHERE rn <= ${limitCondition}
429
+ )
430
+ `);
431
+ }
432
+
433
+ // Build BOTTOM subquery if needed
434
+ if (type === 'BOTTOM' || type === 'BOTH') {
435
+ subQueries.push(`
436
+ ${targetColumn} IN (
437
+ SELECT ranked.${targetColumn}
438
+ FROM (
439
+ SELECT ${targetColumn},
440
+ DENSE_RANK() OVER (ORDER BY ${rankByColumn} ASC) AS rn
441
+ FROM ${tableName}
442
+ ${finalWhere}
443
+ ) ranked
444
+ WHERE rn <= ${limitCondition}
445
+ )
446
+ `);
447
+ }
448
+ }
449
+
450
+ // Join TOP/BOTTOM subqueries with OR, wrap them to maintain AND order
451
+ return `(${subQueries.join(' OR ')})`;
452
+ }
453
+
454
+ /**
455
+ * Resolve column name (table.column or column only)
456
+ */
457
+ private getColumnName(filter: IGroupFilter): string {
458
+ const { alias, joinRelation = [] } = this.joinClauses || {};
459
+ let mainTableAlias = alias;
460
+ let col = filter.columnNames?.[0] ?? filter.columnIds?.[0];
461
+ // If any join.id matches the column, use join.id instead join.label and alias
462
+ const matchingJoin = joinRelation.find((join) => join.id === col);
463
+ if (matchingJoin) {
464
+ col = matchingJoin.label;
465
+ mainTableAlias = matchingJoin.alias as string;
466
+ }
467
+ const formatter = getEntityNameFormatter();
468
+ const tableAlias = mainTableAlias ? `${formatter(mainTableAlias)}.` : '';
469
+ if (!col) return '';
470
+ return tableAlias + formatter(col, filter.dataType?.[0]);
471
+ }
472
+
473
+ /**
474
+ * Get filter data type
475
+ */
476
+ private getFilterDataType(filter: IGroupFilter): string {
477
+ const dataType = filter.dataType?.[0];
478
+ if (!dataType) return '';
479
+ return dataType;
480
+ }
481
+
482
+ /**
483
+ * Format value depending on datatype/db
484
+ */
485
+ private formatValue(val: unknown, dt: string): string | null {
486
+ if (val === null || val === undefined) return null;
487
+ if (dt === 'date' || dt === 'datetime') {
488
+ return `'${val}'`;
489
+ }
490
+ switch (typeof val) {
491
+ case 'string':
492
+ return `'${val}'`;
493
+ case 'boolean':
494
+ return val ? '1' : '0';
495
+ default:
496
+ return String(val);
497
+ }
498
+ }
499
+
500
+ /**
501
+ * Format relative character filter
502
+ */
503
+ private formatRelativeCharacterFilter(column: string, op: RangeOperator, value: any): string {
504
+ switch (op) {
505
+ case 'CONTAINS':
506
+ return `${column} LIKE '%${value}%'`;
507
+ case 'DOES_NOT_CONTAIN':
508
+ return `${column} NOT LIKE '%${value}%'`;
509
+ case 'STARTS_WITH':
510
+ return `${column} LIKE '${value}%'`;
511
+ case 'DOES_NOT_START_WITH':
512
+ return `${column} NOT LIKE '${value}%'`;
513
+ default:
514
+ throw new Error(`Unsupported character operator: ${op}`);
515
+ }
516
+ }
517
+
518
+ /**
519
+ * Process search filter
520
+ */
521
+ public processSearchFilter(filter: IGroupFilter, isSearch = true): string {
522
+ if (!filter.searchConfig) return '';
523
+
524
+ const { joinRelation = [] } = this.joinClauses || {};
525
+ const columnName = filter.columnNames?.[0] ?? filter.columnIds?.[0];
526
+ const joinTableDetails = joinRelation.find((join) => join.id === columnName);
527
+ const tableRef = joinTableDetails?.alias ?? joinTableDetails?.table;
528
+ const formatter = getEntityNameFormatter();
529
+ let column = this.getColumnName(filter);
530
+ if (joinTableDetails) {
531
+ column = `${formatter(tableRef!, filter.dataType?.[0])}.${formatter(joinTableDetails.label, filter.dataType?.[0])}`;
532
+ }
533
+
534
+ const castType = MAX_STRING_TYPE;
535
+ const { searchString, matchCase } = filter.searchConfig;
536
+
537
+ const columnExpr =
538
+ matchCase && isSearch
539
+ ? `CAST(${column} AS ${castType})`
540
+ : `LOWER(CAST(${column} AS ${castType}))`;
541
+ if (!isSearch) {
542
+ // --- No search input mode ---
543
+ // This handles NULL or empty strings gracefully COALESCE support all databases
544
+ return `COALESCE(${columnExpr}, '')`;
545
+ }
546
+ // --- Normal search mode ---
547
+ const valueExpr = matchCase ? `'%${searchString}%'` : `'%${searchString.toLowerCase()}%'`;
548
+ return `${columnExpr} LIKE ${valueExpr}`;
549
+ }
550
+
551
+ private getManualFilterMatch(
552
+ value = '',
553
+ filter: IGroupFilter,
554
+ operator: RangeOperator,
555
+ manualConfig?: { column: string; options: IManualFilterOption[] },
556
+ ): string {
557
+ if (
558
+ !manualConfig ||
559
+ !Array.isArray(manualConfig.options) ||
560
+ manualConfig.options.length === 0
561
+ ) {
562
+ return '';
563
+ }
564
+
565
+ const columnType = filter.dataType?.[0] || '';
566
+ const formattedColumn = this.getColumnName(filter);
567
+
568
+ const {
569
+ isBlankOperator,
570
+ isNotBlankOperator,
571
+ isNegativeOperator,
572
+ isStartsWithOperator,
573
+ isContainsOperator,
574
+ } = this.getOperatorFlags(operator);
575
+
576
+ if (isBlankOperator) {
577
+ return columnType !== EFilterDataType.STRING
578
+ ? `${formattedColumn} IS NULL`
579
+ : `${formattedColumn} IS NULL OR ${formattedColumn} = ''`;
580
+ }
581
+ if (isNotBlankOperator) {
582
+ return columnType !== EFilterDataType.STRING
583
+ ? `${formattedColumn} IS NOT NULL`
584
+ : `${formattedColumn} IS NOT NULL AND ${formattedColumn} <> ''`;
585
+ }
586
+ if (!value) return isNegativeOperator ? '1=1' : '1=0';
587
+ const matchedValues = manualConfig.options
588
+ .filter((opt) => {
589
+ const displayText = (opt.displayName || '').toString();
590
+
591
+ if (isContainsOperator) {
592
+ return displayText.includes(value);
593
+ }
594
+
595
+ if (isStartsWithOperator) {
596
+ return displayText.startsWith(value);
597
+ }
598
+
599
+ return displayText.includes(value);
600
+ })
601
+ .map((opt) => this.formatValue(opt.id ?? opt.value, this.getFilterDataType(filter)));
602
+
603
+ if (matchedValues.length > 0) {
604
+ const sqlOperator = isNegativeOperator ? 'NOT IN' : 'IN';
605
+ return `${formattedColumn} ${sqlOperator} (${matchedValues.join(', ')})`;
606
+ }
607
+
608
+ return isNegativeOperator ? '1=1' : '1=0';
609
+ }
610
+
611
+ private getOperatorFlags(operator: RangeOperator) {
612
+ const isCharacterOperator = characterRangeFilterOperator.includes(operator);
613
+ const isBlankOperator = operator === 'IS_BLANK';
614
+ const isNotBlankOperator = operator === 'IS_NOT_BLANK';
615
+ const isNegativeOperator = negativeCharacterOperators.includes(operator);
616
+ const isStartsWithOperator = startsWithCharacterOperators.includes(operator);
617
+ const isContainsOperator = containsCharacterOperators.includes(operator);
618
+
619
+ return {
620
+ isCharacterOperator,
621
+ isBlankOperator,
622
+ isNotBlankOperator,
623
+ isNegativeOperator,
624
+ isStartsWithOperator,
625
+ isContainsOperator,
626
+ };
627
+ }
628
+ }
@@ -0,0 +1,8 @@
1
+ export const RUNTIME_TABLE_NAME = '$RUNTIME_TABLE_NAME';
2
+ export const RUNTIME_FILTER_EXPR = '$RUNTIME_FILTER_EXPR';
3
+ export const RUNTIME_SORT_EXPR = '$RUNTIME_SORT_EXPR';
4
+ export const RUNTIME_PAGINATION_EXPR = '$RUNTIME_PAGINATION_EXPR';
5
+ export const RUNTIME_LOGGEDIN_EMAIL = '$RUNTIME_LOGGEDIN_EMAIL';
6
+ export const RUNTIME_LOGGEDIN_NAME = '$RUNTIME_LOGGEDIN_NAME';
7
+ export const RUNTIME_CURRENT_DATE = '$RUNTIME_CURRENT_DATE';
8
+ export const RUNTIME_CURRENT_DATETIME = '$RUNTIME_CURRENT_DATETIME';
@@ -0,0 +1,16 @@
1
+ import { Binary, ExpressionValue } from '../sql-types';
2
+
3
+ export function toBinaryExpression(
4
+ operator: string,
5
+ left: ExpressionValue,
6
+ right: ExpressionValue,
7
+ parentheses = false,
8
+ ): Binary {
9
+ return {
10
+ type: 'binary_expr',
11
+ operator,
12
+ left,
13
+ right,
14
+ parentheses,
15
+ };
16
+ }
@@ -0,0 +1,16 @@
1
+ import { Binary, Case, ExpressionValue } from '../sql-types';
2
+
3
+ export function toCase(args: {
4
+ whenList: {
5
+ cond: Binary;
6
+ result: ExpressionValue;
7
+ type: 'when';
8
+ }[];
9
+ elseResult: { type: 'else'; result: ExpressionValue };
10
+ }): Case {
11
+ return {
12
+ type: 'case',
13
+ expr: null,
14
+ args: [...args.whenList, args.elseResult],
15
+ };
16
+ }
@@ -0,0 +1,18 @@
1
+ import { Column, ColumnRef, ExpressionValue } from '../sql-types';
2
+
3
+ export function toColumnRef(columName: string, table: string | null = null): ColumnRef {
4
+ return {
5
+ type: 'column_ref',
6
+ table: table,
7
+ column: columName,
8
+ collate: undefined,
9
+ };
10
+ }
11
+
12
+ export function toColumn(expr: ExpressionValue, as: string | null = null): Column {
13
+ return {
14
+ type: 'column',
15
+ expr: expr,
16
+ as: as,
17
+ };
18
+ }
@@ -0,0 +1,13 @@
1
+ import { ExpressionValue } from '../sql-types';
2
+
3
+ export type ElseType = {
4
+ type: 'else';
5
+ result: ExpressionValue;
6
+ };
7
+
8
+ export function toElse(result: ExpressionValue): ElseType {
9
+ return {
10
+ type: 'else',
11
+ result,
12
+ };
13
+ }