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,114 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+
3
+ import { getEntityNameFormatter } from '../utils';
4
+ import { FilterConfig, LogicalCombinator, Rule, RuleGroup } from './filter-types';
5
+
6
+ const FILTER_OPEATOR_MAP: Record<string, (columnName: string, value: any) => string> = {
7
+ '=': equalsFn,
8
+ IN: isOneOfFn,
9
+ '<>': notEqualsFn,
10
+ LIKE: containsFn,
11
+ 'NOT LIKE': doesNotContainFn,
12
+ 'IS NULL': emptyFn,
13
+ 'IS NOT NULL': notEmptyFn,
14
+ '<': lessThanFn,
15
+ '<=': lessThanOrEqualFn,
16
+ '>': greaterThanFn,
17
+ '>=': greaterThanOrEqualFn,
18
+ };
19
+
20
+ /**
21
+ * Builds the filter condition for the selected columns
22
+ */
23
+ export function buildFilterCondition(tableName: string, filterConfig: FilterConfig): string {
24
+ const { rules, combinator } = filterConfig;
25
+ const entityNameFormatter = getEntityNameFormatter();
26
+ return recursiveFilterBuilder(tableName, rules, combinator, entityNameFormatter);
27
+ }
28
+
29
+ /**
30
+ * Recursively builds the filter condition
31
+ */
32
+ function recursiveFilterBuilder(
33
+ tableName: string,
34
+ filters: (Rule | RuleGroup)[],
35
+ combinator: LogicalCombinator,
36
+ entityNameFormatter: (name: string) => string,
37
+ ): string {
38
+ return filters
39
+ .map((filter) => {
40
+ if ((filter as RuleGroup).rules) {
41
+ const ruleGroup = filter as RuleGroup;
42
+ return recursiveFilterBuilder(
43
+ tableName,
44
+ ruleGroup.rules,
45
+ ruleGroup.combinator,
46
+ entityNameFormatter,
47
+ );
48
+ }
49
+
50
+ const rule = filter as Rule;
51
+ const opFn = FILTER_OPEATOR_MAP[rule.operator];
52
+ if (!opFn) {
53
+ throw new Error(`Unsupported operator: ${rule.operator}`);
54
+ }
55
+ const [sanitizedColumnName, sanitizedValue] = sanitizeValue(
56
+ entityNameFormatter(rule.column),
57
+ rule.value,
58
+ );
59
+ return opFn(sanitizedColumnName, sanitizedValue);
60
+ })
61
+ .join(` ${combinator.toUpperCase()} `);
62
+ }
63
+
64
+ function sanitizeValue(columnName: string, value: any): [string, string] {
65
+ if (typeof value === 'string') {
66
+ return [`LOWER(${columnName})`, `'${value.toLowerCase()}'`];
67
+ }
68
+ return [columnName, value];
69
+ }
70
+
71
+ function equalsFn(columnName: string, value: any): string {
72
+ return `${columnName} = ${value}`;
73
+ }
74
+
75
+ function notEqualsFn(columnName: string, value: any): string {
76
+ return `${columnName} <> ${value} OR ${columnName} IS NULL`;
77
+ }
78
+
79
+ function containsFn(columnName: string, value: any): string {
80
+ return `${columnName} LIKE %${value}%`;
81
+ }
82
+
83
+ function doesNotContainFn(columnName: string, value: any): string {
84
+ return `${columnName} NOT LIKE %${value}% OR ${columnName} IS NULL`;
85
+ }
86
+
87
+ function emptyFn(columnName: string): string {
88
+ return `${columnName} IS NULL`;
89
+ }
90
+
91
+ function notEmptyFn(columnName: string): string {
92
+ return `${columnName} IS NOT NULL`;
93
+ }
94
+
95
+ function lessThanFn(columnName: string, value: any): string {
96
+ return `${columnName} < ${value} OR ${columnName} IS NULL`;
97
+ }
98
+
99
+ function lessThanOrEqualFn(columnName: string, value: any): string {
100
+ return `${columnName} <= ${value} OR ${columnName} IS NULL`;
101
+ }
102
+
103
+ function greaterThanFn(columnName: string, value: any): string {
104
+ return `${columnName} > ${value}`;
105
+ }
106
+
107
+ function greaterThanOrEqualFn(columnName: string, value: any): string {
108
+ return `${columnName} >= ${value}`;
109
+ }
110
+
111
+ function isOneOfFn(columnName: string, value: string): string {
112
+ const values = value.split(',').map((v) => `'${v.trim()}'`);
113
+ return `${columnName} IN (${values.join(', ')})`;
114
+ }
@@ -0,0 +1,133 @@
1
+ import {
2
+ EDateRangeRelativeFilterCondition,
3
+ EDateRangeRelativePeriodFilterCondition,
4
+ } from '../query-builder/SuperFilterBuilder';
5
+
6
+ // Logical combinator for combining multiple rules
7
+ export type LogicalCombinator = 'and' | 'or';
8
+
9
+ // Simple filter rule (leaf node)
10
+ export type Rule = {
11
+ operator: string;
12
+ value: string | number | boolean | (string | number)[];
13
+ column: string;
14
+ isCategoryFilter?: boolean;
15
+ topNDataTarget?: string;
16
+ };
17
+
18
+ // Group of rules (branch node)
19
+ export type RuleGroup = {
20
+ rules: Array<Rule>;
21
+ combinator: LogicalCombinator;
22
+ };
23
+
24
+ // Root filter configuration
25
+ export type FilterConfig = {
26
+ rules: Array<Rule | RuleGroup>;
27
+ combinator: LogicalCombinator;
28
+ not: boolean;
29
+ };
30
+
31
+ export type FilterType = 'VALUES' | 'RANGE' | 'DATE' | 'RANKING';
32
+
33
+ export interface IValueFilter {
34
+ id: string[];
35
+ label: string[];
36
+ }
37
+
38
+ export interface IRangeFilter {
39
+ from?: string;
40
+ to?: string;
41
+ logicalOperator?: 'AND' | 'OR';
42
+ fromCondition?: string;
43
+ toCondition?: string;
44
+ }
45
+
46
+ export interface IRankingFilter {
47
+ type: 'TOP' | 'BOTTOM' | 'BOTH';
48
+ rankByColumnId: string;
49
+ rankByColumnName: string;
50
+ rankByColumnType: string;
51
+ rankByTableName: string;
52
+ rankByAggregationType: string;
53
+ value: number;
54
+ isPercentage: boolean;
55
+ }
56
+
57
+ export type IDateTimeFilter = {
58
+ relativeDateFilter?: EDateRangeRelativeFilterCondition;
59
+ period?: EDateRangeRelativePeriodFilterCondition;
60
+ duration?: number;
61
+ includeToday?: boolean;
62
+ fiscalYearStartMonth?: number;
63
+ };
64
+
65
+ export interface IGroupFilter {
66
+ columnIds: string[];
67
+ tableNames: string[];
68
+ columnNames: string[];
69
+ dataType: string[];
70
+ filterType: FilterType;
71
+ sourceType?: string[];
72
+ columnType?: string[];
73
+ columnDataType?: string[];
74
+ valuesFilterApplicationType?: 'exclude' | 'include';
75
+ valuesFilter?: IValueFilter[];
76
+ logicalOperator?: 'AND' | 'OR';
77
+ aggregationType?: string[];
78
+ rangeFilter?: IRangeFilter[];
79
+ dateTimeFilter?: IDateTimeFilter;
80
+ rankingFilter?: IRankingFilter[];
81
+ searchConfig?: { matchCase: boolean; searchString: string };
82
+ }
83
+
84
+ export interface ISuperFilterConfig {
85
+ groupId: string;
86
+ groupLabel: string;
87
+ condition: 'AND' | 'OR';
88
+ children: ({ filters: IGroupFilter } | ISuperFilterConfig)[];
89
+ }
90
+
91
+ export type RelationshipMapping = {
92
+ joinType: string;
93
+ id: string;
94
+ schema: string;
95
+ table: string;
96
+ joinId: string;
97
+ label: string;
98
+ alias: string;
99
+ };
100
+
101
+ export type JoinExpressions = {
102
+ alias: string;
103
+ joinRelation: RelationshipMapping[];
104
+ };
105
+
106
+ export type RangeOperator =
107
+ | 'IS'
108
+ | 'IS_NOT'
109
+ | 'IS_BLANK'
110
+ | 'IS_NOT_BLANK'
111
+ | 'IS_AFTER'
112
+ | 'IS_BEFORE'
113
+ | 'IS_ON_OR_AFTER'
114
+ | 'IS_ON_OR_BEFORE'
115
+ | 'IS_LESS_THAN'
116
+ | 'IS_LESS_THAN_OR_EQUAL_TO'
117
+ | 'IS_GREATER_THAN'
118
+ | 'IS_GREATER_THAN_OR_EQUAL_TO'
119
+ | 'CONTAINS'
120
+ | 'DOES_NOT_CONTAIN'
121
+ | 'STARTS_WITH'
122
+ | 'DOES_NOT_START_WITH';
123
+
124
+ export interface IManualFilterOption {
125
+ displayName?: string;
126
+ value: string | number | boolean;
127
+ id: string;
128
+ }
129
+
130
+ export interface IManualFilterConfig {
131
+ column: string;
132
+ options: IManualFilterOption[];
133
+ }
package/src/index.ts ADDED
@@ -0,0 +1,10 @@
1
+ export * from './query-builder/QueryBuilder';
2
+ export * from './js-lib/ParseContext';
3
+ export * from './js-lib/JsToSqlParser';
4
+ export * from './utils';
5
+ export * from './query-builder/SuperFilterBuilder';
6
+ export * from './filters/filter-types';
7
+
8
+ // epm query builder
9
+ export { EpmQueryBuilder } from '@epm-query-builder/EpmQueryBuilder';
10
+ export type { EpmQueryBuilderOptions } from '@epm-query-builder/types/query-builder-types';
@@ -0,0 +1,217 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+
3
+ import { ParseContext, ResolvedExpressionType, SqlQueryProps } from './ParseContext';
4
+ import { VisitorInterface } from './db/base/VisitorInterface';
5
+ import * as acorn from 'acorn';
6
+ import * as walk from 'acorn-walk';
7
+ import { CallExpressionVisitor } from './db/base/CallExpressionVisitor';
8
+ import { IdentifierVisitor } from './db/base/IdentifierVisitor';
9
+ import { LiteralVisitor } from './db/base/LiteralVisitor';
10
+ import { BinaryExpressionVisitor } from './db/base/BinaryExpressionVisitor';
11
+ import { ArrayExpressionVisitor } from './db/base/ArrayExpressionVisitor';
12
+ import { UnaryExpressionVisitor } from './db/base/UnaryExpressionVisitor';
13
+ import { ProgramVisitor } from './db/base/ProgramVisitor';
14
+ import { MemberExpressionVisitor } from './db/base/MemberExpressionVisitor';
15
+ import { generateSqlQuery } from '../sql_query_gen';
16
+ import { AssignmentExpressionVisitor } from './db/base/AssignmentExpressionVisitor';
17
+
18
+ export type ResolvedFormulaProps = {
19
+ returnType: string | undefined;
20
+ isValid: boolean;
21
+ error: string | undefined;
22
+ sqlQueryProps: SqlQueryProps;
23
+ deps: string[];
24
+ };
25
+
26
+ /**
27
+ * Factory function to create database specific parser
28
+ */
29
+ export function createJsToSqlParser(context: ParseContext): JsToSqlParser {
30
+ // Handles functions based on fabricsql type
31
+ return new JsToSqlParser(context);
32
+ }
33
+
34
+ export type VisitorConfig = {
35
+ callExpression?: VisitorInterface<acorn.CallExpression>;
36
+ identifier?: VisitorInterface<acorn.Identifier>;
37
+ literal?: VisitorInterface<acorn.Literal>;
38
+ binaryExpression?: VisitorInterface<acorn.BinaryExpression>;
39
+ arrayExpression?: VisitorInterface<acorn.ArrayExpression>;
40
+ unaryExpression?: VisitorInterface<acorn.UnaryExpression>;
41
+ program?: VisitorInterface<acorn.Program>;
42
+ memberExpression?: VisitorInterface<acorn.MemberExpression>;
43
+ assignmentExpression?: VisitorInterface<acorn.AssignmentExpression>;
44
+ };
45
+
46
+ /**
47
+ * Parses a JavaScript formula and returns the corresponding SQL AST.
48
+ * - For Database specific overrides, implement the corresponding visitor in `db` directory and
49
+ * pass it to the constructor as part of the `config` parameter.
50
+ * - Use Factory function to compose parser for specific database
51
+ */
52
+ export class JsToSqlParser {
53
+ private context: ParseContext;
54
+ private callExpression: VisitorInterface<acorn.CallExpression>;
55
+ private identifier: VisitorInterface<acorn.Identifier>;
56
+ private literal: VisitorInterface<acorn.Literal>;
57
+ private binaryExpression: VisitorInterface<acorn.BinaryExpression>;
58
+ private arrayExpression: VisitorInterface<acorn.ArrayExpression>;
59
+ private unaryExpression: VisitorInterface<acorn.UnaryExpression>;
60
+ private program: VisitorInterface<acorn.Program>;
61
+ private memberExpression: VisitorInterface<acorn.MemberExpression>;
62
+ private assignmentExpression: VisitorInterface<acorn.AssignmentExpression>;
63
+
64
+ constructor(context: ParseContext, config: VisitorConfig = {}) {
65
+ this.context = context;
66
+ this.callExpression = config.callExpression || new CallExpressionVisitor();
67
+ this.identifier = config.identifier || new IdentifierVisitor();
68
+ this.literal = config.literal || new LiteralVisitor();
69
+ this.binaryExpression = config.binaryExpression || new BinaryExpressionVisitor();
70
+ this.arrayExpression = config.arrayExpression || new ArrayExpressionVisitor();
71
+ this.unaryExpression = config.unaryExpression || new UnaryExpressionVisitor();
72
+ this.program = config.program || new ProgramVisitor();
73
+ this.memberExpression = config.memberExpression || new MemberExpressionVisitor();
74
+ this.assignmentExpression = config.assignmentExpression || new AssignmentExpressionVisitor();
75
+ }
76
+
77
+ parse(formula: string): any {
78
+ // Parse the formula
79
+ const formulaText = this.processFormulaText(formula);
80
+ const jsAst = acorn.parse(formulaText, { ecmaVersion: 2020 });
81
+
82
+ // Traverse the AST in pre-order to check for 'if' statements
83
+ this.detectIfCondition(jsAst);
84
+
85
+ // Traverse the AST and generate SQL AST
86
+ walk.recursive(jsAst, this.context.reset(), {
87
+ CallExpression: (node, state, c) => {
88
+ c(node.callee, state);
89
+ node.arguments.forEach((arg) => c(arg, state));
90
+ this.callExpression.visitor(node, state);
91
+ },
92
+ Identifier: (node, state) => {
93
+ this.identifier.visitor(node, state);
94
+ },
95
+ Literal: (node, state) => {
96
+ this.literal.visitor(node, state);
97
+ },
98
+ BinaryExpression: (node, state, c) => {
99
+ c(node.left, state);
100
+ c(node.right, state);
101
+ this.binaryExpression.visitor(node, state);
102
+ },
103
+ AssignmentExpression: (node, state, c) => {
104
+ c(node.left, state);
105
+ c(node.right, state);
106
+ this.assignmentExpression.visitor(node, state);
107
+ },
108
+ ArrayExpression: (node, state, c) => {
109
+ node.elements.forEach((el) => {
110
+ if (el !== null) c(el, state);
111
+ });
112
+ this.arrayExpression.visitor(node, state);
113
+ },
114
+ UnaryExpression: (node, state, c) => {
115
+ c(node.argument, state);
116
+ this.unaryExpression.visitor(node, state);
117
+ },
118
+ MemberExpression: (node, state, c) => {
119
+ c(node.object, state);
120
+ if (node.computed) c(node.property, state);
121
+ this.memberExpression.visitor(node, state);
122
+ },
123
+ Program: (node, state, c) => {
124
+ node.body.forEach((statement) => c(statement, state));
125
+ this.program.visitor(node, state);
126
+ },
127
+ });
128
+ const sqlAst = this.context.getLastEvaluatedValue();
129
+ return sqlAst;
130
+ }
131
+
132
+ /**
133
+ * Converts the SQL AST to a SQL query string
134
+ */
135
+ toSql(sqlAst: any): string {
136
+ return generateSqlQuery(sqlAst);
137
+ }
138
+
139
+ /**
140
+ * Creates appropriate SQL query props based on the SQL AST
141
+ */
142
+ toSqlProps(sqlAst: any): SqlQueryProps {
143
+ const sqlQueryStr = this.toSql(sqlAst);
144
+
145
+ if (this.context.cteCount > 0) {
146
+ return {
147
+ resolvedExpression: {
148
+ type: ResolvedExpressionType.CTE,
149
+ cteName: `cte_${this.context.currentColumnId}`,
150
+ value: [sqlQueryStr],
151
+ },
152
+ };
153
+ }
154
+
155
+ return {
156
+ resolvedExpression: {
157
+ type: ResolvedExpressionType.COLUMN,
158
+ value: [sqlQueryStr],
159
+ },
160
+ };
161
+ }
162
+
163
+ /**
164
+ * Returns the resolved formula props for current column along with the dependencies
165
+ */
166
+ getResolvedFormulaProps(sqlAst: any): ResolvedFormulaProps {
167
+ const sqlQueryProps = this.toSqlProps(sqlAst);
168
+ return {
169
+ sqlQueryProps,
170
+ deps: Array.from(this.context.getColumnDeps()),
171
+ returnType: this.context.returnType,
172
+ isValid: this.context.isValid,
173
+ error: this.context.error,
174
+ };
175
+ }
176
+
177
+ /**
178
+ * Pre walk and detect if the jsAst has 'if'
179
+ */
180
+ private detectIfCondition(jsAst: acorn.Node): void {
181
+ walk.simple(jsAst, {
182
+ CallExpression: (node: acorn.CallExpression) => {
183
+ if (node.callee.type === 'Identifier' && node.callee.name.toUpperCase() === 'IF') {
184
+ this.context.ifCondition = true;
185
+ }
186
+ },
187
+ });
188
+ }
189
+
190
+ /**
191
+ * Extracts and normalizes formula by adding parentheses to reserved function names
192
+ */
193
+ private processFormulaText(formula: string): string {
194
+ // Extract formula from {{ ... }} or use the string as-is
195
+ const formulaText = formula?.match(/{{([^}]*)}}/)?.[1] ?? formula;
196
+
197
+ // Reserved function names to patch
198
+ const reservedFunctions = ['NOW', 'TODAY', 'UUID', 'RAND'];
199
+
200
+ // Patch function names not followed by '(' to add ()
201
+ let patched = formulaText;
202
+ for (const fn of reservedFunctions) {
203
+ const regex = new RegExp(`\\b${fn}\\b(?!\\s*\\()`, 'gi');
204
+ patched = patched.replace(regex, `${fn}()`);
205
+ }
206
+
207
+ if (formulaText.includes('FOREACH')) {
208
+ patched = this.removeForeach(formula);
209
+ }
210
+ return patched;
211
+ }
212
+
213
+ private removeForeach(input: string): string {
214
+ const foreachRegex = /FOREACH\(([^,]*),([^()]*)\)/g;
215
+ return input.replace(foreachRegex, '$2');
216
+ }
217
+ }
@@ -0,0 +1,149 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+
3
+ import * as acorn from 'acorn';
4
+ import {
5
+ getEntityNameFormatter,
6
+ getFormulaIdColumnMap,
7
+ getDeprecatedFormulaIdColumnMap,
8
+ } from '../utils';
9
+
10
+ export enum MDM_COLUMN_TYPE {
11
+ TEXT = 1,
12
+ SINGLE_SELECT = 2,
13
+ MULTI_SELECT = 3,
14
+ DATE = 4,
15
+ COMMENTS = 5,
16
+ NUMBER = 6,
17
+ PERSON = 7,
18
+ TIME = 8,
19
+ LAST_UPDATED_BY = 9,
20
+ LAST_UPDATED_AT = 10,
21
+ CHECKBOX = 11,
22
+ DATETIME = 12,
23
+ DECIMAL = 13,
24
+ IMAGE = 14,
25
+ URL = 15,
26
+ EMAIL = 16,
27
+ FORMULA = 17,
28
+ }
29
+
30
+ export enum ResolvedExpressionType {
31
+ COLUMN = 'columnExpr',
32
+ CTE = 'cteExpr',
33
+ }
34
+
35
+ export type CteExpression = {
36
+ type: ResolvedExpressionType.CTE;
37
+ cteName: string;
38
+ value: string[];
39
+ };
40
+
41
+ export type ColumnExpression = {
42
+ type: ResolvedExpressionType.COLUMN;
43
+ value: string[];
44
+ };
45
+
46
+ export type SqlQueryProps = {
47
+ resolvedExpression: ColumnExpression | CteExpression;
48
+ };
49
+
50
+ export type ColumnMeta = {
51
+ sqlQueryProps?: SqlQueryProps;
52
+ computationOrder?: number;
53
+ deps?: string[];
54
+ [key: string]: any;
55
+ };
56
+
57
+ // This is mock interface for column config. Actual type comes from InfoRiverMDM repo from type `IMDMColumnConfigWithParsedMeta`
58
+ export interface IMDMColumnConfigWithParsedMeta {
59
+ columnName: string;
60
+ columnType: MDM_COLUMN_TYPE;
61
+ columnMeta: ColumnMeta;
62
+ }
63
+
64
+ export type TColumnConfigMap = Record<string, IMDMColumnConfigWithParsedMeta>;
65
+
66
+ export type ParseContextProps = {
67
+ columnConfigMap: TColumnConfigMap;
68
+ currentColumnId: string;
69
+ primaryKeyColumns: string[];
70
+ tableName: string;
71
+ };
72
+
73
+ export class ParseContext {
74
+ private lastEvaluatedValue: any;
75
+ private _cteCount: number = 0;
76
+ public formulaIdColumnMap: Record<string, string>;
77
+ public deprecatedFormulaColumnMap: Record<string, string>;
78
+ public ifCondition: boolean = false;
79
+ private readonly columnDeps: Set<string>;
80
+ public readonly state: Map<string, any>;
81
+ public readonly columnConfigMap: TColumnConfigMap;
82
+ public readonly currentColumnId: string;
83
+ public readonly primaryKeyColumns: string[];
84
+ public readonly tableName: string;
85
+ public readonly entityNameFormatter: (name: string) => string;
86
+ public returnType: string | undefined;
87
+ public isValid: boolean = true;
88
+ public error: string | undefined;
89
+
90
+ constructor(props: ParseContextProps) {
91
+ this.state = new Map();
92
+ this.lastEvaluatedValue = null;
93
+ this.columnConfigMap = props.columnConfigMap;
94
+ this.formulaIdColumnMap = getFormulaIdColumnMap(props.columnConfigMap);
95
+ this.deprecatedFormulaColumnMap = getDeprecatedFormulaIdColumnMap(props.columnConfigMap);
96
+ this.currentColumnId = props.currentColumnId;
97
+ this.primaryKeyColumns = props.primaryKeyColumns;
98
+ this.tableName = props.tableName;
99
+ this.columnDeps = new Set<string>();
100
+ this.entityNameFormatter = getEntityNameFormatter();
101
+ }
102
+
103
+ /**
104
+ * Reset the parse context
105
+ */
106
+ reset(): ParseContext {
107
+ this.lastEvaluatedValue = null;
108
+ this.state.clear();
109
+ this.columnDeps.clear();
110
+ return this;
111
+ }
112
+
113
+ get(node: acorn.Node): any {
114
+ return this.state.get(this.getId(node));
115
+ }
116
+
117
+ set(node: acorn.Node, value: any): void {
118
+ this.lastEvaluatedValue = value;
119
+ this.state.set(this.getId(node), value);
120
+ }
121
+
122
+ getId(node: acorn.Node): string {
123
+ return `${node.start}_${node.end}`;
124
+ }
125
+
126
+ getLastEvaluatedValue(): any {
127
+ return this.lastEvaluatedValue;
128
+ }
129
+
130
+ getColumnCount(): number {
131
+ return Object.keys(this.columnConfigMap).length;
132
+ }
133
+
134
+ addColumnDep(columnName: string): void {
135
+ this.columnDeps.add(columnName);
136
+ }
137
+
138
+ getColumnDeps(): Set<string> {
139
+ return this.columnDeps;
140
+ }
141
+
142
+ get cteCount(): number {
143
+ return this._cteCount;
144
+ }
145
+
146
+ incrementCTECount(): void {
147
+ this._cteCount++;
148
+ }
149
+ }
@@ -0,0 +1,16 @@
1
+ import { VisitorInterface } from './VisitorInterface';
2
+ import * as acorn from 'acorn';
3
+ import { ParseContext } from '../../ParseContext';
4
+
5
+ export class ArrayExpressionVisitor implements VisitorInterface<acorn.ArrayExpression> {
6
+ /**
7
+ * visitor needs to be implemented as arrow function to avoid `this` binding issue since it will be
8
+ * passed to `acorn-walk` library
9
+ */
10
+ public visitor = (node: acorn.ArrayExpression, context: ParseContext) => {
11
+ context.set(
12
+ node,
13
+ node.elements.map((arg) => context.get(arg as acorn.Node)),
14
+ );
15
+ };
16
+ }
@@ -0,0 +1,34 @@
1
+ import { VisitorInterface } from './VisitorInterface';
2
+ import * as acorn from 'acorn';
3
+ import { ParseContext } from '../../ParseContext';
4
+ import { toBinaryExpression } from '../../../sql-lib/binary_expr';
5
+ import { PctObject } from '../../objects/PctObject';
6
+ import { ExpressionValue } from '../../../sql-types';
7
+ import { getArgType } from '@/utils';
8
+
9
+ export class AssignmentExpressionVisitor implements VisitorInterface<acorn.AssignmentExpression> {
10
+ public visitor = (node: acorn.AssignmentExpression, context: ParseContext) => {
11
+ const operator = node.operator;
12
+
13
+ // We're only treating "=" as comparison
14
+ if (operator !== '=') {
15
+ context.isValid = false;
16
+ context.error = `Unsupported assignment operator: "${operator}"`;
17
+ return;
18
+ }
19
+ const lhs = context.get(node.left) as ExpressionValue;
20
+ const rhs = context.get(node.right) as ExpressionValue;
21
+ if (getArgType(lhs, context) !== getArgType(rhs, context)) {
22
+ context.isValid = false;
23
+ context.error = `Expected both sides dataType to be same`;
24
+ }
25
+
26
+ if (rhs instanceof PctObject) {
27
+ context.set(node, rhs.resolve(lhs, { type: 'string', value: '=' }));
28
+ return;
29
+ }
30
+
31
+ const expr = toBinaryExpression('=', lhs, rhs, false);
32
+ context.set(node, expr);
33
+ };
34
+ }