@hypequery/clickhouse 1.6.2 → 2.0.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 (142) hide show
  1. package/README-CLI.md +43 -88
  2. package/README.md +84 -253
  3. package/dist/cli/bin.js +16 -8
  4. package/dist/core/adapters/clickhouse-adapter.d.ts.map +1 -1
  5. package/dist/core/adapters/clickhouse-adapter.js +3 -2
  6. package/dist/core/cache/cache-manager.d.ts.map +1 -1
  7. package/dist/core/cache/cache-manager.js +5 -3
  8. package/dist/core/connection.d.ts +6 -6
  9. package/dist/core/connection.js +9 -9
  10. package/dist/core/cross-filter.js +1 -1
  11. package/dist/core/dialects/clickhouse-dialect.d.ts +2 -2
  12. package/dist/core/dialects/clickhouse-dialect.d.ts.map +1 -1
  13. package/dist/core/dialects/clickhouse-dialect.js +39 -22
  14. package/dist/core/dialects/sql-dialect.d.ts +2 -2
  15. package/dist/core/dialects/sql-dialect.d.ts.map +1 -1
  16. package/dist/core/env/auto-client.d.ts.map +1 -1
  17. package/dist/core/env/auto-client.js +1 -1
  18. package/dist/core/features/aggregations.d.ts +27 -84
  19. package/dist/core/features/aggregations.d.ts.map +1 -1
  20. package/dist/core/features/aggregations.js +59 -7
  21. package/dist/core/features/analytics.d.ts +5 -870
  22. package/dist/core/features/analytics.d.ts.map +1 -1
  23. package/dist/core/features/analytics.js +15 -13
  24. package/dist/core/features/cross-filtering.d.ts +1 -1
  25. package/dist/core/features/cross-filtering.d.ts.map +1 -1
  26. package/dist/core/features/cross-filtering.js +28 -73
  27. package/dist/core/features/executor.d.ts +1 -1
  28. package/dist/core/features/executor.d.ts.map +1 -1
  29. package/dist/core/features/executor.js +9 -11
  30. package/dist/core/features/filtering.d.ts +5 -91
  31. package/dist/core/features/filtering.d.ts.map +1 -1
  32. package/dist/core/features/filtering.js +63 -77
  33. package/dist/core/features/joins.d.ts +2 -19
  34. package/dist/core/features/joins.d.ts.map +1 -1
  35. package/dist/core/features/joins.js +16 -5
  36. package/dist/core/features/query-modifiers.d.ts +10 -109
  37. package/dist/core/features/query-modifiers.d.ts.map +1 -1
  38. package/dist/core/features/query-modifiers.js +64 -18
  39. package/dist/core/formatters/sql-formatter.d.ts +16 -5
  40. package/dist/core/formatters/sql-formatter.d.ts.map +1 -1
  41. package/dist/core/formatters/sql-formatter.js +197 -93
  42. package/dist/core/join-relationships.d.ts +22 -5
  43. package/dist/core/join-relationships.d.ts.map +1 -1
  44. package/dist/core/join-relationships.js +1 -1
  45. package/dist/core/query-builder.d.ts +64 -12
  46. package/dist/core/query-builder.d.ts.map +1 -1
  47. package/dist/core/query-builder.js +213 -153
  48. package/dist/core/query-node.d.ts +7 -0
  49. package/dist/core/query-node.d.ts.map +1 -0
  50. package/dist/core/query-node.js +80 -0
  51. package/dist/core/tests/integration/setup.d.ts +3 -10
  52. package/dist/core/tests/integration/setup.d.ts.map +1 -1
  53. package/dist/core/tests/integration/setup.js +30 -249
  54. package/dist/core/types/select-types.d.ts +3 -0
  55. package/dist/core/types/select-types.d.ts.map +1 -1
  56. package/dist/core/utils/connection-endpoint.d.ts +3 -0
  57. package/dist/core/utils/connection-endpoint.d.ts.map +1 -0
  58. package/dist/core/utils/connection-endpoint.js +9 -0
  59. package/dist/core/utils/filter-application.d.ts +15 -0
  60. package/dist/core/utils/filter-application.d.ts.map +1 -0
  61. package/dist/core/utils/filter-application.js +32 -0
  62. package/dist/core/utils/query-config-compat.d.ts +48 -0
  63. package/dist/core/utils/query-config-compat.d.ts.map +1 -0
  64. package/dist/core/utils/query-config-compat.js +137 -0
  65. package/dist/core/utils/relation-application.d.ts +9 -0
  66. package/dist/core/utils/relation-application.d.ts.map +1 -0
  67. package/dist/core/utils/relation-application.js +19 -0
  68. package/dist/core/utils/relation-validation.d.ts +6 -0
  69. package/dist/core/utils/relation-validation.d.ts.map +1 -0
  70. package/dist/core/utils/relation-validation.js +29 -0
  71. package/dist/core/utils/sql-expressions.d.ts +14 -0
  72. package/dist/core/utils/sql-expressions.d.ts.map +1 -1
  73. package/dist/core/utils/sql-expressions.js +40 -0
  74. package/dist/core/utils/tuple-filter-validation.d.ts +3 -0
  75. package/dist/core/utils/tuple-filter-validation.d.ts.map +1 -0
  76. package/dist/core/utils/tuple-filter-validation.js +16 -0
  77. package/dist/index.d.ts +2 -13
  78. package/dist/index.d.ts.map +1 -1
  79. package/dist/index.js +3 -8
  80. package/dist/types/base.d.ts +89 -22
  81. package/dist/types/base.d.ts.map +1 -1
  82. package/dist/types/filters.d.ts +9 -5
  83. package/dist/types/filters.d.ts.map +1 -1
  84. package/package.json +5 -5
  85. package/dist/core/tests/integration/test-data.json +0 -190
  86. package/dist/migrations/config/index.d.ts +0 -3
  87. package/dist/migrations/config/index.d.ts.map +0 -1
  88. package/dist/migrations/config/index.js +0 -1
  89. package/dist/migrations/config/types.d.ts +0 -45
  90. package/dist/migrations/config/types.d.ts.map +0 -1
  91. package/dist/migrations/config/types.js +0 -28
  92. package/dist/migrations/diff/diff.d.ts +0 -11
  93. package/dist/migrations/diff/diff.d.ts.map +0 -1
  94. package/dist/migrations/diff/diff.js +0 -240
  95. package/dist/migrations/diff/index.d.ts +0 -3
  96. package/dist/migrations/diff/index.d.ts.map +0 -1
  97. package/dist/migrations/diff/index.js +0 -1
  98. package/dist/migrations/diff/types.d.ts +0 -74
  99. package/dist/migrations/diff/types.d.ts.map +0 -1
  100. package/dist/migrations/diff/types.js +0 -1
  101. package/dist/migrations/plan/index.d.ts +0 -3
  102. package/dist/migrations/plan/index.d.ts.map +0 -1
  103. package/dist/migrations/plan/index.js +0 -1
  104. package/dist/migrations/plan/plan.d.ts +0 -12
  105. package/dist/migrations/plan/plan.d.ts.map +0 -1
  106. package/dist/migrations/plan/plan.js +0 -416
  107. package/dist/migrations/plan/types.d.ts +0 -93
  108. package/dist/migrations/plan/types.d.ts.map +0 -1
  109. package/dist/migrations/plan/types.js +0 -1
  110. package/dist/migrations/schema/column.d.ts +0 -71
  111. package/dist/migrations/schema/column.d.ts.map +0 -1
  112. package/dist/migrations/schema/column.js +0 -123
  113. package/dist/migrations/schema/define.d.ts +0 -24
  114. package/dist/migrations/schema/define.d.ts.map +0 -1
  115. package/dist/migrations/schema/define.js +0 -47
  116. package/dist/migrations/schema/index.d.ts +0 -4
  117. package/dist/migrations/schema/index.d.ts.map +0 -1
  118. package/dist/migrations/schema/index.js +0 -2
  119. package/dist/migrations/schema/types.d.ts +0 -74
  120. package/dist/migrations/schema/types.d.ts.map +0 -1
  121. package/dist/migrations/schema/types.js +0 -1
  122. package/dist/migrations/snapshot/index.d.ts +0 -3
  123. package/dist/migrations/snapshot/index.d.ts.map +0 -1
  124. package/dist/migrations/snapshot/index.js +0 -1
  125. package/dist/migrations/snapshot/serialize.d.ts +0 -21
  126. package/dist/migrations/snapshot/serialize.d.ts.map +0 -1
  127. package/dist/migrations/snapshot/serialize.js +0 -127
  128. package/dist/migrations/snapshot/types.d.ts +0 -47
  129. package/dist/migrations/snapshot/types.d.ts.map +0 -1
  130. package/dist/migrations/snapshot/types.js +0 -1
  131. package/dist/migrations/sql/index.d.ts +0 -4
  132. package/dist/migrations/sql/index.d.ts.map +0 -1
  133. package/dist/migrations/sql/index.js +0 -2
  134. package/dist/migrations/sql/render.d.ts +0 -10
  135. package/dist/migrations/sql/render.d.ts.map +0 -1
  136. package/dist/migrations/sql/render.js +0 -347
  137. package/dist/migrations/sql/types.d.ts +0 -53
  138. package/dist/migrations/sql/types.d.ts.map +0 -1
  139. package/dist/migrations/sql/types.js +0 -1
  140. package/dist/migrations/sql/write.d.ts +0 -10
  141. package/dist/migrations/sql/write.d.ts.map +0 -1
  142. package/dist/migrations/sql/write.js +0 -35
