@quillsql/react 2.13.35 → 2.13.37

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 (196) hide show
  1. package/dist/cjs/Chart.d.ts +0 -1
  2. package/dist/cjs/Chart.d.ts.map +1 -1
  3. package/dist/cjs/Chart.js +17 -14
  4. package/dist/cjs/ChartBuilder.d.ts +1 -0
  5. package/dist/cjs/ChartBuilder.d.ts.map +1 -1
  6. package/dist/cjs/ChartBuilder.js +178 -96
  7. package/dist/cjs/Context.d.ts.map +1 -1
  8. package/dist/cjs/Context.js +6 -8
  9. package/dist/cjs/Dashboard.d.ts +2 -1
  10. package/dist/cjs/Dashboard.d.ts.map +1 -1
  11. package/dist/cjs/Dashboard.js +18 -8
  12. package/dist/cjs/ReportBuilder.d.ts +2 -0
  13. package/dist/cjs/ReportBuilder.d.ts.map +1 -1
  14. package/dist/cjs/ReportBuilder.js +398 -271
  15. package/dist/cjs/SQLEditor.d.ts.map +1 -1
  16. package/dist/cjs/SQLEditor.js +33 -11
  17. package/dist/cjs/Table.d.ts.map +1 -1
  18. package/dist/cjs/Table.js +17 -1
  19. package/dist/cjs/components/Chart/InternalChart.d.ts +0 -1
  20. package/dist/cjs/components/Chart/InternalChart.d.ts.map +1 -1
  21. package/dist/cjs/components/Chart/InternalChart.js +6 -7
  22. package/dist/cjs/components/Dashboard/DataLoader.d.ts.map +1 -1
  23. package/dist/cjs/components/Dashboard/DataLoader.js +75 -30
  24. package/dist/cjs/components/Dashboard/MetricComponent.d.ts +1 -1
  25. package/dist/cjs/components/Dashboard/MetricComponent.d.ts.map +1 -1
  26. package/dist/cjs/components/Dashboard/MetricComponent.js +98 -106
  27. package/dist/cjs/components/QuillMultiSelectSectionList.d.ts.map +1 -1
  28. package/dist/cjs/components/QuillMultiSelectSectionList.js +21 -16
  29. package/dist/cjs/components/QuillMultiSelectWithCombo.js +18 -8
  30. package/dist/cjs/components/QuillSelectWithCombo.js +17 -7
  31. package/dist/cjs/components/ReportBuilder/AddLimitPopover.d.ts +1 -1
  32. package/dist/cjs/components/ReportBuilder/AddLimitPopover.d.ts.map +1 -1
  33. package/dist/cjs/components/ReportBuilder/AddLimitPopover.js +2 -2
  34. package/dist/cjs/components/ReportBuilder/AddSortPopover.d.ts +16 -4
  35. package/dist/cjs/components/ReportBuilder/AddSortPopover.d.ts.map +1 -1
  36. package/dist/cjs/components/ReportBuilder/AddSortPopover.js +9 -18
  37. package/dist/cjs/components/UiComponents.d.ts +2 -1
  38. package/dist/cjs/components/UiComponents.d.ts.map +1 -1
  39. package/dist/cjs/components/UiComponents.js +44 -28
  40. package/dist/cjs/hooks/useAskQuill.d.ts.map +1 -1
  41. package/dist/cjs/hooks/useAskQuill.js +28 -4
  42. package/dist/cjs/hooks/useQuill.d.ts.map +1 -1
  43. package/dist/cjs/hooks/useQuill.js +9 -1
  44. package/dist/cjs/hooks/useVirtualTables.d.ts.map +1 -1
  45. package/dist/cjs/hooks/useVirtualTables.js +11 -35
  46. package/dist/cjs/internals/ReportBuilder/PivotForm.d.ts.map +1 -1
  47. package/dist/cjs/internals/ReportBuilder/PivotForm.js +14 -2
  48. package/dist/cjs/internals/ReportBuilder/PivotModal.d.ts +12 -11
  49. package/dist/cjs/internals/ReportBuilder/PivotModal.d.ts.map +1 -1
  50. package/dist/cjs/internals/ReportBuilder/PivotModal.js +63 -18
  51. package/dist/cjs/models/Client.d.ts +4 -5
  52. package/dist/cjs/models/Client.d.ts.map +1 -1
  53. package/dist/cjs/models/Pivot.d.ts +10 -0
  54. package/dist/cjs/models/Pivot.d.ts.map +1 -1
  55. package/dist/cjs/models/Report.d.ts +13 -1
  56. package/dist/cjs/models/Report.d.ts.map +1 -1
  57. package/dist/cjs/utils/astFilterProcessing.d.ts +1 -1
  58. package/dist/cjs/utils/astFilterProcessing.d.ts.map +1 -1
  59. package/dist/cjs/utils/astFilterProcessing.js +799 -64
  60. package/dist/cjs/utils/astProcessing.d.ts +4 -1
  61. package/dist/cjs/utils/astProcessing.d.ts.map +1 -1
  62. package/dist/cjs/utils/astProcessing.js +2 -2
  63. package/dist/cjs/utils/client.d.ts.map +1 -1
  64. package/dist/cjs/utils/client.js +6 -3
  65. package/dist/cjs/utils/columnProcessing.d.ts +1 -0
  66. package/dist/cjs/utils/columnProcessing.d.ts.map +1 -1
  67. package/dist/cjs/utils/columnProcessing.js +1 -0
  68. package/dist/cjs/utils/dashboard.d.ts +2 -1
  69. package/dist/cjs/utils/dashboard.d.ts.map +1 -1
  70. package/dist/cjs/utils/dashboard.js +38 -10
  71. package/dist/cjs/utils/filterProcessing.d.ts +1 -1
  72. package/dist/cjs/utils/filterProcessing.d.ts.map +1 -1
  73. package/dist/cjs/utils/merge.d.ts +16 -0
  74. package/dist/cjs/utils/merge.d.ts.map +1 -1
  75. package/dist/cjs/utils/merge.js +210 -0
  76. package/dist/cjs/utils/paginationProcessing.d.ts +1 -1
  77. package/dist/cjs/utils/paginationProcessing.d.ts.map +1 -1
  78. package/dist/cjs/utils/paginationProcessing.js +3 -2
  79. package/dist/cjs/utils/pivotConstructor.d.ts +19 -9
  80. package/dist/cjs/utils/pivotConstructor.d.ts.map +1 -1
  81. package/dist/cjs/utils/pivotConstructor.js +90 -34
  82. package/dist/cjs/utils/queryConstructor.d.ts +8 -1
  83. package/dist/cjs/utils/queryConstructor.d.ts.map +1 -1
  84. package/dist/cjs/utils/queryConstructor.js +276 -310
  85. package/dist/cjs/utils/report.d.ts +25 -12
  86. package/dist/cjs/utils/report.d.ts.map +1 -1
  87. package/dist/cjs/utils/report.js +13 -7
  88. package/dist/cjs/utils/schema.d.ts +1 -1
  89. package/dist/cjs/utils/schema.d.ts.map +1 -1
  90. package/dist/cjs/utils/schema.js +3 -32
  91. package/dist/cjs/utils/tableProcessing.d.ts +43 -13
  92. package/dist/cjs/utils/tableProcessing.d.ts.map +1 -1
  93. package/dist/cjs/utils/tableProcessing.js +140 -75
  94. package/dist/cjs/utils/textProcessing.d.ts.map +1 -1
  95. package/dist/cjs/utils/textProcessing.js +10 -1
  96. package/dist/cjs/utils/valueFormatter.d.ts +2 -1
  97. package/dist/cjs/utils/valueFormatter.d.ts.map +1 -1
  98. package/dist/cjs/utils/valueFormatter.js +18 -14
  99. package/dist/esm/Chart.d.ts +0 -1
  100. package/dist/esm/Chart.d.ts.map +1 -1
  101. package/dist/esm/Chart.js +0 -6
  102. package/dist/esm/ChartBuilder.d.ts +1 -0
  103. package/dist/esm/ChartBuilder.d.ts.map +1 -1
  104. package/dist/esm/ChartBuilder.js +179 -97
  105. package/dist/esm/Context.d.ts.map +1 -1
  106. package/dist/esm/Context.js +7 -9
  107. package/dist/esm/Dashboard.d.ts +2 -1
  108. package/dist/esm/Dashboard.d.ts.map +1 -1
  109. package/dist/esm/Dashboard.js +1 -1
  110. package/dist/esm/ReportBuilder.d.ts +2 -0
  111. package/dist/esm/ReportBuilder.d.ts.map +1 -1
  112. package/dist/esm/ReportBuilder.js +399 -272
  113. package/dist/esm/SQLEditor.d.ts.map +1 -1
  114. package/dist/esm/SQLEditor.js +33 -11
  115. package/dist/esm/Table.d.ts.map +1 -1
  116. package/dist/esm/Table.js +17 -1
  117. package/dist/esm/components/Chart/InternalChart.d.ts +0 -1
  118. package/dist/esm/components/Chart/InternalChart.d.ts.map +1 -1
  119. package/dist/esm/components/Chart/InternalChart.js +6 -6
  120. package/dist/esm/components/Dashboard/DataLoader.d.ts.map +1 -1
  121. package/dist/esm/components/Dashboard/DataLoader.js +75 -30
  122. package/dist/esm/components/Dashboard/MetricComponent.d.ts +1 -1
  123. package/dist/esm/components/Dashboard/MetricComponent.d.ts.map +1 -1
  124. package/dist/esm/components/Dashboard/MetricComponent.js +98 -106
  125. package/dist/esm/components/QuillMultiSelectSectionList.d.ts.map +1 -1
  126. package/dist/esm/components/QuillMultiSelectSectionList.js +5 -12
  127. package/dist/esm/components/QuillMultiSelectWithCombo.js +1 -1
  128. package/dist/esm/components/ReportBuilder/AddLimitPopover.d.ts +1 -1
  129. package/dist/esm/components/ReportBuilder/AddLimitPopover.d.ts.map +1 -1
  130. package/dist/esm/components/ReportBuilder/AddLimitPopover.js +2 -2
  131. package/dist/esm/components/ReportBuilder/AddSortPopover.d.ts +16 -4
  132. package/dist/esm/components/ReportBuilder/AddSortPopover.d.ts.map +1 -1
  133. package/dist/esm/components/ReportBuilder/AddSortPopover.js +9 -18
  134. package/dist/esm/components/UiComponents.d.ts +2 -1
  135. package/dist/esm/components/UiComponents.d.ts.map +1 -1
  136. package/dist/esm/components/UiComponents.js +27 -21
  137. package/dist/esm/hooks/useAskQuill.d.ts.map +1 -1
  138. package/dist/esm/hooks/useAskQuill.js +28 -4
  139. package/dist/esm/hooks/useQuill.d.ts.map +1 -1
  140. package/dist/esm/hooks/useQuill.js +10 -2
  141. package/dist/esm/hooks/useVirtualTables.d.ts.map +1 -1
  142. package/dist/esm/hooks/useVirtualTables.js +12 -36
  143. package/dist/esm/internals/ReportBuilder/PivotForm.d.ts.map +1 -1
  144. package/dist/esm/internals/ReportBuilder/PivotForm.js +14 -2
  145. package/dist/esm/internals/ReportBuilder/PivotModal.d.ts +12 -11
  146. package/dist/esm/internals/ReportBuilder/PivotModal.d.ts.map +1 -1
  147. package/dist/esm/internals/ReportBuilder/PivotModal.js +63 -18
  148. package/dist/esm/models/Client.d.ts +4 -5
  149. package/dist/esm/models/Client.d.ts.map +1 -1
  150. package/dist/esm/models/Pivot.d.ts +10 -0
  151. package/dist/esm/models/Pivot.d.ts.map +1 -1
  152. package/dist/esm/models/Report.d.ts +13 -1
  153. package/dist/esm/models/Report.d.ts.map +1 -1
  154. package/dist/esm/utils/astFilterProcessing.d.ts +1 -1
  155. package/dist/esm/utils/astFilterProcessing.d.ts.map +1 -1
  156. package/dist/esm/utils/astFilterProcessing.js +799 -64
  157. package/dist/esm/utils/astProcessing.d.ts +4 -1
  158. package/dist/esm/utils/astProcessing.d.ts.map +1 -1
  159. package/dist/esm/utils/astProcessing.js +2 -2
  160. package/dist/esm/utils/client.d.ts.map +1 -1
  161. package/dist/esm/utils/client.js +6 -3
  162. package/dist/esm/utils/columnProcessing.d.ts +1 -0
  163. package/dist/esm/utils/columnProcessing.d.ts.map +1 -1
  164. package/dist/esm/utils/columnProcessing.js +1 -1
  165. package/dist/esm/utils/dashboard.d.ts +2 -1
  166. package/dist/esm/utils/dashboard.d.ts.map +1 -1
  167. package/dist/esm/utils/dashboard.js +39 -11
  168. package/dist/esm/utils/filterProcessing.d.ts +1 -1
  169. package/dist/esm/utils/filterProcessing.d.ts.map +1 -1
  170. package/dist/esm/utils/merge.d.ts +16 -0
  171. package/dist/esm/utils/merge.d.ts.map +1 -1
  172. package/dist/esm/utils/merge.js +207 -0
  173. package/dist/esm/utils/paginationProcessing.d.ts +1 -1
  174. package/dist/esm/utils/paginationProcessing.d.ts.map +1 -1
  175. package/dist/esm/utils/paginationProcessing.js +3 -2
  176. package/dist/esm/utils/pivotConstructor.d.ts +19 -9
  177. package/dist/esm/utils/pivotConstructor.d.ts.map +1 -1
  178. package/dist/esm/utils/pivotConstructor.js +91 -35
  179. package/dist/esm/utils/queryConstructor.d.ts +8 -1
  180. package/dist/esm/utils/queryConstructor.d.ts.map +1 -1
  181. package/dist/esm/utils/queryConstructor.js +274 -314
  182. package/dist/esm/utils/report.d.ts +25 -12
  183. package/dist/esm/utils/report.d.ts.map +1 -1
  184. package/dist/esm/utils/report.js +13 -7
  185. package/dist/esm/utils/schema.d.ts +1 -1
  186. package/dist/esm/utils/schema.d.ts.map +1 -1
  187. package/dist/esm/utils/schema.js +1 -30
  188. package/dist/esm/utils/tableProcessing.d.ts +43 -13
  189. package/dist/esm/utils/tableProcessing.d.ts.map +1 -1
  190. package/dist/esm/utils/tableProcessing.js +140 -75
  191. package/dist/esm/utils/textProcessing.d.ts.map +1 -1
  192. package/dist/esm/utils/textProcessing.js +10 -1
  193. package/dist/esm/utils/valueFormatter.d.ts +2 -1
  194. package/dist/esm/utils/valueFormatter.d.ts.map +1 -1
  195. package/dist/esm/utils/valueFormatter.js +18 -14
  196. package/package.json +1 -1
