@hypequery/clickhouse 2.11.1 → 2.11.2
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sql-formatter.d.ts","sourceRoot":"","sources":["../../../src/core/formatters/sql-formatter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,aAAa,EAAE,KAAK,QAAQ,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAkB,MAAM,sBAAsB,CAAC;AAIhJ,qBAAa,YAAY;IACvB,YAAY,CAAC,KAAK,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;IAMtD,aAAa,CAAC,KAAK,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;IAMvD,gBAAgB,CAAC,KAAK,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;IAO1D,cAAc,CAAC,KAAK,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;IAIxD,WAAW,CAAC,KAAK,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;IAIrD,UAAU,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,MAAM;IAYvC,WAAW,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,MAAM,UAAQ,GAAG,aAAa;IA0D3D,OAAO,CAAC,gBAAgB;
|
|
1
|
+
{"version":3,"file":"sql-formatter.d.ts","sourceRoot":"","sources":["../../../src/core/formatters/sql-formatter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,aAAa,EAAE,KAAK,QAAQ,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAkB,MAAM,sBAAsB,CAAC;AAIhJ,qBAAa,YAAY;IACvB,YAAY,CAAC,KAAK,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;IAMtD,aAAa,CAAC,KAAK,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;IAMvD,gBAAgB,CAAC,KAAK,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;IAO1D,cAAc,CAAC,KAAK,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;IAIxD,WAAW,CAAC,KAAK,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;IAIrD,UAAU,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,MAAM;IAYvC,WAAW,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,MAAM,UAAQ,GAAG,aAAa;IA0D3D,OAAO,CAAC,gBAAgB;IA4FxB,aAAa,CAAC,KAAK,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,aAAa;IAkB9D,OAAO,CAAC,cAAc;IActB,YAAY,CAAC,KAAK,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,aAAa;IAkB7D,WAAW,CAAC,KAAK,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;IAIrD,WAAW,CAAC,KAAK,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,aAAa;IAW5D,UAAU,CAAC,KAAK,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;IAIpD,aAAa,CAAC,KAAK,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;IAOvD,aAAa,CAAC,KAAK,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,MAAM;IAKvD,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,4BAA4B;CAMrC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { cteFragment } from '../utils/cte-fragments.js';
|
|
2
|
-
import { hasTopLevelLogicalOperator, terminateTrailingLineComment } from '../utils/sql-parens.js';
|
|
2
|
+
import { groupConditionColumn, hasTopLevelLogicalOperator, terminateTrailingLineComment } from '../utils/sql-parens.js';
|
|
3
3
|
export class SQLFormatter {
|
|
4
4
|
formatSelect(query) {
|
|
5
5
|
const distinctClause = query.distinct ? 'DISTINCT ' : '';
|
|
@@ -94,7 +94,10 @@ export class SQLFormatter {
|
|
|
94
94
|
throw new Error(`Unsupported expression kind: ${String(expr.kind)}`);
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
|
-
compileCondition({ column, operator, value }) {
|
|
97
|
+
compileCondition({ column: rawColumn, operator, value }) {
|
|
98
|
+
// The column may be a dataset's trusted SQL expression rather than an
|
|
99
|
+
// identifier, so it is grouped before it meets the conditions beside it.
|
|
100
|
+
const column = groupConditionColumn(rawColumn);
|
|
98
101
|
if (operator === 'isNull' || operator === 'isNotNull') {
|
|
99
102
|
return {
|
|
100
103
|
query: `${column} IS ${operator === 'isNull' ? '' : 'NOT '}NULL`.trim(),
|
|
@@ -23,6 +23,21 @@ export declare function terminateTrailingLineComment(sql: string): string;
|
|
|
23
23
|
* WHERE parses.
|
|
24
24
|
*/
|
|
25
25
|
export declare function hasTopLevelLogicalOperator(sql: string): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Groups a condition's column so it cannot reach past its own operand.
|
|
28
|
+
*
|
|
29
|
+
* A condition's column slot does not always hold an identifier. A dataset
|
|
30
|
+
* dimension may be backed by a trusted SQL expression, and that expression is
|
|
31
|
+
* passed here verbatim. Rendered bare next to other conditions it rebinds
|
|
32
|
+
* against them: with a dimension of `active OR is_public`, the WHERE becomes
|
|
33
|
+
* `tenant_id = ? AND active OR is_public = ?`, and because AND binds tighter
|
|
34
|
+
* than OR the second branch returns rows for every tenant. Grouping the
|
|
35
|
+
* expression keeps the tenant predicate independent of it.
|
|
36
|
+
*
|
|
37
|
+
* A trailing line comment is terminated for the same reason it is on raw
|
|
38
|
+
* fragments: unterminated, it swallows the rest of the statement.
|
|
39
|
+
*/
|
|
40
|
+
export declare function groupConditionColumn(column: string): string;
|
|
26
41
|
/**
|
|
27
42
|
* True when the fragment is a single parenthesized group, so wrapping it again
|
|
28
43
|
* would only add redundant parentheses.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sql-parens.d.ts","sourceRoot":"","sources":["../../../src/core/utils/sql-parens.ts"],"names":[],"mappings":"AAuEA;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAqB1E;AASD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAqB1D;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAchE;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CA8B/D;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAuBzD"}
|
|
1
|
+
{"version":3,"file":"sql-parens.d.ts","sourceRoot":"","sources":["../../../src/core/utils/sql-parens.ts"],"names":[],"mappings":"AAuEA;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAqB1E;AASD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAqB1D;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAchE;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CA8B/D;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAM3D;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAuBzD"}
|
|
@@ -184,6 +184,27 @@ export function hasTopLevelLogicalOperator(sql) {
|
|
|
184
184
|
}
|
|
185
185
|
return false;
|
|
186
186
|
}
|
|
187
|
+
/**
|
|
188
|
+
* Groups a condition's column so it cannot reach past its own operand.
|
|
189
|
+
*
|
|
190
|
+
* A condition's column slot does not always hold an identifier. A dataset
|
|
191
|
+
* dimension may be backed by a trusted SQL expression, and that expression is
|
|
192
|
+
* passed here verbatim. Rendered bare next to other conditions it rebinds
|
|
193
|
+
* against them: with a dimension of `active OR is_public`, the WHERE becomes
|
|
194
|
+
* `tenant_id = ? AND active OR is_public = ?`, and because AND binds tighter
|
|
195
|
+
* than OR the second branch returns rows for every tenant. Grouping the
|
|
196
|
+
* expression keeps the tenant predicate independent of it.
|
|
197
|
+
*
|
|
198
|
+
* A trailing line comment is terminated for the same reason it is on raw
|
|
199
|
+
* fragments: unterminated, it swallows the rest of the statement.
|
|
200
|
+
*/
|
|
201
|
+
export function groupConditionColumn(column) {
|
|
202
|
+
const terminated = terminateTrailingLineComment(column);
|
|
203
|
+
if (!hasTopLevelLogicalOperator(terminated) || isFullyParenthesized(terminated)) {
|
|
204
|
+
return terminated;
|
|
205
|
+
}
|
|
206
|
+
return `(${terminated})`;
|
|
207
|
+
}
|
|
187
208
|
/**
|
|
188
209
|
* True when the fragment is a single parenthesized group, so wrapping it again
|
|
189
210
|
* would only add redundant parentheses.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hypequery/clickhouse",
|
|
3
|
-
"version": "2.11.
|
|
3
|
+
"version": "2.11.2",
|
|
4
4
|
"description": "Type-safe ClickHouse query builder and schema generator for TypeScript analytics",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"clickhouse",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"typescript": "^5.7.3",
|
|
74
74
|
"@vitest/coverage-v8": "^3.2.6",
|
|
75
75
|
"vitest": "^3.2.6",
|
|
76
|
-
"@hypequery/datasets": "0.
|
|
76
|
+
"@hypequery/datasets": "0.18.0"
|
|
77
77
|
},
|
|
78
78
|
"ts-node": {
|
|
79
79
|
"esm": true,
|