@@ -1 +1 @@
1
- {"version":3,"file":"cache-manager.d.ts","sourceRoot":"","sources":["../../../src/core/cache/cache-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACxE,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AA4DnF,wBAAsB,gBAAgB,CACpC,MAAM,SAAS,gBAAgB,CAAC,MAAM,CAAC,EACvC,KAAK,SAAS,eAAe,EAE7B,OAAO,EAAE,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,EACpC,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CA4J5B"}
1
+ {"version":3,"file":"cache-manager.d.ts","sourceRoot":"","sources":["../../../src/core/cache/cache-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACxE,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AA6DnF,wBAAsB,gBAAgB,CACpC,MAAM,SAAS,gBAAgB,CAAC,MAAM,CAAC,EACvC,KAAK,SAAS,eAAe,EAE7B,OAAO,EAAE,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,EACpC,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CA6J5B"}
@@ -8,9 +8,10 @@ function isCacheable(options) {
8
8
  return ttl > 0 || stale > 0;
9
9
  }
10
10
  function deriveTags(builder) {
11
+ const queryNode = builder.toQueryNode();
11
12
  const tags = new Set();
12
- tags.add(builder.getTableName());
13
- const joins = builder.getConfig().joins || [];
13
+ tags.add(queryNode.from?.kind === 'table' ? queryNode.from.name : builder.getTableName());
14
+ const joins = queryNode.joins || [];
14
15
  joins.forEach(join => tags.add(join.table));
15
16
  return Array.from(tags);
16
17
  }
@@ -49,11 +50,12 @@ export async function executeWithCache(builder, options) {
49
50
  const renderSql = adapter.render ? adapter.render(sql, parameters) : substituteParameters(sql, parameters);
50
51
  const tableName = builder.getTableName();
51
52
  const namespace = mergedOptions.namespace || runtime.namespace;
53
+ const queryNode = builder.toQueryNode();
52
54
  const key = mergedOptions.key || computeCacheKey({
53
55
  namespace,
54
56
  sql,
55
57
  parameters,
56
- settings: builder.getConfig().settings,
58
+ settings: queryNode.settings,
57
59
  version: runtime.versionTag,
58
60
  tableName
59
61
  });
@@ -17,20 +17,20 @@ type ClickHouseClient = NodeClickHouseClient | WebClickHouseClient;
17
17
  * // Method 1: Manual injection (required for browser environments)
18
18
  * import { createClient } from '@clickhouse/client-web';
19
19
  * const client = createClient({
20
- * host: 'http://localhost:8123',
20
+ * url: 'http://localhost:8123',
21
21
  * username: 'default',
22
22
  * password: 'password'
23
23
  * });
24
24
  *
25
25
  * ClickHouseConnection.initialize({
26
- * host: 'http://localhost:8123',
26
+ * url: 'http://localhost:8123',
27
27
  * database: 'my_database',
28
28
  * client // Explicitly provide the client
29
29
  * });
30
30
  *
31
31
  * // Method 2: Auto-detection (Node.js environments only)
32
32
  * ClickHouseConnection.initialize({
33
- * host: 'http://localhost:8123',
33
+ * url: 'http://localhost:8123',
34
34
  * username: 'default',
35
35
  * password: 'password',
36
36
  * database: 'my_database'
@@ -71,12 +71,12 @@ export declare class ClickHouseConnection {
71
71
  * ```typescript
72
72
  * // Manual injection (required for browser environments)
73
73
  * import { createClient } from '@clickhouse/client-web';
74
- * const client = createClient({ host: 'http://localhost:8123' });
75
- * ClickHouseConnection.initialize({ host: 'http://localhost:8123', client });
74
+ * const client = createClient({ url: 'http://localhost:8123' });
75
+ * ClickHouseConnection.initialize({ url: 'http://localhost:8123', client });
76
76
  *
77
77
  * // Auto-detection (Node.js environments only)
78
78
  * ClickHouseConnection.initialize({
79
- * host: 'http://localhost:8123',
79
+ * url: 'http://localhost:8123',
80
80
  * username: 'default',
81
81
  * password: 'password',
82
82
  * database: 'my_database'
@@ -21,8 +21,8 @@ function getClickHouseClientSync() {
21
21
  'Please use manual injection by providing a client instance:\n\n' +
22
22
  '```typescript\n' +
23
23
  'import { createClient } from \'@clickhouse/client-web\';\n' +
24
- 'const client = createClient({ host: \'http://localhost:8123\' });\n' +
25
- 'ClickHouseConnection.initialize({ host: \'http://localhost:8123\', client });\n' +
24
+ 'const client = createClient({ url: \'http://localhost:8123\' });\n' +
25
+ 'ClickHouseConnection.initialize({ url: \'http://localhost:8123\', client });\n' +
26
26
  '```\n\n' +
27
27
  'This is required because browser environments cannot use require() to load modules.');
28
28
  }
@@ -40,20 +40,20 @@ function getClickHouseClientSync() {
40
40
  * // Method 1: Manual injection (required for browser environments)
41
41
  * import { createClient } from '@clickhouse/client-web';
42
42
  * const client = createClient({
43
- * host: 'http://localhost:8123',
43
+ * url: 'http://localhost:8123',
44
44
  * username: 'default',
45
45
  * password: 'password'
46
46
  * });
47
47
  *
48
48
  * ClickHouseConnection.initialize({
49
- * host: 'http://localhost:8123',
49
+ * url: 'http://localhost:8123',
50
50
  * database: 'my_database',
51
51
  * client // Explicitly provide the client
52
52
  * });
53
53
  *
54
54
  * // Method 2: Auto-detection (Node.js environments only)
55
55
  * ClickHouseConnection.initialize({
56
- * host: 'http://localhost:8123',
56
+ * url: 'http://localhost:8123',
57
57
  * username: 'default',
58
58
  * password: 'password',
59
59
  * database: 'my_database'
@@ -94,12 +94,12 @@ export class ClickHouseConnection {
94
94
  * ```typescript
95
95
  * // Manual injection (required for browser environments)
96
96
  * import { createClient } from '@clickhouse/client-web';
97
- * const client = createClient({ host: 'http://localhost:8123' });
98
- * ClickHouseConnection.initialize({ host: 'http://localhost:8123', client });
97
+ * const client = createClient({ url: 'http://localhost:8123' });
98
+ * ClickHouseConnection.initialize({ url: 'http://localhost:8123', client });
99
99
  *
100
100
  * // Auto-detection (Node.js environments only)
101
101
  * ClickHouseConnection.initialize({
102
- * host: 'http://localhost:8123',
102
+ * url: 'http://localhost:8123',
103
103
  * username: 'default',
104
104
  * password: 'password',
105
105
  * database: 'my_database'
@@ -112,7 +112,7 @@ export class ClickHouseConnection {
112
112
  this.instance = config.client;
113
113
  return ClickHouseConnection;
114
114
  }
115
- // Otherwise, auto-detect the client (we know we have a host-based config)
115
+ // Otherwise, auto-detect the client using the provided ClickHouse connection config.
116
116
  this.clientModule = getClickHouseClientSync();
117
117
  this.instance = this.clientModule.createClient(config);
118
118
  return ClickHouseConnection;
@@ -114,7 +114,7 @@ export class CrossFilter {
114
114
  * Type guard to check if an item is a FilterGroup.
115
115
  */
116
116
  isGroup(item) {
117
- return typeof item.conditions !== 'undefined';
117
+ return 'conditions' in item;
118
118
  }
119
119
  /**
120
120
  * Creates a filter for top N records by a value column
@@ -1,9 +1,9 @@
1
- import type { QueryConfig } from '../../types/index.js';
1
+ import type { CompiledQuery, SelectQueryNode } from '../../types/index.js';
2
2
  import type { CompileQueryContext, SqlDialect } from './sql-dialect.js';
3
3
  export declare class ClickHouseDialect implements SqlDialect {
4
4
  readonly name = "clickhouse";
5
5
  private formatter;
6
- compileQuery(config: QueryConfig<any, any>, context: CompileQueryContext): string;
6
+ compileQuery(query: SelectQueryNode<any, any>, context: CompileQueryContext): CompiledQuery;
7
7
  formatTimeInterval(column: string, interval: string, method: string): string;
8
8
  formatSettings(settings: Record<string, unknown>): string;
9
9
  }
@@ -1 +1 @@
1
- {"version":3,"file":"clickhouse-dialect.d.ts","sourceRoot":"","sources":["../../../src/core/dialects/clickhouse-dialect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,OAAO,KAAK,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAExE,qBAAa,iBAAkB,YAAW,UAAU;IAClD,QAAQ,CAAC,IAAI,gBAAgB;IAC7B,OAAO,CAAC,SAAS,CAAsB;IAEvC,YAAY,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,mBAAmB,GAAG,MAAM;IAyCjF,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM;IAQ5E,cAAc,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM;CAK1D"}
1
+ {"version":3,"file":"clickhouse-dialect.d.ts","sourceRoot":"","sources":["../../../src/core/dialects/clickhouse-dialect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE3E,OAAO,KAAK,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAExE,qBAAa,iBAAkB,YAAW,UAAU;IAClD,QAAQ,CAAC,IAAI,gBAAgB;IAC7B,OAAO,CAAC,SAAS,CAAsB;IAEvC,YAAY,CAAC,KAAK,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,mBAAmB,GAAG,aAAa;IA6D3F,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM;IAQ5E,cAAc,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM;CAK1D"}
@@ -2,36 +2,53 @@ import { SQLFormatter } from '../formatters/sql-formatter.js';
2
2
  export class ClickHouseDialect {
3
3
  name = 'clickhouse';
4
4
  formatter = new SQLFormatter();
5
- compileQuery(config, context) {
5
+ compileQuery(query, context) {
6
6
  const parts = [];
7
- if (config.ctes?.length) {
8
- parts.push(`WITH ${config.ctes.join(', ')}`);
7
+ const parameters = [];
8
+ if (query.ctes?.length) {
9
+ parts.push(`WITH ${this.formatter.formatCtes(query)}`);
9
10
  }
10
- parts.push(`SELECT ${this.formatter.formatSelect(config)}`);
11
- parts.push(`FROM ${context.tableName}`);
12
- if (config.joins?.length) {
13
- parts.push(this.formatter.formatJoins(config));
11
+ parts.push(`SELECT ${this.formatter.formatSelect(query)}`);
12
+ parts.push(`FROM ${this.formatter.formatFrom(query.from ?? { kind: 'table', name: context.tableName })}`);
13
+ if (query.arrayJoins?.length) {
14
+ parts.push(this.formatter.formatArrayJoins(query));
14
15
  }
15
- if (config.where?.length) {
16
- parts.push(`WHERE ${this.formatter.formatWhere(config)}`);
16
+ if (query.joins?.length) {
17
+ parts.push(this.formatter.formatJoins(query));
17
18
  }
18
- if (config.groupBy?.length) {
19
- parts.push(`GROUP BY ${this.formatter.formatGroupBy(config)}`);
19
+ if (query.prewhere) {
20
+ const compiled = this.formatter.compileExpr(query.prewhere);
21
+ parts.push(`PREWHERE ${compiled.query}`);
22
+ parameters.push(...compiled.parameters);
20
23
  }
21
- if (config.having?.length) {
22
- parts.push(`HAVING ${config.having.join(' AND ')}`);
24
+ if (query.where) {
25
+ const compiled = this.formatter.compileExpr(query.where);
26
+ parts.push(`WHERE ${compiled.query}`);
27
+ parameters.push(...compiled.parameters);
23
28
  }
24
- if (config.orderBy?.length) {
25
- const orderBy = config.orderBy
26
- .map(({ column, direction }) => `${String(column)} ${direction}`.trim())
27
- .join(', ');
28
- parts.push(`ORDER BY ${orderBy}`);
29
+ if (query.groupBy?.length) {
30
+ const groupByClause = `GROUP BY ${this.formatter.formatGroupBy(query)}`;
31
+ parts.push(query.withTotals ? `${groupByClause} WITH TOTALS` : groupByClause);
29
32
  }
30
- if (config.limit) {
31
- const offsetClause = config.offset ? `OFFSET ${config.offset}` : '';
32
- parts.push(`LIMIT ${config.limit} ${offsetClause}`);
33
+ if (query.having?.length) {
34
+ const compiled = this.formatter.compileHaving(query);
35
+ parts.push(`HAVING ${compiled.query}`);
36
+ parameters.push(...compiled.parameters);
33
37
  }
34
- return parts.join(' ').trim();
38
+ if (query.orderBy?.length) {
39
+ parts.push(`ORDER BY ${this.formatter.formatOrderBy(query)}`);
40
+ }
41
+ if (query.limitBy) {
42
+ parts.push(`LIMIT ${this.formatter.formatLimitBy(query)}`);
43
+ }
44
+ if (query.limit) {
45
+ const offsetClause = query.offset ? `OFFSET ${query.offset}` : '';
46
+ parts.push(`LIMIT ${query.limit} ${offsetClause}`);
47
+ }
48
+ return {
49
+ query: parts.join(' ').trim(),
50
+ parameters,
51
+ };
35
52
  }
36
53
  formatTimeInterval(column, interval, method) {
37
54
  if (method === 'toStartOfInterval') {
@@ -1,10 +1,10 @@
1
- import type { QueryConfig } from '../../types/index.js';
1
+ import type { CompiledQuery, SelectQueryNode } from '../../types/index.js';
2
2
  export interface CompileQueryContext {
3
3
  tableName: string;
4
4
  }
5
5
  export interface SqlDialect {
6
6
  readonly name: string;
7
- compileQuery(config: QueryConfig<any, any>, context: CompileQueryContext): string;
7
+ compileQuery(query: SelectQueryNode<any, any>, context: CompileQueryContext): CompiledQuery;
8
8
  formatTimeInterval(column: string, interval: string, method: string): string;
9
9
  formatSettings(settings: Record<string, unknown>): string;
10
10
  }
@@ -1 +1 @@
1
- {"version":3,"file":"sql-dialect.d.ts","sourceRoot":"","sources":["../../../src/core/dialects/sql-dialect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,mBAAmB,GAAG,MAAM,CAAC;IAClF,kBAAkB,CAChB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GACb,MAAM,CAAC;IACV,cAAc,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC;CAC3D"}
1
+ {"version":3,"file":"sql-dialect.d.ts","sourceRoot":"","sources":["../../../src/core/dialects/sql-dialect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE3E,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,KAAK,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,mBAAmB,GAAG,aAAa,CAAC;IAC5F,kBAAkB,CAChB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GACb,MAAM,CAAC;IACV,cAAc,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC;CAC3D"}
@@ -1 +1 @@
1
- {"version":3,"file":"auto-client.d.ts","sourceRoot":"","sources":["../../../src/core/env/auto-client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAEpE,KAAK,gBAAgB,GAAG,cAAc,oBAAoB,CAAC,CAAC;AAE5D,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,gBAAgB,CAAC,cAAc,CAAC,CAAC;IAC/C,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;CACzC;AAID,wBAAgB,mBAAmB,IAAI,gBAAgB,CAqBtD"}
1
+ {"version":3,"file":"auto-client.d.ts","sourceRoot":"","sources":["../../../src/core/env/auto-client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAEpE,KAAK,gBAAgB,GAAG,cAAc,oBAAoB,CAAC,CAAC;AAE5D,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,gBAAgB,CAAC,cAAc,CAAC,CAAC;IAC/C,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;CACzC;AAKD,wBAAgB,mBAAmB,IAAI,gBAAgB,CAoBtD"}
@@ -1,9 +1,9 @@
1
1
  import { createRequire } from 'module';
2
2
  let cachedModule;
3
+ const nodeRequire = createRequire(import.meta.url);
3
4
  export function getAutoClientModule() {
4
5
  if (!cachedModule) {
5
6
  try {
6
- const nodeRequire = createRequire(`${process.cwd()}/noop.js`);
7
7
  const clientModule = nodeRequire('@clickhouse/client');
8
8
  const settings = clientModule.ClickHouseSettings;
9
9
  cachedModule = {
@@ -1,96 +1,39 @@
1
1
  import type { BuilderState, SchemaDefinition } from '../types/builder-state.js';
2
2
  import { QueryBuilder } from '../query-builder.js';
3
+ import type { SelectQueryNode, SelectionNode } from '../../types/index.js';
3
4
  export declare class AggregationFeature<Schema extends SchemaDefinition<Schema>, State extends BuilderState<Schema, string, any, keyof Schema, Partial<Record<string, keyof Schema>>>> {
4
5
  private builder;
6
+ private static readonly TRAILING_ALIAS_PATTERN;
7
+ private static readonly LEADING_AGGREGATE_CALL_PATTERN;
5
8
  constructor(builder: QueryBuilder<Schema, State>);
9
+ private stripTrailingAlias;
10
+ private isAggregateSelection;
11
+ private createAggregateSelection;
12
+ private shouldInferGroupByFromSelection;
13
+ private inferGroupBySelections;
6
14
  private createAggregation;
7
- sum(column: string, alias: string): {
8
- select: string[];
9
- where?: import("../../types/base.js").WhereCondition[];
10
- groupBy?: string[];
11
- having?: string[];
15
+ sum(column: string, alias: string): SelectQueryNode<State["output"], Schema>;
16
+ count(column: string, alias: string): SelectQueryNode<State["output"], Schema>;
17
+ avg(column: string, alias: string): SelectQueryNode<State["output"], Schema>;
18
+ min(column: string, alias: string): SelectQueryNode<State["output"], Schema>;
19
+ max(column: string, alias: string): SelectQueryNode<State["output"], Schema>;
20
+ countDistinct(column: string, alias: string): {
21
+ select: SelectionNode[];
22
+ kind: "select-query";
23
+ from?: import("../../types/base.js").SourceNode;
24
+ arrayJoins?: import("../../types/base.js").ArrayJoinNode[];
25
+ prewhere?: import("../../types/base.js").ExprNode;
26
+ where?: import("../../types/base.js").ExprNode;
27
+ groupBy?: import("../../types/base.js").GroupByItemNode[];
28
+ withTotals?: boolean;
29
+ having?: import("../../types/base.js").HavingNode[];
30
+ limitBy?: import("../../types/base.js").LimitByNode;
12
31
  limit?: number;
13
32
  offset?: number;
14
33
  distinct?: boolean;
15
- orderBy?: {
16
- column: keyof State["output"] | import("../../index.js").TableColumn<Schema>;
17
- direction: import("../../index.js").OrderDirection;
18
- }[] | undefined;
19
- joins?: import("../../types/base.js").JoinClause[];
20
- parameters?: any[];
21
- ctes?: string[];
22
- unionQueries?: string[];
23
- settings?: import("@clickhouse/client-common").ClickHouseSettings;
24
- };
25
- count(column: string, alias: string): {
26
- select: string[];
27
- where?: import("../../types/base.js").WhereCondition[];
28
- groupBy?: string[];
29
- having?: string[];
30
- limit?: number;
31
- offset?: number;
32
- distinct?: boolean;
33
- orderBy?: {
34
- column: keyof State["output"] | import("../../index.js").TableColumn<Schema>;
35
- direction: import("../../index.js").OrderDirection;
36
- }[] | undefined;
37
- joins?: import("../../types/base.js").JoinClause[];
38
- parameters?: any[];
39
- ctes?: string[];
40
- unionQueries?: string[];
41
- settings?: import("@clickhouse/client-common").ClickHouseSettings;
42
- };
43
- avg(column: string, alias: string): {
44
- select: string[];
45
- where?: import("../../types/base.js").WhereCondition[];
46
- groupBy?: string[];
47
- having?: string[];
48
- limit?: number;
49
- offset?: number;
50
- distinct?: boolean;
51
- orderBy?: {
52
- column: keyof State["output"] | import("../../index.js").TableColumn<Schema>;
53
- direction: import("../../index.js").OrderDirection;
54
- }[] | undefined;
55
- joins?: import("../../types/base.js").JoinClause[];
56
- parameters?: any[];
57
- ctes?: string[];
58
- unionQueries?: string[];
59
- settings?: import("@clickhouse/client-common").ClickHouseSettings;
60
- };
61
- min(column: string, alias: string): {
62
- select: string[];
63
- where?: import("../../types/base.js").WhereCondition[];
64
- groupBy?: string[];
65
- having?: string[];
66
- limit?: number;
67
- offset?: number;
68
- distinct?: boolean;
69
- orderBy?: {
70
- column: keyof State["output"] | import("../../index.js").TableColumn<Schema>;
71
- direction: import("../../index.js").OrderDirection;
72
- }[] | undefined;
73
- joins?: import("../../types/base.js").JoinClause[];
74
- parameters?: any[];
75
- ctes?: string[];
76
- unionQueries?: string[];
77
- settings?: import("@clickhouse/client-common").ClickHouseSettings;
78
- };
79
- max(column: string, alias: string): {
80
- select: string[];
81
- where?: import("../../types/base.js").WhereCondition[];
82
- groupBy?: string[];
83
- having?: string[];
84
- limit?: number;
85
- offset?: number;
86
- distinct?: boolean;
87
- orderBy?: {
88
- column: keyof State["output"] | import("../../index.js").TableColumn<Schema>;
89
- direction: import("../../index.js").OrderDirection;
90
- }[] | undefined;
91
- joins?: import("../../types/base.js").JoinClause[];
92
- parameters?: any[];
93
- ctes?: string[];
34
+ orderBy?: import("../../types/base.js").OrderByItemNode[];
35
+ joins?: import("../../types/base.js").JoinNode[];
36
+ ctes?: import("../../types/base.js").CteNode[];
94
37
  unionQueries?: string[];
95
38
  settings?: import("@clickhouse/client-common").ClickHouseSettings;
96
39
  };
@@ -1 +1 @@
1
- {"version":3,"file":"aggregations.d.ts","sourceRoot":"","sources":["../../../src/core/features/aggregations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAChF,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,qBAAa,kBAAkB,CAC7B,MAAM,SAAS,gBAAgB,CAAC,MAAM,CAAC,EACvC,KAAK,SAAS,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,MAAM,CAAC,CAAC,CAAC;IAExF,OAAO,CAAC,OAAO;gBAAP,OAAO,EAAE,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC;IAExD,OAAO,CAAC,iBAAiB;IAsBzB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;;;;;;;;;;;;;;;;;;IAIjC,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;;;;;;;;;;;;;;;;;;IAInC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;;;;;;;;;;;;;;;;;;IAIjC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;;;;;;;;;;;;;;;;;;IAIjC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;;;;;;;;;;;;;;;;;;CAGlC"}
1
+ {"version":3,"file":"aggregations.d.ts","sourceRoot":"","sources":["../../../src/core/features/aggregations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAChF,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE3E,qBAAa,kBAAkB,CAC7B,MAAM,SAAS,gBAAgB,CAAC,MAAM,CAAC,EACvC,KAAK,SAAS,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,MAAM,CAAC,CAAC,CAAC;IAKxF,OAAO,CAAC,OAAO;IAH3B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,sBAAsB,CAAsC;IACpF,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,8BAA8B,CAAoC;gBAEtE,OAAO,EAAE,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC;IAExD,OAAO,CAAC,kBAAkB;IAI1B,OAAO,CAAC,oBAAoB;IAK5B,OAAO,CAAC,wBAAwB;IAQhC,OAAO,CAAC,+BAA+B;IAYvC,OAAO,CAAC,sBAAsB;IAa9B,OAAO,CAAC,iBAAiB;IAsBzB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAIjC,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAInC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAIjC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAIjC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAIjC,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;CAiB5C"}
@@ -1,21 +1,58 @@
1
1
  export class AggregationFeature {
2
2
  builder;
3
+ static TRAILING_ALIAS_PATTERN = /\s+AS\s+[A-Za-z_][A-Za-z0-9_]*$/i;
4
+ static LEADING_AGGREGATE_CALL_PATTERN = /^(COUNT|SUM|AVG|MIN|MAX)\s*\(/i;
3
5
  constructor(builder) {
4
6
  this.builder = builder;
5
7
  }
8
+ stripTrailingAlias(selection) {
9
+ return selection.replace(AggregationFeature.TRAILING_ALIAS_PATTERN, '').trim();
10
+ }
11
+ isAggregateSelection(selection) {
12
+ const expressionWithoutAlias = this.stripTrailingAlias(selection);
13
+ return AggregationFeature.LEADING_AGGREGATE_CALL_PATTERN.test(expressionWithoutAlias);
14
+ }
15
+ createAggregateSelection(selection) {
16
+ return {
17
+ kind: 'selection',
18
+ selection,
19
+ isAggregate: true,
20
+ };
21
+ }
22
+ shouldInferGroupByFromSelection(item) {
23
+ if (item.selection === '*') {
24
+ return false;
25
+ }
26
+ if (item.isAggregate === true) {
27
+ return false;
28
+ }
29
+ return !this.isAggregateSelection(item.selection);
30
+ }
31
+ inferGroupBySelections(select) {
32
+ return select
33
+ .filter(item => this.shouldInferGroupByFromSelection(item))
34
+ .map(item => item.selection)
35
+ .map(selection => {
36
+ const aliasMatch = selection.match(/\s+AS\s+([A-Za-z_][A-Za-z0-9_]*)$/i);
37
+ return {
38
+ kind: 'group-by-item',
39
+ expression: aliasMatch ? aliasMatch[1] : selection,
40
+ };
41
+ });
42
+ }
6
43
  createAggregation(column, fn, alias) {
7
44
  const aggregationSQL = `${fn}(${column}) AS ${alias}`;
8
- const config = this.builder.getConfig();
9
- if (config.select) {
45
+ const query = this.builder.getQueryNode();
46
+ if (query.select) {
10
47
  return {
11
- ...config,
12
- select: [...(config.select || []).map(String), aggregationSQL],
13
- groupBy: (config.select || []).map(String).filter(col => !col.includes(' AS '))
48
+ ...query,
49
+ select: [...query.select, this.createAggregateSelection(aggregationSQL)],
50
+ groupBy: query.groupBy || this.inferGroupBySelections(query.select)
14
51
  };
15
52
  }
16
53
  return {
17
- ...config,
18
- select: [aggregationSQL]
54
+ ...query,
55
+ select: [this.createAggregateSelection(aggregationSQL)]
19
56
  };
20
57
  }
21
58
  sum(column, alias) {
@@ -33,4 +70,19 @@ export class AggregationFeature {
33
70
  max(column, alias) {
34
71
  return this.createAggregation(column, 'MAX', alias);
35
72
  }
73
+ countDistinct(column, alias) {
74
+ const aggregationSQL = `COUNT(DISTINCT ${column}) AS ${alias}`;
75
+ const query = this.builder.getQueryNode();
76
+ if (query.select) {
77
+ return {
78
+ ...query,
79
+ select: [...query.select, this.createAggregateSelection(aggregationSQL)],
80
+ groupBy: query.groupBy || this.inferGroupBySelections(query.select),
81
+ };
82
+ }
83
+ return {
84
+ ...query,
85
+ select: [this.createAggregateSelection(aggregationSQL)],
86
+ };
87
+ }
36
88
  }