@@ -2,13 +2,13 @@ import { isStringType } from './columnProcessing';
2
2
  import { MAX_PIVOT_UNIQUE_VALUES } from './constants';
3
3
  import { isValidPivot } from './pivotProcessing';
4
4
  import { matchCasing } from './textProcessing';
5
- function processSingleQuotes(value, databaseType) {
5
+ export function processSingleQuotes(value, databaseType) {
6
6
  if (['postgresql', 'snowflake', 'clickhouse'].includes(databaseType.toLowerCase())) {
7
7
  return value.replaceAll("'", "''");
8
8
  }
9
9
  return value.replaceAll("'", "\\'");
10
10
  }
11
- function processAggType(aggType, hasColumnField = false) {
11
+ export function processAggType(aggType, hasColumnField = false) {
12
12
  if (aggType === 'count' && hasColumnField)
13
13
  return 'SUM';
14
14
  return aggType?.toLowerCase() === 'average' ? 'AVG' : aggType?.toLowerCase();
@@ -84,6 +84,23 @@ isPivotColumnFieldAlias, isPivotValueFieldAlias) {
84
84
  }
85
85
  return `\`${replaceBigQuerySpecialCharacters(column.replaceAll('`', ''))}\``;
86
86
  }
87
+ case 'mssql': {
88
+ if (column === '') {
89
+ return fallbackOnNull ? `[${fallbackOnNull}]` : `[_]`;
90
+ }
91
+ if (isPivotColumnFieldAlias) {
92
+ return `[${column.replaceAll(']', '').replaceAll('[', '')}]`;
93
+ }
94
+ const columnParts = column.split('.');
95
+ if (columnParts.length > 1) {
96
+ return (`[` +
97
+ columnParts
98
+ .map((part) => part.replaceAll(']', '').replaceAll('[', ''))
99
+ .join('].[`') +
100
+ `]`);
101
+ }
102
+ return `[${column.replaceAll(']', '').replaceAll('[', '')}]`;
103
+ }
87
104
  default:
88
105
  return column;
89
106
  }
@@ -100,31 +117,37 @@ function processInterval(interval, rowField, databaseType) {
100
117
  }
101
118
  return `TIMESTAMP_ADD(${processColumnReference(rowField, databaseType)}, INTERVAL ${interval} )`;
102
119
  }
