@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;IAuFxB,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
+ {"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.1",
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.17.0"
76
+ "@hypequery/datasets": "0.18.0"
77
77
  },
78
78
  "ts-node": {
79
79
  "esm": true,