103
- function processDateTrunc(dateBucket, rowField, databaseType, comparisonInterval) {
120
+ export function processDateTrunc(dateBucket, rowField, databaseType, comparisonInterval) {
121
+ const dateField = comparisonInterval
122
+ ? processInterval(comparisonInterval, rowField, databaseType)
123
+ : processColumnReference(rowField, databaseType);
104
124
  if (['postgresql', 'snowflake'].includes(databaseType.toLowerCase())) {
105
- const dateField = comparisonInterval
106
- ? processInterval(comparisonInterval, rowField, databaseType)
107
- : processColumnReference(rowField, databaseType);
108
125
  return `date_trunc('${dateBucket}', ${dateField})`;
109
126
  }
110
127
  if (['clickhouse'].includes(databaseType.toLowerCase())) {
111
- const dateField = comparisonInterval
112
- ? processInterval(comparisonInterval, rowField, databaseType)
113
- : processColumnReference(rowField, databaseType);
114
128
  return `dateTrunc('${dateBucket}', ${dateField})`;
115
129
  }
116
130
  if (['mysql'].includes(databaseType.toLowerCase())) {
117
- const dateField = comparisonInterval
118
- ? processInterval(comparisonInterval, rowField, databaseType)
119
- : processColumnReference(rowField, databaseType);
120
- return `extract(${dateBucket} from ${dateField})`;
131
+ switch (dateBucket.toLowerCase()) {
132
+ case 'year':
133
+ return `DATE_FORMAT(${dateField}, '%Y-01-01 00:00:00')`;
134
+ case 'month':
135
+ return `DATE_FORMAT(${dateField}, '%Y-%m-01 00:00:00')`;
136
+ case 'week':
137
+ return `DATE_FORMAT(${dateField}, '%Y-%U-1 00:00:00')`;
138
+ case 'day':
139
+ default:
140
+ return `DATE(${dateField})`;
141
+ }
142
+ }
143
+ if (['mssql'].includes(databaseType.toLowerCase())) {
144
+ // note DATETRUNC is for mssql >= 2022
145
+ return `DATETRUNC(${dateBucket}, ${dateField})`;
146
+ // return `FORMAT(${dateField}, '${mssqlDateBucketFormat(dateBucket)}')`;
121
147
  }
122
- const dateField = comparisonInterval
123
- ? processInterval(comparisonInterval, rowField, databaseType)
124
- : processColumnReference(rowField, databaseType);
125
148
  return `TIMESTAMP_TRUNC(${dateField}, ${dateBucket})`;
126
149
  }
127
- function processValueField(aggType, databaseType, valueField) {
150
+ export function processValueField(aggType, databaseType, valueField) {
128
151
  if (aggType === 'min' ||
129
152
  aggType === 'max' ||
130
153
  aggType?.toLowerCase() === 'average')
@@ -164,6 +187,8 @@ export function generateDistinctQuery(stringFields, query, databaseType) {
164
187
  return generateDistinctQueryPostgres(stringFields, query);
165
188
  case 'snowflake':
166
189
  return generateDistinctQuerySnowflake(stringFields, query);
190
+ case 'mssql':
191
+ return generateDistinctQueryMSSQL(stringFields, query);
167
192
  default:
168
193
  return generateDistinctQueryDefault(stringFields, query, databaseType);
169
194
  }
@@ -199,44 +224,73 @@ function generateDistinctQuerySnowflake(stringFields, query) {
199
224
  const distinctQuery = distinctQueries.join(' UNION ALL ');
200
225
  return `WITH querytable AS (${query.replace(';', '')}) ` + distinctQuery;
201
226
  }
227
+ // Not the ideal way to do things, but node-sql-parser doesn't support
228
+ // DISTINCT or JSON PATH for transactsql at the moment
229
+ // Note, this does not return an array like the other functions, so we need to keep that in mind
230
+ // for mssql distinct values when we attempt to parse them
231
+ function generateDistinctQueryMSSQL(stringFields, query) {
232
+ const escapeFieldName = (field) => field.replace(/'/g, "''");
233
+ const distinctQueries = stringFields.map((field) => {
234
+ return `
235
+ SELECT
236
+ '${escapeFieldName(field)}' AS field,
237
+ STRING_AGG(${field}, ',') AS string_values
238
+ FROM (
239
+ SELECT ${field}
240
+ FROM querytable
241
+ WHERE ${field} IS NOT NULL
242
+ GROUP BY ${field}
243
+ ) AS grouped
244
+ `
245
+ .replace(/\s+/g, ' ')
246
+ .trim();
247
+ });
248
+ const distinctQuery = distinctQueries.join(' UNION ALL ');
249
+ return `
250
+ WITH querytable AS (
251
+ ${query.replace(';', '')}
252
+ )
253
+ ${distinctQuery}`
254
+ .replace(/\s+/g, ' ')
255
+ .trim();
256
+ }
202
257
  export function generateMinMaxDateRangeQueries(columnFields, query, databaseType) {
203
258
  const max = 'MAX';
204
259
  const min = 'MIN';
260
+ const cast = databaseType.toLowerCase() === 'snowflake' ? '::TIMESTAMP_TZ' : '';
205
261
  const distinctQueries = columnFields.map((field) => {
206
262
  const wrappedField = ['postgresql', 'clickhouse'].includes(databaseType.toLowerCase())
207
263
  ? `"${field}"`
208
264
  : field;
209
- return `SELECT '${field}' AS field, ${min}(${wrappedField}) AS min_range, ${max}(${wrappedField}) AS max_range FROM querytable`;
265
+ return `SELECT '${field}' AS field, ${min}(${wrappedField})${cast} AS min_range, ${max}(${wrappedField})${cast} AS max_range FROM querytable`;
210
266
  });
211
267
  const distinctQuery = distinctQueries.join(' UNION ALL ');
212
268
  return `WITH querytable AS (${query.replace(';', '')}) ` + distinctQuery;
213
269
  }
214
- export function generatePivotQuery(pivot, itemQueries, databaseType, comparisonInterval, distinctStrings, dateBucket, rowLimit) {
270
+ export function generatePivotQuery(pivot, itemQuery, databaseType, distinctStrings, dateBucket) {
215
271
  if (!isValidPivot(pivot).valid) {
216
272
  return undefined;
217
273
  }
218
- if (pivot.columnField) {
219
- return create2DPivotQuery(pivot, itemQueries, databaseType, dateBucket, comparisonInterval, distinctStrings, rowLimit);
274
+ if (pivot.columnField && distinctStrings && distinctStrings.length > 0) {
275
+ return create2DPivotQuery(pivot, itemQuery, databaseType, distinctStrings, dateBucket);
220
276
  }
221
277
  if (pivot.rowField) {
222
- return create1DPivotQuery(pivot, itemQueries, dateBucket, databaseType, comparisonInterval, rowLimit);
278
+ return create1DPivotQuery(pivot, itemQuery, dateBucket, databaseType);
223
279
  }
224
- return createAggregationValuePivot(pivot, itemQueries, databaseType, rowLimit);
280
+ return createAggregationValuePivot(pivot, itemQuery, databaseType);
225
281
  }
226
- function create2DPivotQuery(pivot, itemQueries, databaseType, dateBucket, comparisonInterval, columnFieldValues, rowLimit) {
282
+ function create2DPivotQuery(pivot, itemQuery, databaseType, columnFieldValues, dateBucket) {
227
283
  if (!pivot || !pivot.columnField) {
228
284
  return undefined;
229
285
  }
230
- if (!columnFieldValues) {
231
- return undefined;
232
- }
233
286
  if (isStringType(pivot.rowFieldType || '') || !pivot.rowFieldType) {
234
- return create2DStringPivotQuery(pivot, itemQueries, columnFieldValues, databaseType, rowLimit);
287
+ return create2DStringPivotQuery(pivot, itemQuery, columnFieldValues, databaseType);
235
288
  }
236
- return create2DDatePivotQuery(pivot, itemQueries, columnFieldValues, databaseType, dateBucket, comparisonInterval, rowLimit);
289
+ return create2DDatePivotQuery(pivot, itemQuery, columnFieldValues, databaseType, dateBucket);
237
290
  }
238
- function create2DStringPivotQuery(pivot, itemQueries, columnFieldValues, databaseType, rowLimit) {
239
- if (!pivot.columnField || !pivot.rowField) {
291
+ function create2DStringPivotQuery(pivot, itemQuery, columnFieldValues, databaseType) {
292
+ const isValidBaseQuery = itemQuery.match(/SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/);
293
+ if (!isValidBaseQuery || !pivot.columnField || !pivot.rowField) {
240
294
  return undefined;
241
295
  }
242
296
  const rowField = pivot.rowField;
@@ -245,97 +299,44 @@ function create2DStringPivotQuery(pivot, itemQueries, columnFieldValues, databas
245
299
  const rowFieldAlias = processColumnReference(rowField, databaseType, undefined, false, true);
246
300
  const valueFieldAlias = processColumnReference(valueField ?? rowField, databaseType, undefined, false, true);
247
301
  const columnFieldAlias = processColumnReference(columnField, databaseType, undefined, false, true);
248
- const max = 'GREATEST';
249
302
  const valueAliasSubstring = valueField
250
303
  ? `${processColumnReference(valueField, databaseType, undefined, true)} AS ${valueFieldAlias},`
251
304
  : '';
305
+ let value2AliasSubstring = '';
306
+ let caseWhens = columnFieldValues.map((column) => {
307
+ return `${processAggType(pivot.aggregationType, true)}(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(column, databaseType)}' THEN ${processValueField(pivot.aggregationType, databaseType, valueFieldAlias)} END) AS ${processColumnReference(column, databaseType, '_', true)}`;
308
+ });
252
309
  if (pivot.aggregationType === 'percentage' && pivot.valueField2) {
253
310
  const valueField2Alias = processColumnReference(pivot.valueField2, databaseType, undefined, false, true);
254
- const value2AliasSubstring = `${processColumnReference(pivot.valueField2, databaseType, undefined, true)} AS ${valueField2Alias},`;
255
311
  if (pivot.valueField === pivot.valueField2) {
256
- const caseWhens = columnFieldValues.map((column) => {
257
- return `sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(column, databaseType)}' THEN ${processValueField(pivot.aggregationType, databaseType, valueFieldAlias)} END) / ${max}(sum(${processColumnReference(pivot.valueField2, databaseType)}), 1) AS ${processColumnReference(column, databaseType, '_', true)}`;
312
+ caseWhens = columnFieldValues.map((column) => {
313
+ return `sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(column, databaseType)}' THEN ${processValueField(pivot.aggregationType, databaseType, valueFieldAlias)} END) / GREATEST(sum(${processColumnReference(pivot.valueField2, databaseType)}), 1) AS ${processColumnReference(column, databaseType, '_', true)}`;
258
314
  });
259
- const caseWhensCompare = columnFieldValues.map((column) => {
260
- return `sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(column, databaseType)}' THEN ${processValueField(pivot.aggregationType, databaseType, valueFieldAlias)}) / ${max}(sum(${processColumnReference(pivot.valueField2, databaseType)}), 1) AS ${processColumnReference(`comparison_${column}`, databaseType, '_', true)}`;
315
+ }
316
+ else {
317
+ value2AliasSubstring = `${processColumnReference(pivot.valueField2, databaseType, undefined, true)} AS ${valueField2Alias},`;
318
+ caseWhens = columnFieldValues.map((column) => {
319
+ return `sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(column, databaseType)}' THEN ${processValueField(pivot.aggregationType, databaseType, valueFieldAlias)} END) / GREATEST(sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(column, databaseType)}' THEN ${processValueField(pivot.aggregationType, databaseType, valueField2Alias)} END), 1) AS ${processColumnReference(column, databaseType, '_', true)}`;
261
320
  });
262
- const compareCTE = itemQueries[1]
263
- ? `, quill_ct as (${itemQueries[1]?.replaceAll(';', '')})`
264
- : '';
265
- const aliasCTE = itemQueries[1]
266
- ? `, quill_c_alias as (SELECT ${processColumnReference(rowField, databaseType, undefined, true)} AS ${rowFieldAlias}, ${valueAliasSubstring}
267
- ${processColumnReference(columnField, databaseType, undefined, true)} AS ${columnFieldAlias} FROM quill_ct)`
268
- : '';
269
- const compareCaseWhenCTE = itemQueries[1]
270
- ? `, quill_ct_cw as (SELECT ${rowFieldAlias} as ${processColumnReference(`comparison_${rowFieldAlias}`, databaseType)}${caseWhensCompare.length > 0 ? `, ${caseWhensCompare.join(', ')}` : ''} FROM quill_c_alias GROUP BY ${rowFieldAlias})`
271
- : '';
272
- const joinQuery = itemQueries[1]
273
- ? `JOIN quill_ct_cw ct ON ct.${processColumnReference(`comparison_${rowFieldAlias}`, databaseType)} = qt.${rowFieldAlias}`
274
- : '';
275
- return `WITH quill_qt AS (${itemQueries[0]?.replaceAll(';', '')}),
276
- quill_alias AS (SELECT ${processColumnReference(rowField, databaseType, undefined, true)} AS ${rowFieldAlias}, ${valueAliasSubstring}
277
- ${processColumnReference(columnField, databaseType, undefined, true)} AS ${columnFieldAlias} FROM quill_qt),
278
- quill_qt_cw AS (SELECT ${rowFieldAlias}${caseWhens.length > 0 ? `, ${caseWhens.join(', ')}` : ''} FROM quill_alias GROUP BY ${rowFieldAlias})
279
- ${compareCTE} ${aliasCTE} ${compareCaseWhenCTE}
280
- SELECT * FROM quill_qt_cw qt
281
- ${joinQuery}${rowLimit ? ` LIMIT ${rowLimit}` : ''}`;
282
321
  }
283
- const caseWhens = columnFieldValues.map((column) => {
284
- return `sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(column, databaseType)}' THEN ${processValueField(pivot.aggregationType, databaseType, valueFieldAlias)} END) / ${max}(sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(column, databaseType)}' THEN ${processValueField(pivot.aggregationType, databaseType, valueField2Alias)} END), 1) AS ${processColumnReference(column, databaseType, '_', true)}`;
285
- });
286
- const caseWhensCompare = columnFieldValues.map((column) => {
287
- return `sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(column, databaseType)}' THEN ${processValueField(pivot.aggregationType, databaseType, valueFieldAlias)} END) / ${max}(sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(column, databaseType)}' THEN ${processValueField(pivot.aggregationType, databaseType, valueField2Alias)} END), 1) AS ${processColumnReference(`comparison_${column}`, databaseType, '_', true)}`;
288
- });
289
- const compareCTE = itemQueries[1]
290
- ? `, quill_ct as (${itemQueries[1]?.replaceAll(';', '')})`
291
- : '';
292
- const aliasCTE = itemQueries[1]
293
- ? `, quill_c_alias as (SELECT ${processColumnReference(rowField, databaseType, undefined, true)} AS ${rowFieldAlias}, ${valueAliasSubstring} ${value2AliasSubstring}
294
- ${processColumnReference(columnField, databaseType, undefined, true)} AS ${columnFieldAlias} FROM quill_ct)`
295
- : '';
296
- const compareCaseWhenCTE = itemQueries[1]
297
- ? `, quill_ct_cw as (SELECT ${rowFieldAlias} as ${processColumnReference(`comparison_${rowFieldAlias}`, databaseType)}${caseWhensCompare.length > 0 ? `, ${caseWhensCompare.join(', ')}` : ''} FROM quill_c_alias GROUP BY ${rowFieldAlias})`
298
- : '';
299
- const joinQuery = itemQueries[1]
300
- ? `JOIN quill_ct_cw ct ON ct.${processColumnReference(`comparison_${rowFieldAlias}`, databaseType)} = qt.${rowFieldAlias}`
301
- : '';
302
- return `WITH quill_qt AS (${itemQueries[0]?.replaceAll(';', '')}),
303
- quill_alias AS (SELECT ${processColumnReference(rowField, databaseType, undefined, true)} AS ${rowFieldAlias}, ${valueAliasSubstring} ${value2AliasSubstring}
304
- ${processColumnReference(columnField, databaseType, undefined, true)} AS ${columnFieldAlias} FROM quill_qt),
305
- quill_qt_cw AS (SELECT ${rowFieldAlias}${caseWhens.length > 0 ? `, ${caseWhens.join(', ')}` : ''} FROM quill_alias GROUP BY ${rowFieldAlias})
306
- ${compareCTE} ${aliasCTE} ${compareCaseWhenCTE}
307
- SELECT * FROM quill_qt_cw qt
308
- ${joinQuery}${rowLimit ? ` LIMIT ${rowLimit}` : ''}`;
309
322
  }
310
- const caseWhens = columnFieldValues.map((column) => {
311
- return `${processAggType(pivot.aggregationType, true)}(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(column, databaseType)}' THEN ${processValueField(pivot.aggregationType, databaseType, valueFieldAlias)} END) AS ${processColumnReference(column, databaseType, '_', true)}`;
312
- });
313
- const caseWhensCompare = columnFieldValues.map((column) => {
314
- return `${processAggType(pivot.aggregationType, true)}(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(column, databaseType)}' THEN ${processValueField(pivot.aggregationType, databaseType, valueFieldAlias)} END) AS ${processColumnReference(`comparison_${column}`, databaseType, '_', true)}`;
315
- });
316
- const compareCTE = itemQueries[1]
317
- ? `, quill_ct as (${itemQueries[1]?.replaceAll(';', '')})`
318
- : '';
319
- const aliasCTE = itemQueries[1]
320
- ? `, quill_c_alias as (SELECT ${processColumnReference(rowField, databaseType, undefined, true)} AS ${rowFieldAlias}, ${valueAliasSubstring}
321
- ${processColumnReference(columnField, databaseType, undefined, true)} AS ${columnFieldAlias} FROM quill_ct)`
322
- : '';
323
- const compareCaseWhenCTE = itemQueries[1]
324
- ? `, quill_ct_cw as (SELECT ${rowFieldAlias} as ${processColumnReference(`comparison_${rowFieldAlias}`, databaseType)}${caseWhensCompare.length > 0 ? `, ${caseWhensCompare.join(', ')}` : ''} FROM quill_c_alias GROUP BY ${rowFieldAlias})`
325
- : '';
326
- const joinQuery = itemQueries[1]
327
- ? `JOIN quill_ct_cw ct ON ct.${processColumnReference(`comparison_${rowFieldAlias}`, databaseType)} = qt.${rowFieldAlias}`
328
- : '';
329
- return `WITH quill_qt AS (${itemQueries[0]?.replaceAll(';', '')}),
330
- quill_alias AS (SELECT ${processColumnReference(rowField, databaseType, undefined, true)} AS ${rowFieldAlias}, ${valueAliasSubstring}
331
- ${processColumnReference(columnField, databaseType, undefined, true)} AS ${columnFieldAlias} FROM quill_qt),
332
- quill_qt_cw AS (SELECT ${rowFieldAlias}${caseWhens.length > 0 ? `, ${caseWhens.join(', ')}` : ''} FROM quill_alias GROUP BY ${rowFieldAlias})
333
- ${compareCTE} ${aliasCTE} ${compareCaseWhenCTE}
334
- SELECT * FROM quill_qt_cw qt
335
- ${joinQuery}${rowLimit ? ` LIMIT ${rowLimit}` : ''}`;
323
+ // pivot sort matters in the base query when there is a rowLimit. In mssql, an orderby must be accompanied by a limit in a subquery and not allowed in a cte
324
+ const sortQuery = `${pivot.sort && pivot.sortField && pivot.rowLimit ? ` ORDER BY ${processColumnReference(pivot.sortField, databaseType)} ${pivot.sortDirection || ''} ` : ''}`;
325
+ const pivotQuery = `
326
+ ,quill_alias AS (SELECT ${processColumnReference(rowField, databaseType, undefined, true)} AS ${rowFieldAlias}, ${valueAliasSubstring} ${value2AliasSubstring}
327
+ ${processColumnReference(columnField, databaseType, undefined, true)} AS ${columnFieldAlias} FROM quill_base_table),
328
+ quill_qt_cw AS (SELECT ${rowFieldAlias}${caseWhens.length > 0 ? `, ${caseWhens.join(', ')}` : ''} FROM quill_alias GROUP BY ${rowFieldAlias}),
329
+ quill_base_pivot AS (SELECT ${pivot.rowLimit && databaseType.toLowerCase() === 'mssql' ? `TOP ${pivot.rowLimit}` : ''} * FROM quill_qt_cw qt
330
+ ${sortQuery}${pivot.rowLimit && databaseType.toLowerCase() !== 'mssql' ? ` LIMIT ${pivot.rowLimit}` : ''})
331
+ SELECT * FROM quill_base_pivot
332
+ `
333
+ .replace(/\s+/g, ' ')
334
+ .trim();
335
+ return itemQuery.replace(/SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/, pivotQuery);
336
336
  }
337
- function create2DDatePivotQuery(pivot, itemQueries, columnFieldValues, databaseType, dateBucket = 'month', comparisonInterval, rowLimit) {
338
- if (!pivot.columnField || !pivot.rowField) {
337
+ function create2DDatePivotQuery(pivot, itemQuery, columnFieldValues, databaseType, dateBucket = 'month') {
338
+ const isValidBaseQuery = itemQuery.match(/SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/);
339
+ if (!isValidBaseQuery || !pivot.columnField || !pivot.rowField) {
339
340
  return undefined;
340
341
  }
341
342
  const rowField = pivot.rowField;
@@ -346,246 +347,205 @@ function create2DDatePivotQuery(pivot, itemQueries, columnFieldValues, databaseT
346
347
  const valueAliasSubstring = pivot.valueField
347
348
  ? `${processColumnReference(pivot.valueField, databaseType, undefined, true)} AS ${valueFieldAlias},`
348
349
  : '';
349
- const max = 'GREATEST';
350
+ let value2AliasSubstring = '';
351
+ let caseWhens = columnFieldValues.map((column) => {
352
+ return `${processAggType(pivot.aggregationType, true)}(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(column, databaseType)}' THEN ${processValueField(pivot.aggregationType, databaseType, valueFieldAlias)} END) AS ${processColumnReference(column, databaseType, '_', true)}`;
353
+ });
350
354
  if (pivot.aggregationType === 'percentage' && pivot.valueField2) {
351
355
  const valueField2Alias = processColumnReference(pivot.valueField2, databaseType, undefined, false, true);
352
- const value2AliasSubstring = `${processColumnReference(pivot.valueField2, databaseType, undefined, true)} AS ${valueField2Alias},`;
353
356
  // edge case. if the user picks amount and amount, we assume they want a pie chart like breakdown of amount. so the summation of valueField2 has to be moved outside of the case when
354
357
  if (pivot.valueField === pivot.valueField2) {
355
- const caseWhens = columnFieldValues.map((column) => {
356
- return `sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(column, databaseType)}' THEN ${processValueField(pivot.aggregationType, databaseType, valueFieldAlias)} END) / ${max}(sum(${processColumnReference(valueField2Alias, databaseType)}), 1) AS ${processColumnReference(column, databaseType, '_', true)}`;
358
+ caseWhens = columnFieldValues.map((column) => {
359
+ return `sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(column, databaseType)}' THEN ${processValueField(pivot.aggregationType, databaseType, valueFieldAlias)} END) / GREATEST(sum(${processColumnReference(valueField2Alias, databaseType)}), 1) AS ${processColumnReference(column, databaseType, '_', true)}`;
357
360
  });
358
- const caseWhensCompare = columnFieldValues.map((column) => {
359
- return `sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(column, databaseType)}' THEN ${processValueField(pivot.aggregationType, databaseType, valueFieldAlias)} END) / ${max}(sum(${processColumnReference(valueField2Alias, databaseType)}), 1) AS ${processColumnReference(`comparison_${column}`, databaseType, '_', true)}`;
361
+ }
362
+ else {
363
+ value2AliasSubstring = `${processColumnReference(pivot.valueField2, databaseType, undefined, true)} AS ${valueField2Alias},`;
364
+ caseWhens = columnFieldValues.map((column) => {
365
+ return `sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(column, databaseType)}' THEN ${processValueField(pivot.aggregationType, databaseType, valueFieldAlias)} END) / GREATEST(sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(column, databaseType)}' THEN ${processValueField(pivot.aggregationType, databaseType, valueField2Alias)} END), 1) AS ${processColumnReference(column, databaseType, '_', true)}`;
360
366
  });
361
- const compareCTE = itemQueries[1] && comparisonInterval
362
- ? `, quill_ct as (${itemQueries[1]?.replaceAll(';', '')})`
363
- : '';
364
- const aliasCTE = itemQueries[1] && comparisonInterval
365
- ? `, quill_c_alias as (SELECT ${processColumnReference(rowField, databaseType, undefined, true)} AS ${rowFieldAlias}, ${valueAliasSubstring}
366
- ${processColumnReference(columnField, databaseType, undefined, true)} AS ${columnFieldAlias} FROM quill_ct)`
367
- : '';
368
- const compareCaseWhenCTE = itemQueries[1] && comparisonInterval
369
- ? `, quill_ct_cw as (SELECT ${processDateTrunc(dateBucket, rowField, databaseType)} as ${processColumnReference(`comparison_${rowFieldAlias}`, databaseType)}${caseWhensCompare.length > 0 ? `, ${caseWhensCompare.join(', ')}` : ''} FROM quill_c_alias GROUP BY ${databaseType.toLowerCase() === 'clickhouse' ? processColumnReference(`${rowField}`, databaseType) : processDateTrunc(dateBucket, rowFieldAlias, databaseType)})`
370
- : '';
371
- const joinQuery = itemQueries[1] && comparisonInterval
372
- ? `LEFT JOIN quill_ct_cw ${processColumnReference(`ct`, databaseType)} ON qt.${rowFieldAlias} = ${processDateTrunc(dateBucket, `ct.comparison_${rowFieldAlias}`, databaseType, comparisonInterval)}`
373
- : '';
374
- return `WITH quill_query_table AS (${itemQueries[0]?.replaceAll(';', '')}),
375
- quill_alias AS (SELECT ${processColumnReference(`${rowField}`, databaseType, undefined, true)} AS ${rowFieldAlias}, ${valueAliasSubstring}
376
- ${processColumnReference(columnField, databaseType, undefined, true, true)} AS ${columnFieldAlias} FROM quill_query_table),
377
- quill_qt_agg as (SELECT ${processDateTrunc(dateBucket, rowFieldAlias, databaseType)} as ${rowFieldAlias}${caseWhens.length > 0 ? `, ${caseWhens.join(', ')}` : ''} FROM quill_alias GROUP BY ${databaseType.toLowerCase() === 'clickhouse' ? processColumnReference(`${rowField}`, databaseType) : processDateTrunc(dateBucket, rowFieldAlias, databaseType)})
378
- ${compareCTE} ${aliasCTE} ${compareCaseWhenCTE}
379
- SELECT * FROM quill_qt_agg qt
380
- ${joinQuery} ORDER BY qt.${rowFieldAlias}${rowLimit ? ` LIMIT ${rowLimit}` : ''}`;
381
367
  }
382
- const caseWhens = columnFieldValues.map((column) => {
383
- return `sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(column, databaseType)}' THEN ${processValueField(pivot.aggregationType, databaseType, valueFieldAlias)} END) / ${max}(sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(column, databaseType)}' THEN ${processValueField(pivot.aggregationType, databaseType, valueField2Alias)} END), 1) AS ${processColumnReference(column, databaseType, '_', true)}`;
384
- });
385
- const caseWhensCompare = columnFieldValues.map((column) => {
386
- return `sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(column, databaseType)}' THEN ${processValueField(pivot.aggregationType, databaseType, valueFieldAlias)} END) / ${max}(sum(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(column, databaseType)}' THEN ${processValueField(pivot.aggregationType, databaseType, valueField2Alias)} END), 1) AS ${processColumnReference(`comparison_${column}`, databaseType, '_', true)}`;
387
- });
388
- const compareCTE = itemQueries[1] && comparisonInterval
389
- ? `, quill_ct as (${itemQueries[1]?.replaceAll(';', '')})`
390
- : '';
391
- const aliasCTE = itemQueries[1] && comparisonInterval
392
- ? `, quill_c_alias as (SELECT ${processColumnReference(rowField, databaseType, undefined, true)} AS ${rowFieldAlias}, ${valueAliasSubstring} ${value2AliasSubstring}
393
- ${processColumnReference(columnField, databaseType, undefined, true)} AS ${columnFieldAlias} FROM quill_ct)`
394
- : '';
395
- const compareCaseWhenCTE = itemQueries[1] && comparisonInterval
396
- ? `, quill_ct_cw as (SELECT ${processDateTrunc(dateBucket, rowField, databaseType)} as ${processColumnReference(`comparison_${rowFieldAlias}`, databaseType)}${caseWhensCompare.length > 0 ? `, ${caseWhensCompare.join(', ')}` : ''} FROM quill_c_alias GROUP BY ${databaseType.toLowerCase() === 'clickhouse' ? processColumnReference(`${rowField}`, databaseType) : processDateTrunc(dateBucket, rowFieldAlias, databaseType)})`
397
- : '';
398
- const joinQuery = itemQueries[1] && comparisonInterval
399
- ? `LEFT JOIN quill_ct_cw ${processColumnReference(`ct`, databaseType)} ON qt.${rowFieldAlias} = ${processDateTrunc(dateBucket, `ct.comparison_${rowFieldAlias}`, databaseType, comparisonInterval)}`
400
- : '';
401
- return `WITH quill_query_table AS (${itemQueries[0]?.replaceAll(';', '')}),
402
- quill_alias AS (SELECT ${processColumnReference(`${rowField}`, databaseType, undefined, true)} AS ${rowFieldAlias}, ${valueAliasSubstring} ${value2AliasSubstring}
403
- ${processColumnReference(columnField, databaseType, undefined, true, true)} AS ${columnFieldAlias} FROM quill_query_table),
404
- quill_qt_agg as (SELECT ${processDateTrunc(dateBucket, rowFieldAlias, databaseType)} as ${rowFieldAlias}${caseWhens.length > 0 ? `, ${caseWhens.join(', ')}` : ''} FROM quill_alias GROUP BY ${databaseType.toLowerCase() === 'clickhouse' ? processColumnReference(`${rowField}`, databaseType) : processDateTrunc(dateBucket, rowFieldAlias, databaseType)})
405
- ${compareCTE} ${aliasCTE} ${compareCaseWhenCTE}
406
- SELECT * FROM quill_qt_agg qt
407
- ${joinQuery} ORDER BY qt.${rowFieldAlias}${rowLimit ? ` LIMIT ${rowLimit}` : ''}`;
408
368
  }
409
- const caseWhens = columnFieldValues.map((column) => {
410
- return `${processAggType(pivot.aggregationType, true)}(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(column, databaseType)}' THEN ${processValueField(pivot.aggregationType, databaseType, valueFieldAlias)} END) AS ${processColumnReference(column, databaseType, '_', true)}`;
411
- });
412
- const caseWhensCompare = columnFieldValues.map((column) => {
413
- return `${processAggType(pivot.aggregationType, true)}(CASE WHEN ${columnFieldAlias} = '${processSingleQuotes(column, databaseType)}' THEN ${processValueField(pivot.aggregationType, databaseType, valueFieldAlias)} END) AS ${processColumnReference(`comparison_${column}`, databaseType, '_', true)}`;
414
- });
415
- const compareCTE = itemQueries[1] && comparisonInterval
416
- ? `, quill_ct as (${itemQueries[1]?.replaceAll(';', '')})`
417
- : '';
418
- const aliasCTE = itemQueries[1] && comparisonInterval
419
- ? `, quill_c_alias as (SELECT ${processColumnReference(rowField, databaseType, undefined, true)} AS ${rowFieldAlias}, ${valueAliasSubstring}
420
- ${processColumnReference(columnField, databaseType, undefined, true)} AS ${columnFieldAlias} FROM quill_ct)`
421
- : '';
422
- const compareCaseWhenCTE = itemQueries[1] && comparisonInterval
423
- ? `, quill_ct_cw as (SELECT ${processDateTrunc(dateBucket, rowField, databaseType)} as ${processColumnReference(`comparison_${rowFieldAlias}`, databaseType)}${caseWhensCompare.length > 0 ? `, ${caseWhensCompare.join(', ')}` : ''} FROM quill_c_alias GROUP BY ${databaseType.toLowerCase() === 'clickhouse' ? processColumnReference(`${rowField}`, databaseType) : processDateTrunc(dateBucket, rowFieldAlias, databaseType)})`
424
- : '';
425
- const joinQuery = itemQueries[1] && comparisonInterval
426
- ? `LEFT JOIN quill_ct_cw ${processColumnReference(`ct`, databaseType)} ON qt.${rowFieldAlias} = ${processDateTrunc(dateBucket, `ct.comparison_${rowFieldAlias}`, databaseType, comparisonInterval)}`
427
- : '';
428
- return `WITH quill_query_table AS (${itemQueries[0]?.replaceAll(';', '')}),
429
- quill_alias AS (SELECT ${processColumnReference(`${rowField}`, databaseType, undefined, true)} AS ${rowFieldAlias}, ${valueAliasSubstring}
430
- ${processColumnReference(columnField, databaseType, undefined, true, true)} AS ${columnFieldAlias} FROM quill_query_table),
431
- quill_qt_agg as (SELECT ${processDateTrunc(dateBucket, rowFieldAlias, databaseType)} as ${rowFieldAlias}${caseWhens.length > 0 ? `, ${caseWhens.join(', ')}` : ''} FROM quill_alias GROUP BY ${databaseType.toLowerCase() === 'clickhouse' ? processColumnReference(`${rowField}`, databaseType) : processDateTrunc(dateBucket, rowFieldAlias, databaseType)})
432
- ${compareCTE} ${aliasCTE} ${compareCaseWhenCTE}
433
- SELECT * FROM quill_qt_agg qt
434
- ${joinQuery} ORDER BY qt.${rowFieldAlias}${rowLimit ? ` LIMIT ${rowLimit}` : ''}`;
369
+ // pivot sort matters in the base query when there is a rowLimit. In mssql, an orderby must be accompanied by a limit in a subquery and not allowed in a cte
370
+ const sortQuery = `${pivot.sort && pivot.sortField && pivot.rowLimit ? ` ORDER BY ${processColumnReference(pivot.sortField, databaseType)} ${pivot.sortDirection || ''} ` : ''}`;
371
+ const pivotQuery = `
372
+ , quill_alias AS (SELECT ${processColumnReference(`${rowField}`, databaseType, undefined, true)} AS ${rowFieldAlias}, ${valueAliasSubstring} ${value2AliasSubstring}
373
+ ${processColumnReference(columnField, databaseType, undefined, true, true)} AS ${columnFieldAlias} FROM quill_base_table),
374
+ quill_qt_agg AS (SELECT ${processDateTrunc(dateBucket, rowFieldAlias, databaseType)} as ${rowFieldAlias}${caseWhens.length > 0 ? `, ${caseWhens.join(', ')}` : ''} FROM quill_alias GROUP BY ${databaseType.toLowerCase() === 'clickhouse' ? processColumnReference(`${rowField}`, databaseType) : processDateTrunc(dateBucket, rowFieldAlias, databaseType)}),
375
+ quill_base_pivot AS (SELECT ${pivot.rowLimit && databaseType.toLowerCase() === 'mssql' ? `TOP ${pivot.rowLimit}` : ''} * FROM quill_qt_agg qt
376
+ ${sortQuery}${pivot.rowLimit && databaseType.toLowerCase() !== 'mssql' ? ` LIMIT ${pivot.rowLimit}` : ''})
377
+ SELECT * FROM quill_base_pivot
378
+ `
379
+ .replace(/\s+/g, ' ')
380
+ .trim();
381
+ return itemQuery.replace(/SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/, pivotQuery);
435
382
  }
436
- function create1DPivotQuery(pivot, itemQueries, dateBucket = 'month', databaseType, comparisonInterval, rowLimit) {
383
+ function create1DPivotQuery(pivot, itemQuery, dateBucket = 'month', databaseType) {
437
384
  if (isStringType(pivot.rowFieldType || '') || !pivot.rowFieldType) {
438
- return create1DStringPivotQuery(pivot, itemQueries, databaseType, rowLimit);
385
+ return create1DStringPivotQuery(pivot, itemQuery, databaseType);
439
386
  }
440
- return create1DDatePivotQuery(pivot, itemQueries, dateBucket, databaseType, comparisonInterval, rowLimit);
387
+ return create1DDatePivotQuery(pivot, itemQuery, dateBucket, databaseType);
441
388
  }
442
- function create1DStringPivotQuery(pivot, itemQueries, databaseType, rowLimit) {
389
+ // we have an item query that is guaranteed to end in SELECT * FROM [quill_base_table]
390
+ // also, stop fetching the comparison rows in the same query, since we're no longer wrapping everything in
391
+ // their own with statements and withs with the same name will conflict
392
+ function create1DStringPivotQuery(pivot, itemQuery, databaseType) {
393
+ const isValidBaseQuery = itemQuery.match(/SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/);
394
+ if (!isValidBaseQuery) {
395
+ return undefined;
396
+ }
443
397
  const rowField = pivot.rowField;
444
398
  const valueField = pivot.valueField;
445
399
  const rowAlias = processColumnReference(rowField, databaseType, undefined, false, true);
446
400
  const valueAlias = processColumnReference(valueField ?? rowField, databaseType, undefined, false, true);
447
401
  const countAlias = processColumnReference('count', databaseType);
448
- const max = 'GREATEST';
449
402
  const valueAliasSubstring = valueField
450
403
  ? `, ${processColumnReference(`${valueField}`, databaseType, undefined, true)} AS ${valueAlias}`
451
404
  : ``;
405
+ let value2AliasSubstring = '';
406
+ let quillSelectSubstring = `${rowAlias}, ${processAggType(pivot.aggregationType)}(${valueField ? valueAlias : '*'}) as ${valueField ? valueAlias : countAlias}`;
452
407
  if (pivot.aggregationType === 'percentage' && pivot.valueField2) {
453
408
  const value2Alias = processColumnReference(pivot.valueField2, databaseType, undefined, false, true);
454
- const value2AliasSubstring = valueField && valueField !== pivot.valueField2
455
- ? `, ${processColumnReference(`${pivot.valueField2}`, databaseType, undefined, true)} AS ${value2Alias}`
456
- : ``;
457
- const compareCTE = itemQueries[1]
458
- ? `, quill_ct as (${itemQueries[1]?.replaceAll(';', '')})`
459
- : '';
460
- const aliasCTE = itemQueries[1]
461
- ? `, quill_c_alias as (SELECT ${processColumnReference(rowField, databaseType, undefined, true)} AS ${rowAlias}${valueAliasSubstring}${value2AliasSubstring} FROM quill_ct)`
462
- : '';
463
- const compareCaseWhenCTE = itemQueries[1]
464
- ? `, quill_ct_cw as (SELECT ${rowAlias} as ${processColumnReference(`comparison_${rowAlias}`, databaseType)}, ${processAggType(pivot.aggregationType)}(${valueField ? valueAlias : '*'}) as ${processColumnReference(`comparison_${valueField ? valueAlias : countAlias}`, databaseType)} FROM quill_c_alias GROUP BY ${rowAlias})`
465
- : '';
466
- const joinQuery = itemQueries[1]
467
- ? `JOIN quill_ct_cw ct ON ct.${processColumnReference(`comparison_${rowField}`, databaseType)} = qt.${processColumnReference(`${rowField}`, databaseType)}`
468
- : '';
469
- return `WITH quill_qt AS (${itemQueries[0]?.replaceAll(';', '')}),
470
- quill_alias AS (SELECT ${processColumnReference(`${rowField}`, databaseType, undefined, true)} AS ${rowAlias}${valueAliasSubstring}${value2AliasSubstring} FROM quill_qt),
471
- quill_qt_cw AS (SELECT ${rowAlias}, sum(${valueAlias}) / ${max}(sum(${value2Alias}), 1) as ${processColumnReference(`${valueField ?? ''}_${matchCasing('percentage', valueField)}`, databaseType, undefined, false, true)} FROM quill_alias GROUP BY ${rowAlias})
472
- ${compareCTE} ${aliasCTE} ${compareCaseWhenCTE}
473
- SELECT * FROM quill_qt_cw qt
474
- ${joinQuery}${rowLimit ? ` LIMIT ${rowLimit}` : ''}`;
409
+ if (pivot.valueField !== pivot.valueField2) {
410
+ value2AliasSubstring =
411
+ valueField && valueField !== pivot.valueField2
412
+ ? `, ${processColumnReference(`${pivot.valueField2}`, databaseType, undefined, true)} AS ${value2Alias}`
413
+ : '';
414
+ }
415
+ quillSelectSubstring = `${rowAlias}, sum(${valueAlias}) / GREATEST(sum(${value2Alias}), 1) as ${processColumnReference(`${valueField ?? ''}_${matchCasing('percentage', valueField)}`, databaseType, undefined, false, true)}`;
475
416
  }
476
- const compareCTE = itemQueries[1]
477
- ? `, quill_ct as (${itemQueries[1]?.replaceAll(';', '')})`
478
- : '';
479
- const aliasCTE = itemQueries[1]
480
- ? `, quill_c_alias as (SELECT ${processColumnReference(rowField, databaseType, undefined, true)} AS ${rowAlias}${valueAliasSubstring} FROM quill_ct)`
481
- : '';
482
- const compareCaseWhenCTE = itemQueries[1]
483
- ? `, quill_ct_cw as (SELECT ${rowAlias} as ${processColumnReference(`comparison_${rowAlias}`, databaseType)}, ${processAggType(pivot.aggregationType)}(${valueField ? valueAlias : '*'}) as ${processColumnReference(`comparison_${valueField ? valueAlias : countAlias}`, databaseType)} FROM quill_c_alias GROUP BY ${rowAlias})`
484
- : '';
485
- const joinQuery = itemQueries[1]
486
- ? `JOIN quill_ct_cw ct ON ct.${processColumnReference(`comparison_${rowField}`, databaseType)} = qt.${processColumnReference(`${rowField}`, databaseType)}`
487
- : '';
488
- return `WITH quill_qt AS (${itemQueries[0]?.replaceAll(';', '')}),
489
- quill_alias AS (SELECT ${processColumnReference(`${rowField}`, databaseType, undefined, true)} AS ${rowAlias}${valueAliasSubstring} FROM quill_qt),
490
- quill_qt_cw AS (SELECT ${rowAlias}, ${processAggType(pivot.aggregationType)}(${valueField ? valueAlias : '*'}) as ${valueField ? valueAlias : countAlias} FROM quill_alias GROUP BY ${rowAlias})
491
- ${compareCTE} ${aliasCTE} ${compareCaseWhenCTE}
492
- SELECT * FROM quill_qt_cw qt
493
- ${joinQuery}${rowLimit ? ` LIMIT ${rowLimit}` : ''}`;
417
+ // pivot sort matters in the base query when there is a rowLimit. In mssql, an orderby must be accompanied by a limit in a subquery and not allowed in a cte
418
+ const sortQuery = `${pivot.sort && pivot.sortField && pivot.rowLimit ? ` ORDER BY ${processColumnReference(pivot.sortField, databaseType)} ${pivot.sortDirection || ''} ` : ''}`;
419
+ const pivotQuery = `, quill_alias AS (SELECT ${processColumnReference(`${rowField}`, databaseType, undefined, true)} AS ${rowAlias}${valueAliasSubstring}${value2AliasSubstring} FROM quill_base_table),
420
+ quill_qt_cw AS (SELECT ${quillSelectSubstring} FROM quill_alias GROUP BY ${rowAlias}),
421
+ quill_base_pivot AS (SELECT ${pivot.rowLimit && databaseType.toLowerCase() === 'mssql' ? `TOP ${pivot.rowLimit}` : ''} * FROM quill_qt_cw qt
422
+ ${sortQuery}${pivot.rowLimit && databaseType.toLowerCase() !== 'mssql' ? ` LIMIT ${pivot.rowLimit}` : ''})
423
+ SELECT * FROM quill_base_pivot
424
+ `
425
+ .replace(/\s+/g, ' ')
426
+ .trim();
427
+ return itemQuery.replace(/SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/, pivotQuery);
494
428
  }
495
- function create1DDatePivotQuery(pivot, itemQueries, dateBucket = 'month', databaseType, comparisonInterval, rowLimit) {
429
+ function create1DDatePivotQuery(pivot, itemQuery, dateBucket = 'month', databaseType) {
430
+ const isValidBaseQuery = itemQuery.match(/SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/);
431
+ if (!isValidBaseQuery) {
432
+ return undefined;
433
+ }
496
434
  const rowField = pivot.rowField || '';
497
435
  const valueField = pivot.valueField;
498
436
  const rowFieldAlias = processColumnReference(rowField, databaseType, undefined);
499
437
  const valueFieldAlias = processColumnReference(valueField ?? rowField, databaseType, undefined, false, true);
500
- const countAlias = processColumnReference('count', databaseType);
501
- const valueAliasSubstring = valueField
502
- ? `, ${processColumnReference(`${valueField}`, databaseType, undefined, true)} AS ${valueFieldAlias}`
503
- : ``;
504
- const max = 'GREATEST';
438
+ const comparisonValueFieldAlias = processColumnReference(valueField ?? rowField, databaseType, undefined, false, true);
439
+ const countAlias = processColumnReference(`count`, databaseType);
440
+ const valueAliasSubstring = valueField ? `, ${valueFieldAlias}` : ``;
441
+ let value2AliasSubstring = '';
442
+ let quillAggSubstring = `
443
+ ${processDateTrunc(dateBucket, rowFieldAlias, databaseType)} as ${processColumnReference(`${rowField}`, databaseType)}
444
+ , ${processAggType(pivot.aggregationType)}(${valueField ? valueFieldAlias : rowFieldAlias}) as ${valueField ? comparisonValueFieldAlias : countAlias}
445
+ `;
505
446
  // this "and" is for typescript. in reality, pivot.valueField2 must exist if pivot.aggregationType === 'percentage'
506
447
  if (pivot.aggregationType === 'percentage' && pivot.valueField2) {
507
448
  const valueField2Alias = processColumnReference(pivot.valueField2, databaseType, undefined, false, true);
508
- const value2AliasSubstring = valueField !== pivot.valueField2
509
- ? `, ${processColumnReference(`${pivot.valueField2}`, databaseType, undefined, true)} AS ${valueField2Alias}`
510
- : '';
511
- const compareCTE = itemQueries[1] && comparisonInterval
512
- ? `, quill_ct as (${itemQueries[1]?.replaceAll(';', '')})`
513
- : '';
514
- const aliasCTE = itemQueries[1] && comparisonInterval
515
- ? `, quill_c_alias as (SELECT ${processColumnReference(rowField, databaseType, undefined, true)} AS ${rowFieldAlias}${valueAliasSubstring}${value2AliasSubstring} FROM quill_ct)`
516
- : '';
517
- const compareCaseWhenCTE = itemQueries[1] && comparisonInterval
518
- ? `, quill_ct_cw as (SELECT ${processDateTrunc(dateBucket, rowFieldAlias, databaseType)} as ${processColumnReference(`comparison_${rowFieldAlias}`, databaseType)},
519
- sum(${processColumnReference(`${valueFieldAlias}`, databaseType, undefined, true)}) / ${max}(sum(${processColumnReference(`${valueField2Alias}`, databaseType, undefined, true)}), 1) as ${processColumnReference(`comparison_${valueField ? valueFieldAlias : 'count'}_${matchCasing('percentage', valueField)}`, databaseType, undefined, false, true)} FROM quill_c_alias GROUP BY ${processDateTrunc(dateBucket, rowFieldAlias, databaseType)})`
520
- : '';
521
- const joinQuery = itemQueries[1] && comparisonInterval
522
- ? `LEFT JOIN quill_ct_cw ${processColumnReference(`ct`, databaseType)} ON qt.${processColumnReference(rowField, databaseType)} = ${processDateTrunc(dateBucket, `ct.comparison_${rowField}`, databaseType, comparisonInterval)}`
523
- : '';
524
- return `WITH quill_qt AS (${itemQueries[0]?.replaceAll(';', '')}),
525
- quill_alias AS (SELECT ${processColumnReference(`${rowField}`, databaseType, undefined, true)} AS ${rowFieldAlias}${valueAliasSubstring}${value2AliasSubstring} FROM quill_qt),
526
- quill_qt_agg AS (SELECT ${processDateTrunc(dateBucket, rowFieldAlias, databaseType)} as ${processColumnReference(`${rowField}`, databaseType)}, sum(${valueFieldAlias}) / ${max}(sum(${valueField2Alias}), 1) as ${processColumnReference(`${valueField ?? ''}_${matchCasing('percentage', valueField)}`, databaseType, undefined, false, true)} FROM quill_alias GROUP BY ${databaseType.toLowerCase() === 'clickhouse' ? processColumnReference(`${rowField}`, databaseType) : processDateTrunc(dateBucket, rowFieldAlias, databaseType)})
527
- ${compareCTE} ${aliasCTE} ${compareCaseWhenCTE}
528
- SELECT * FROM quill_qt_agg qt
529
- ${joinQuery} ORDER BY qt.${processColumnReference(`${rowField}`, databaseType)}${rowLimit ? ` LIMIT ${rowLimit}` : ''}`;
449
+ value2AliasSubstring =
450
+ valueField !== pivot.valueField2 ? `, ${valueField2Alias}` : '';
451
+ quillAggSubstring = `
452
+ ${processDateTrunc(dateBucket, rowFieldAlias, databaseType)} as ${processColumnReference(rowField, databaseType)}
453
+ , sum(${valueFieldAlias}) / GREATEST(sum(${valueField2Alias}), 1) as ${processColumnReference(`${valueField ?? ''}_${matchCasing('percentage', valueField)}`, databaseType, undefined, false, true)}
454
+ `;
530
455
  }
531
- const compareCTE = itemQueries[1] && comparisonInterval
532
- ? `, quill_ct as (${itemQueries[1]?.replaceAll(';', '')})`
533
- : '';
534
- const aliasCTE = itemQueries[1] && comparisonInterval
535
- ? `, quill_c_alias as (SELECT ${processColumnReference(rowField, databaseType, undefined, true)} AS ${rowFieldAlias}${valueAliasSubstring} FROM quill_ct)`
536
- : '';
537
- const compareCaseWhenCTE = itemQueries[1] && comparisonInterval
538
- ? `, quill_ct_cw as (SELECT ${processDateTrunc(dateBucket, rowFieldAlias, databaseType)} as ${processColumnReference(`comparison_${rowFieldAlias}`, databaseType)},
539
- ${processAggType(pivot.aggregationType)}(${valueFieldAlias}) as ${processColumnReference(`comparison_${valueField ? valueFieldAlias : 'count'}`, databaseType)} FROM quill_c_alias GROUP BY ${databaseType.toLowerCase() === 'clickhouse' ? processColumnReference(`${rowField}`, databaseType) : processDateTrunc(dateBucket, rowFieldAlias, databaseType)})`
540
- : '';
541
- const joinQuery = itemQueries[1] && comparisonInterval
542
- ? `LEFT JOIN quill_ct_cw ${processColumnReference(`ct`, databaseType)} ON qt.${processColumnReference(rowField, databaseType)} = ${processDateTrunc(dateBucket, `ct.comparison_${rowField}`, databaseType, comparisonInterval)}`
543
- : '';
544
- return `WITH quill_qt AS (${itemQueries[0]?.replaceAll(';', '')}),
545
- quill_alias AS (SELECT ${processColumnReference(`${rowField}`, databaseType, undefined, true)} AS ${rowFieldAlias}${valueAliasSubstring} FROM quill_qt),
546
- quill_qt_agg AS (SELECT ${processDateTrunc(dateBucket, rowFieldAlias, databaseType)} as ${processColumnReference(`${rowField}`, databaseType)}, ${processAggType(pivot.aggregationType)}(${valueField ? valueFieldAlias : rowFieldAlias}) as ${valueField ? valueFieldAlias : countAlias} FROM quill_alias GROUP BY ${databaseType.toLowerCase() === 'clickhouse' ? processColumnReference(`${rowField}`, databaseType) : processDateTrunc(dateBucket, rowFieldAlias, databaseType)})
547
- ${compareCTE} ${aliasCTE} ${compareCaseWhenCTE}
548
- SELECT * FROM quill_qt_agg qt
549
- ${joinQuery} ORDER BY qt.${processColumnReference(`${rowField}`, databaseType)}${rowLimit ? ` LIMIT ${rowLimit}` : ''}`;
456
+ // pivot sort matters in the base query when there is a rowLimit. In mssql, an orderby must be accompanied by a limit in a subquery and not allowed in a cte
457
+ const sortQuery = `${pivot.sort && pivot.sortField && pivot.rowLimit ? ` ORDER BY ${processColumnReference(pivot.sortField, databaseType)} ${pivot.sortDirection || ''} ` : ''}`;
458
+ const pivotQuery = `, quill_alias AS (SELECT ${processColumnReference(`${rowField}`, databaseType, undefined, true)} AS ${rowFieldAlias}${valueAliasSubstring}
459
+ ${value2AliasSubstring}
460
+ FROM quill_base_table),
461
+ quill_qt_agg AS (SELECT ${quillAggSubstring}
462
+ FROM quill_alias GROUP BY ${databaseType.toLowerCase() === 'clickhouse' ? processColumnReference(`${rowField}`, databaseType) : processDateTrunc(dateBucket, rowFieldAlias, databaseType)}),
463
+ quill_base_pivot AS (SELECT ${pivot.rowLimit && databaseType.toLowerCase() === 'mssql' ? `TOP ${pivot.rowLimit}` : ''} * FROM quill_qt_agg qt
464
+ ${sortQuery}${pivot.rowLimit && databaseType.toLowerCase() !== 'mssql' ? ` LIMIT ${pivot.rowLimit}` : ''})
465
+ SELECT * FROM quill_base_pivot
466
+ `
467
+ .replace(/\s+/g, ' ')
468
+ .trim();
469
+ return itemQuery.replace(/SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/, pivotQuery);
550
470
  }
551
- function createAggregationValuePivot(pivot, itemQueries, databaseType, rowLimit) {
471
+ function createAggregationValuePivot(pivot, itemQuery, databaseType) {
472
+ const isValidBaseQuery = itemQuery.match(/SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/);
473
+ if (!isValidBaseQuery || !pivot.valueField) {
474
+ return undefined;
475
+ }
552
476
  const valueField = pivot.valueField;
553
477
  const aggregationType = processAggType(pivot.aggregationType);
554
- if (!pivot.valueField)
555
- return undefined;
556
478
  const valueAlias = processColumnReference(valueField, databaseType, undefined, false, true);
479
+ let value2AliasSubstring = '';
480
+ let quillAggSubstring = `SELECT ${aggregationType}(${valueAlias}) as ${processColumnReference(valueField, databaseType)}`;
557
481
  if (aggregationType === 'percentage' && pivot.valueField2) {
558
482
  const value2Alias = processColumnReference(pivot.valueField2, databaseType, undefined, false, true);
559
- const max = 'GREATEST';
560
- const compareCTE = itemQueries[1]
561
- ? `, quill_ct as (${itemQueries[1]?.replaceAll(';', '')})`
562
- : '';
563
- const aliasCTE = itemQueries[1]
564
- ? `, quill_c_alias as (SELECT ${processColumnReference(valueField, databaseType, undefined, true)} AS ${valueAlias}, ${processColumnReference(pivot.valueField2, databaseType, undefined, true)} AS ${value2Alias} FROM quill_ct)`
565
- : '';
566
- const compareAggregation = itemQueries[1]
567
- ? `, quill_ct_cw as (SELECT sum(${processColumnReference(`${valueField}`, databaseType)}) / ${max}(sum(${processColumnReference(`${pivot.valueField2}`, databaseType)}), 1) as ${processColumnReference(`comparison_${valueField}_${matchCasing('percentage', valueField)}`, databaseType, undefined, false, true)} FROM quill_c_alias)`
568
- : '';
569
- const joinQuery = itemQueries[1] ? `CROSS JOIN quill_ct_cw ct` : '';
570
- return `WITH quill_qt AS (${itemQueries[0]?.replaceAll(';', '')}), quill_alias AS (SELECT ${processColumnReference(`${valueField}`, databaseType, undefined, true)} AS ${valueAlias}, ${processColumnReference(`${pivot.valueField2}`, databaseType, undefined, true)} AS ${value2Alias} FROM quill_qt),
571
- quill_qt_agg AS (SELECT sum(${valueAlias}) / ${max}(sum(${value2Alias}), 1) as ${processColumnReference(`${valueField}_${matchCasing('percentage', valueField)}`, databaseType)} FROM quill_alias)
572
- ${compareCTE} ${aliasCTE} ${compareAggregation}
573
- SELECT * FROM quill_qt_agg qt
574
- ${joinQuery}${rowLimit ? ` LIMIT ${rowLimit}` : ''}`;
483
+ value2AliasSubstring = `, ${value2Alias}`;
484
+ quillAggSubstring = `SELECT sum(${valueAlias}) / GREATEST(sum(${value2Alias}), 1) as ${processColumnReference(`${valueField}_${matchCasing('percentage', valueField)}`, databaseType, undefined, false, true)}`;
575
485
  }
576
- const compareCTE = itemQueries[1]
577
- ? `, quill_ct as (${itemQueries[1]?.replaceAll(';', '')})`
578
- : '';
579
- const aliasCTE = itemQueries[1]
580
- ? `, quill_c_alias as (SELECT ${processColumnReference(valueField, databaseType, undefined, true)} AS ${valueAlias} FROM quill_ct)`
581
- : '';
582
- const compareAggregation = itemQueries[1]
583
- ? `, quill_ct_cw as (SELECT ${aggregationType}(${processColumnReference(`${valueField}`, databaseType)}) as ${processColumnReference(`comparison_${valueField}`, databaseType)} FROM quill_c_alias)`
584
- : '';
585
- const joinQuery = itemQueries[1] ? `CROSS JOIN quill_ct_cw ct` : '';
586
- return `WITH quill_qt AS (${itemQueries[0]?.replaceAll(';', '')}), quill_alias AS (SELECT ${processColumnReference(`${valueField}`, databaseType, undefined, true)} AS ${valueAlias} FROM quill_qt),
587
- quill_qt_agg AS (SELECT ${aggregationType}(${valueAlias}) as ${processColumnReference(`${valueField}`, databaseType)} FROM quill_alias)
588
- ${compareCTE} ${aliasCTE} ${compareAggregation}
589
- SELECT * FROM quill_qt_agg qt
590
- ${joinQuery} ${rowLimit ? ` LIMIT ${rowLimit}` : ''}`;
486
+ // pivot sort matters in the base query when there is a rowLimit. In mssql, an orderby must be accompanied by a limit in a subquery and not allowed in a cte
487
+ const sortQuery = `${pivot.sort && pivot.sortField && pivot.rowLimit ? ` ORDER BY ${processColumnReference(pivot.sortField, databaseType)} ${pivot.sortDirection || ''} ` : ''}`;
488
+ const pivotQuery = `, quill_alias AS (SELECT ${valueAlias}${value2AliasSubstring} FROM quill_base_table),
489
+ quill_qt_agg AS (${quillAggSubstring} FROM quill_alias),
490
+ quill_base_pivot AS (SELECT ${pivot.rowLimit && databaseType.toLowerCase() === 'mssql' ? `TOP ${pivot.rowLimit}` : ''} * FROM quill_qt_agg qt
491
+ ${sortQuery}${pivot.rowLimit && databaseType.toLowerCase() !== 'mssql' ? ` LIMIT ${pivot.rowLimit}` : ''})
492
+ SELECT * FROM quill_base_pivot
493
+ `
494
+ .replace(/\s+/g, ' ')
495
+ .trim();
496
+ return itemQuery.replace(/SELECT \* FROM\s+["'[`]?quill_base_table["'\]`]?\s*$/, pivotQuery);
497
+ }
498
+ export function additionalProcessingOnPivotQuery(pivot, query, additionalProcessing, databaseType = 'postgresql') {
499
+ if (!additionalProcessing || !query)
500
+ return query;
501
+ const isValidBaseQuery = query.match(/SELECT \* FROM\s+["'[`]?quill_base_pivot["'\]`]?\s*$/);
502
+ if (!isValidBaseQuery) {
503
+ return undefined;
504
+ }
505
+ let rowsPerPage = 0;
506
+ let currentInterval = 0;
507
+ let offset = 0;
508
+ let limit = 1000;
509
+ let sortQuery = `${pivot.sort && pivot.sortField
510
+ ? ` ORDER BY ${processColumnReference(pivot.sortField, databaseType, undefined, true)} ${pivot.sortDirection || ''}`
511
+ : pivot.rowField
512
+ ? `ORDER BY ${processColumnReference(`${pivot.rowField}`, databaseType)}`
513
+ : pivot.valueField
514
+ ? pivot.aggregationType === 'percentage'
515
+ ? `ORDER BY ${processColumnReference(`${pivot.valueField}_${matchCasing('percentage', pivot.valueField)}`, databaseType, undefined, false, true)}`
516
+ : `ORDER BY ${processColumnReference(pivot.valueField, databaseType, undefined, false, true)}`
517
+ : ''}`;
518
+ if (additionalProcessing.page) {
519
+ const page = additionalProcessing.page.page || 0;
520
+ if (additionalProcessing.page.rowsPerRequest) {
521
+ limit = additionalProcessing.page.rowsPerRequest;
522
+ }
523
+ rowsPerPage = additionalProcessing.page.rowsPerPage || 0;
524
+ currentInterval = page ? Math.floor(page / (limit / rowsPerPage)) : 0;
525
+ offset = currentInterval * limit;
526
+ }
527
+ if (additionalProcessing.sort) {
528
+ sortQuery = `ORDER BY ${processColumnReference(additionalProcessing.sort.field, databaseType)} ${additionalProcessing.sort.direction || ''}`;
529
+ }
530
+ const additionalProcessingQuery = `
531
+ SELECT *
532
+ FROM quill_base_pivot ${sortQuery}${databaseType === 'mssql' ? ` OFFSET ${offset} ROWS FETCH NEXT ${limit} ROWS ONLY` : ` LIMIT ${limit} OFFSET ${offset}`}
533
+ `
534
+ .replace(/\s+/g, ' ')
535
+ .trim();
536
+ return query.replace(/SELECT \* FROM\s+["'[`]?quill_base_pivot["'\]`]?\s*$/, additionalProcessingQuery);
537
+ }
538
+ export function generateRowCountQuery(query, databaseType) {
539
+ if (!query)
540
+ return query;
541
+ const isValidBaseQuery = query.match(/SELECT \* FROM\s+["'[`]?quill_base_pivot["'\]`]?\s*$/);
542
+ if (!isValidBaseQuery) {
543
+ return undefined;
544
+ }
545
+ const rowCountQuery = `, subview_row_count_cte AS (SELECT * FROM quill_base_pivot)
546
+ SELECT count(*) as ${processColumnReference('row_count', databaseType || 'postgresql', undefined, true)} FROM subview_row_count_cte
547
+ `
548
+ .replace(/\s+/g, ' ')
549
+ .trim();
550
+ return query.replace(/SELECT \* FROM\s+["'[`]?quill_base_pivot["'\]`]?\s*$/, rowCountQuery);
591
551
  }