@hypequery/clickhouse 0.0.0-canary-20260306132943

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/LICENSE +201 -0
  2. package/README-CLI.md +123 -0
  3. package/README.md +316 -0
  4. package/dist/cli/bin.js +285 -0
  5. package/dist/cli/generate-types.d.ts +5 -0
  6. package/dist/cli/generate-types.js +218 -0
  7. package/dist/cli/index.d.ts +2 -0
  8. package/dist/cli/index.js +2 -0
  9. package/dist/core/cache/cache-manager.d.ts +4 -0
  10. package/dist/core/cache/cache-manager.d.ts.map +1 -0
  11. package/dist/core/cache/cache-manager.js +176 -0
  12. package/dist/core/cache/controller.d.ts +15 -0
  13. package/dist/core/cache/controller.d.ts.map +1 -0
  14. package/dist/core/cache/controller.js +58 -0
  15. package/dist/core/cache/key.d.ts +11 -0
  16. package/dist/core/cache/key.d.ts.map +1 -0
  17. package/dist/core/cache/key.js +26 -0
  18. package/dist/core/cache/providers/memory-lru.d.ts +31 -0
  19. package/dist/core/cache/providers/memory-lru.d.ts.map +1 -0
  20. package/dist/core/cache/providers/memory-lru.js +156 -0
  21. package/dist/core/cache/providers/noop.d.ts +7 -0
  22. package/dist/core/cache/providers/noop.d.ts.map +1 -0
  23. package/dist/core/cache/providers/noop.js +11 -0
  24. package/dist/core/cache/runtime-context.d.ts +30 -0
  25. package/dist/core/cache/runtime-context.d.ts.map +1 -0
  26. package/dist/core/cache/runtime-context.js +58 -0
  27. package/dist/core/cache/serialization.d.ts +6 -0
  28. package/dist/core/cache/serialization.d.ts.map +1 -0
  29. package/dist/core/cache/serialization.js +166 -0
  30. package/dist/core/cache/types.d.ts +52 -0
  31. package/dist/core/cache/types.d.ts.map +1 -0
  32. package/dist/core/cache/types.js +1 -0
  33. package/dist/core/cache/utils.d.ts +9 -0
  34. package/dist/core/cache/utils.d.ts.map +1 -0
  35. package/dist/core/cache/utils.js +30 -0
  36. package/dist/core/connection.d.ts +112 -0
  37. package/dist/core/connection.d.ts.map +1 -0
  38. package/dist/core/connection.js +150 -0
  39. package/dist/core/cross-filter.d.ts +73 -0
  40. package/dist/core/cross-filter.d.ts.map +1 -0
  41. package/dist/core/cross-filter.js +142 -0
  42. package/dist/core/env/auto-client.browser.d.ts +3 -0
  43. package/dist/core/env/auto-client.browser.d.ts.map +1 -0
  44. package/dist/core/env/auto-client.browser.js +3 -0
  45. package/dist/core/env/auto-client.d.ts +9 -0
  46. package/dist/core/env/auto-client.d.ts.map +1 -0
  47. package/dist/core/env/auto-client.js +21 -0
  48. package/dist/core/features/aggregations.d.ts +98 -0
  49. package/dist/core/features/aggregations.d.ts.map +1 -0
  50. package/dist/core/features/aggregations.js +36 -0
  51. package/dist/core/features/analytics.d.ts +81 -0
  52. package/dist/core/features/analytics.d.ts.map +1 -0
  53. package/dist/core/features/analytics.js +45 -0
  54. package/dist/core/features/cross-filtering.d.ts +11 -0
  55. package/dist/core/features/cross-filtering.d.ts.map +1 -0
  56. package/dist/core/features/cross-filtering.js +90 -0
  57. package/dist/core/features/executor.d.ts +21 -0
  58. package/dist/core/features/executor.d.ts.map +1 -0
  59. package/dist/core/features/executor.js +146 -0
  60. package/dist/core/features/filtering.d.ts +99 -0
  61. package/dist/core/features/filtering.d.ts.map +1 -0
  62. package/dist/core/features/filtering.js +118 -0
  63. package/dist/core/features/joins.d.ts +26 -0
  64. package/dist/core/features/joins.d.ts.map +1 -0
  65. package/dist/core/features/joins.js +17 -0
  66. package/dist/core/features/query-modifiers.d.ts +116 -0
  67. package/dist/core/features/query-modifiers.d.ts.map +1 -0
  68. package/dist/core/features/query-modifiers.js +51 -0
  69. package/dist/core/formatters/sql-formatter.d.ts +9 -0
  70. package/dist/core/formatters/sql-formatter.d.ts.map +1 -0
  71. package/dist/core/formatters/sql-formatter.js +131 -0
  72. package/dist/core/join-relationships.d.ts +51 -0
  73. package/dist/core/join-relationships.d.ts.map +1 -0
  74. package/dist/core/join-relationships.js +54 -0
  75. package/dist/core/query-builder.d.ts +236 -0
  76. package/dist/core/query-builder.d.ts.map +1 -0
  77. package/dist/core/query-builder.js +495 -0
  78. package/dist/core/tests/index.d.ts +2 -0
  79. package/dist/core/tests/index.d.ts.map +1 -0
  80. package/dist/core/tests/index.js +1 -0
  81. package/dist/core/tests/integration/setup.d.ts +48 -0
  82. package/dist/core/tests/integration/setup.d.ts.map +1 -0
  83. package/dist/core/tests/integration/setup.js +349 -0
  84. package/dist/core/tests/integration/test-config.d.ts +15 -0
  85. package/dist/core/tests/integration/test-config.d.ts.map +1 -0
  86. package/dist/core/tests/integration/test-config.js +14 -0
  87. package/dist/core/tests/integration/test-data.json +190 -0
  88. package/dist/core/tests/test-utils.d.ts +44 -0
  89. package/dist/core/tests/test-utils.d.ts.map +1 -0
  90. package/dist/core/tests/test-utils.js +65 -0
  91. package/dist/core/types/builder-state.d.ts +27 -0
  92. package/dist/core/types/builder-state.d.ts.map +1 -0
  93. package/dist/core/types/builder-state.js +1 -0
  94. package/dist/core/types/select-types.d.ts +33 -0
  95. package/dist/core/types/select-types.d.ts.map +1 -0
  96. package/dist/core/types/select-types.js +1 -0
  97. package/dist/core/types/type-helpers.d.ts +5 -0
  98. package/dist/core/types/type-helpers.d.ts.map +1 -0
  99. package/dist/core/types/type-helpers.js +1 -0
  100. package/dist/core/utils/logger.d.ts +43 -0
  101. package/dist/core/utils/logger.d.ts.map +1 -0
  102. package/dist/core/utils/logger.js +104 -0
  103. package/dist/core/utils/predicate-builder.d.ts +33 -0
  104. package/dist/core/utils/predicate-builder.d.ts.map +1 -0
  105. package/dist/core/utils/predicate-builder.js +95 -0
  106. package/dist/core/utils/sql-expressions.d.ts +77 -0
  107. package/dist/core/utils/sql-expressions.d.ts.map +1 -0
  108. package/dist/core/utils/sql-expressions.js +54 -0
  109. package/dist/core/utils/streaming-helpers.d.ts +2 -0
  110. package/dist/core/utils/streaming-helpers.d.ts.map +1 -0
  111. package/dist/core/utils/streaming-helpers.js +137 -0
  112. package/dist/core/utils.d.ts +3 -0
  113. package/dist/core/utils.d.ts.map +1 -0
  114. package/dist/core/utils.js +29 -0
  115. package/dist/core/validators/filter-validator.d.ts +9 -0
  116. package/dist/core/validators/filter-validator.d.ts.map +1 -0
  117. package/dist/core/validators/filter-validator.js +19 -0
  118. package/dist/core/validators/value-validator.d.ts +7 -0
  119. package/dist/core/validators/value-validator.d.ts.map +1 -0
  120. package/dist/core/validators/value-validator.js +47 -0
  121. package/dist/formatters/index.d.ts +2 -0
  122. package/dist/formatters/index.d.ts.map +1 -0
  123. package/dist/formatters/index.js +1 -0
  124. package/dist/index.d.ts +20 -0
  125. package/dist/index.d.ts.map +1 -0
  126. package/dist/index.js +21 -0
  127. package/dist/types/base.d.ts +50 -0
  128. package/dist/types/base.d.ts.map +1 -0
  129. package/dist/types/base.js +1 -0
  130. package/dist/types/clickhouse-types.d.ts +17 -0
  131. package/dist/types/clickhouse-types.d.ts.map +1 -0
  132. package/dist/types/clickhouse-types.js +1 -0
  133. package/dist/types/filters.d.ts +53 -0
  134. package/dist/types/filters.d.ts.map +1 -0
  135. package/dist/types/filters.js +1 -0
  136. package/dist/types/index.d.ts +5 -0
  137. package/dist/types/index.d.ts.map +1 -0
  138. package/dist/types/index.js +4 -0
  139. package/dist/types/schema.d.ts +19 -0
  140. package/dist/types/schema.d.ts.map +1 -0
  141. package/dist/types/schema.js +1 -0
  142. package/package.json +90 -0
@@ -0,0 +1,495 @@
1
+ import { ClickHouseConnection } from './connection.js';
2
+ import { SQLFormatter } from './formatters/sql-formatter.js';
3
+ import { AggregationFeature } from './features/aggregations.js';
4
+ import { JoinFeature } from './features/joins.js';
5
+ import { FilteringFeature } from './features/filtering.js';
6
+ import { AnalyticsFeature } from './features/analytics.js';
7
+ import { ExecutorFeature } from './features/executor.js';
8
+ import { QueryModifiersFeature } from './features/query-modifiers.js';
9
+ import { FilterValidator } from './validators/filter-validator.js';
10
+ import { createPredicateBuilder, } from './utils/predicate-builder.js';
11
+ import { CrossFilteringFeature } from './features/cross-filtering.js';
12
+ import { executeWithCache } from './cache/cache-manager.js';
13
+ import { substituteParameters } from './utils.js';
14
+ import { mergeCacheOptionsPartial, initializeCacheRuntime } from './cache/utils.js';
15
+ /**
16
+ * Type guard to check if a config is a client-based configuration.
17
+ */
18
+ export function isClientConfig(config) {
19
+ return 'client' in config && config.client !== undefined;
20
+ }
21
+ /**
22
+ * A type-safe query builder for ClickHouse databases.
23
+ * The builder carries a single state object that encodes scope, output, and schema metadata.
24
+ */
25
+ export class QueryBuilder {
26
+ static relationships;
27
+ config = {};
28
+ tableName;
29
+ state;
30
+ formatter = new SQLFormatter();
31
+ aggregations;
32
+ joins;
33
+ filtering;
34
+ analytics;
35
+ executor;
36
+ modifiers;
37
+ crossFiltering;
38
+ runtime;
39
+ cacheOptions;
40
+ constructor(tableName, state, runtime) {
41
+ this.tableName = tableName;
42
+ this.state = state;
43
+ this.runtime = runtime;
44
+ this.aggregations = new AggregationFeature(this);
45
+ this.joins = new JoinFeature(this);
46
+ this.filtering = new FilteringFeature(this);
47
+ this.analytics = new AnalyticsFeature(this);
48
+ this.executor = new ExecutorFeature(this);
49
+ this.modifiers = new QueryModifiersFeature(this);
50
+ this.crossFiltering = new CrossFilteringFeature(this);
51
+ }
52
+ fork(state, config) {
53
+ const builder = new QueryBuilder(this.tableName, state, this.runtime);
54
+ builder.config = { ...config };
55
+ builder.cacheOptions = this.cacheOptions;
56
+ return builder;
57
+ }
58
+ debug() {
59
+ console.log('Current Type:', {
60
+ state: this.state,
61
+ config: this.config
62
+ });
63
+ return this;
64
+ }
65
+ cache(options) {
66
+ if (options === false) {
67
+ this.cacheOptions = { mode: 'no-store', ttlMs: 0, staleTtlMs: 0, cacheTimeMs: 0 };
68
+ return this;
69
+ }
70
+ this.cacheOptions = mergeCacheOptionsPartial(this.cacheOptions, options);
71
+ return this;
72
+ }
73
+ // --- Analytics Helper: Add a CTE.
74
+ withCTE(alias, subquery) {
75
+ this.config = this.analytics.addCTE(alias, subquery);
76
+ return this;
77
+ }
78
+ // --- Analytics Helper: Add a scalar WITH alias.
79
+ withScalar(alias, expressionBuilder) {
80
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(alias)) {
81
+ throw new Error(`Invalid scalar alias "${alias}". Use an unquoted SQL identifier (letters, numbers, underscore; cannot start with a number).`);
82
+ }
83
+ const expression = expressionBuilder(createPredicateBuilder());
84
+ const nextConfig = this.analytics.addScalar(alias, expression);
85
+ const nextState = {
86
+ ...this.state,
87
+ scalars: {
88
+ ...this.state.scalars,
89
+ [alias]: undefined,
90
+ },
91
+ };
92
+ const builder = new QueryBuilder(this.tableName, nextState, this.runtime);
93
+ builder.config = { ...nextConfig };
94
+ builder.cacheOptions = this.cacheOptions;
95
+ return builder;
96
+ }
97
+ /**
98
+ * Groups results by a time interval using a specified ClickHouse function.
99
+ *
100
+ * @param column - The column containing the date or timestamp.
101
+ * @param interval - The interval value. For example, "1 day" or "15 minute".
102
+ * This is only used when the method is 'toStartOfInterval'.
103
+ * @param method - The time bucketing function to use.
104
+ * Defaults to 'toStartOfInterval'.
105
+ * Other valid values include 'toStartOfMinute', 'toStartOfHour',
106
+ * 'toStartOfDay', 'toStartOfWeek', 'toStartOfMonth', 'toStartOfQuarter', and 'toStartOfYear'.
107
+ * @returns The current QueryBuilder instance.
108
+ */
109
+ groupByTimeInterval(column, interval, method = 'toStartOfInterval') {
110
+ this.config = this.analytics.addTimeInterval(String(column), interval, method);
111
+ return this;
112
+ }
113
+ // --- Analytics Helper: Add a raw SQL fragment.
114
+ raw(sql) {
115
+ // Use raw() to inject SQL that isn't supported by the builder.
116
+ // Use with caution.
117
+ this.config.having = this.config.having || [];
118
+ this.config.having.push(sql);
119
+ return this;
120
+ }
121
+ // --- Analytics Helper: Add query settings.
122
+ settings(opts) {
123
+ this.config = this.analytics.addSettings(opts);
124
+ return this;
125
+ }
126
+ applyCrossFilters(crossFilter) {
127
+ const normalized = crossFilter;
128
+ this.config = this.crossFiltering.applyCrossFilters(normalized);
129
+ return this;
130
+ }
131
+ select(columnsOrAsterisk) {
132
+ if (columnsOrAsterisk === '*') {
133
+ const nextState = {
134
+ ...this.state,
135
+ output: {}
136
+ };
137
+ const nextConfig = {
138
+ ...this.config,
139
+ select: ['*'],
140
+ orderBy: this.config.orderBy?.map(({ column, direction }) => ({
141
+ column: String(column),
142
+ direction
143
+ }))
144
+ };
145
+ return this.fork(nextState, nextConfig);
146
+ }
147
+ const columns = columnsOrAsterisk;
148
+ const processedColumns = columns.map(col => {
149
+ if (typeof col === 'object' && col !== null && '__type' in col) {
150
+ return col.toSql();
151
+ }
152
+ return String(col);
153
+ });
154
+ const nextState = {
155
+ ...this.state,
156
+ output: {}
157
+ };
158
+ const nextConfig = {
159
+ ...this.config,
160
+ select: processedColumns,
161
+ orderBy: this.config.orderBy?.map(({ column, direction }) => ({
162
+ column: String(column),
163
+ direction
164
+ }))
165
+ };
166
+ return this.fork(nextState, nextConfig);
167
+ }
168
+ selectConst(...columns) {
169
+ return this.select(columns);
170
+ }
171
+ sum(column, alias) {
172
+ return this.applyAggregation(column, alias, 'sum', (col, finalAlias) => this.aggregations.sum(col, finalAlias));
173
+ }
174
+ count(column, alias) {
175
+ return this.applyAggregation(column, alias, 'count', (col, finalAlias) => this.aggregations.count(col, finalAlias));
176
+ }
177
+ avg(column, alias) {
178
+ return this.applyAggregation(column, alias, 'avg', (col, finalAlias) => this.aggregations.avg(col, finalAlias));
179
+ }
180
+ min(column, alias) {
181
+ return this.applyAggregation(column, alias, 'min', (col, finalAlias) => this.aggregations.min(col, finalAlias));
182
+ }
183
+ max(column, alias) {
184
+ return this.applyAggregation(column, alias, 'max', (col, finalAlias) => this.aggregations.max(col, finalAlias));
185
+ }
186
+ applyAggregation(column, alias, suffix, updater) {
187
+ const columnName = String(column);
188
+ const finalAlias = (alias || `${columnName}_${suffix}`);
189
+ const nextState = {
190
+ ...this.state,
191
+ output: {}
192
+ };
193
+ const nextConfig = updater(columnName, finalAlias);
194
+ return this.fork(nextState, nextConfig);
195
+ }
196
+ // Make needed properties accessible to features
197
+ getTableName() {
198
+ return this.tableName;
199
+ }
200
+ getFormatter() {
201
+ return this.formatter;
202
+ }
203
+ getRuntimeContext() {
204
+ return this.runtime;
205
+ }
206
+ getCacheOptions() {
207
+ return this.cacheOptions;
208
+ }
209
+ getExecutor() {
210
+ return this.executor;
211
+ }
212
+ // Delegate execution methods to feature
213
+ toSQL() {
214
+ return this.executor.toSQL();
215
+ }
216
+ toSQLWithParams() {
217
+ return this.executor.toSQLWithParams();
218
+ }
219
+ execute(options) {
220
+ return executeWithCache(this, options);
221
+ }
222
+ async stream() {
223
+ return this.executor.stream();
224
+ }
225
+ /**
226
+ * Processes each row in a stream with the provided callback function
227
+ * @param callback Function to call for each row in the stream
228
+ */
229
+ async streamForEach(callback) {
230
+ const stream = await this.stream();
231
+ const reader = stream.getReader();
232
+ try {
233
+ while (true) {
234
+ const { done, value: rows } = await reader.read();
235
+ if (done)
236
+ break;
237
+ for (const row of rows) {
238
+ await callback(row);
239
+ }
240
+ }
241
+ }
242
+ finally {
243
+ reader.releaseLock();
244
+ }
245
+ }
246
+ validateFilterValue(column, operator, value) {
247
+ // Handle tuple columns
248
+ if (Array.isArray(column)) {
249
+ // For tuple operations, we don't validate individual column types
250
+ // as they might be cross-table references
251
+ return;
252
+ }
253
+ // Skip validation for advanced IN operators - they handle their own validation
254
+ const advancedInOperators = [
255
+ 'globalIn', 'globalNotIn', 'inSubquery', 'globalInSubquery',
256
+ 'inTable', 'globalInTable', 'inTuple', 'globalInTuple'
257
+ ];
258
+ if (advancedInOperators.includes(operator)) {
259
+ return;
260
+ }
261
+ const columnName = String(column);
262
+ if (FilterValidator.validateJoinedColumn(columnName))
263
+ return;
264
+ const baseColumns = this.state.base;
265
+ const columnType = baseColumns[columnName];
266
+ FilterValidator.validateFilterCondition({ column: columnName, operator, value }, columnType);
267
+ }
268
+ where(columnOrColumns, operator, value) {
269
+ if (typeof columnOrColumns === 'function') {
270
+ const expression = columnOrColumns(createPredicateBuilder());
271
+ this.config = this.filtering.addExpressionCondition('AND', expression);
272
+ return this;
273
+ }
274
+ if (operator === undefined) {
275
+ throw new Error('Operator is required when specifying a column for where()');
276
+ }
277
+ // Handle tuple operations
278
+ if (Array.isArray(columnOrColumns) && (operator === 'inTuple' || operator === 'globalInTuple')) {
279
+ const columns = columnOrColumns;
280
+ this.validateFilterValue(columns, operator, value);
281
+ this.config = this.filtering.addCondition('AND', columns.map(String), operator, value);
282
+ return this;
283
+ }
284
+ const column = columnOrColumns;
285
+ this.validateFilterValue(column, operator, value);
286
+ this.config = this.filtering.addCondition('AND', String(column), operator, value);
287
+ return this;
288
+ }
289
+ orWhere(columnOrColumns, operator, value) {
290
+ if (typeof columnOrColumns === 'function') {
291
+ const expression = columnOrColumns(createPredicateBuilder());
292
+ this.config = this.filtering.addExpressionCondition('OR', expression);
293
+ return this;
294
+ }
295
+ if (operator === undefined) {
296
+ throw new Error('Operator is required when specifying a column for orWhere()');
297
+ }
298
+ if (Array.isArray(columnOrColumns) && (operator === 'inTuple' || operator === 'globalInTuple')) {
299
+ const columns = columnOrColumns;
300
+ this.validateFilterValue(columns, operator, value);
301
+ this.config = this.filtering.addCondition('OR', columns.map(String), operator, value);
302
+ return this;
303
+ }
304
+ const column = columnOrColumns;
305
+ this.validateFilterValue(column, operator, value);
306
+ this.config = this.filtering.addCondition('OR', String(column), operator, value);
307
+ return this;
308
+ }
309
+ /**
310
+ * Creates a parenthesized group of WHERE conditions joined with AND/OR operators.
311
+ * @param {Function} callback - Function that builds the conditions within the group
312
+ * @returns {this} The current QueryBuilder instance
313
+ * @example
314
+ * ```ts
315
+ * builder.whereGroup(qb => {
316
+ * qb.where('status', 'eq', 'active').orWhere('status', 'eq', 'pending');
317
+ * })
318
+ * ```
319
+ */
320
+ whereGroup(callback) {
321
+ this.config = this.filtering.startWhereGroup();
322
+ callback(this);
323
+ this.config = this.filtering.endWhereGroup();
324
+ return this;
325
+ }
326
+ /**
327
+ * Creates a parenthesized group of WHERE conditions joined with OR operator.
328
+ * @param {Function} callback - Function that builds the conditions within the group
329
+ * @returns {this} The current QueryBuilder instance
330
+ * @example
331
+ * ```ts
332
+ * builder.orWhereGroup(qb => {
333
+ * qb.where('status', 'eq', 'active').orWhere('status', 'eq', 'pending');
334
+ * })
335
+ * ```
336
+ */
337
+ orWhereGroup(callback) {
338
+ this.config = this.filtering.startOrWhereGroup();
339
+ callback(this);
340
+ this.config = this.filtering.endWhereGroup();
341
+ return this;
342
+ }
343
+ /**
344
+ * Adds a GROUP BY clause.
345
+ * @param {keyof T | Array<keyof T>} columns - Column(s) to group by
346
+ * @returns {this} The current QueryBuilder instance
347
+ * @example
348
+ * ```ts
349
+ * builder.groupBy(['category', 'status'])
350
+ * ```
351
+ */
352
+ groupBy(columns) {
353
+ const normalized = Array.isArray(columns) ? columns.map(String) : String(columns);
354
+ this.config = this.modifiers.addGroupBy(normalized);
355
+ return this;
356
+ }
357
+ limit(count) {
358
+ this.config = this.modifiers.addLimit(count);
359
+ return this;
360
+ }
361
+ offset(count) {
362
+ this.config = this.modifiers.addOffset(count);
363
+ return this;
364
+ }
365
+ /**
366
+ * Adds an ORDER BY clause.
367
+ * @param {keyof T} column - The column to order by
368
+ * @param {OrderDirection} [direction='ASC'] - The sort direction
369
+ * @returns {this} The current QueryBuilder instance
370
+ * @example
371
+ * ```ts
372
+ * builder.orderBy('created_at', 'DESC')
373
+ * ```
374
+ */
375
+ orderBy(column, direction = 'ASC') {
376
+ this.config = this.modifiers.addOrderBy(String(column), direction);
377
+ return this;
378
+ }
379
+ /**
380
+ * Adds a HAVING clause for filtering grouped results.
381
+ * @param {string} condition - The HAVING condition
382
+ * @returns {this} The current QueryBuilder instance
383
+ * @example
384
+ * ```ts
385
+ * builder.having('COUNT(*) > 5')
386
+ * ```
387
+ */
388
+ having(condition, parameters) {
389
+ this.config = this.modifiers.addHaving(condition, parameters);
390
+ return this;
391
+ }
392
+ distinct() {
393
+ this.config = this.modifiers.setDistinct();
394
+ return this;
395
+ }
396
+ whereBetween(column, [min, max]) {
397
+ if (min === null || max === null) {
398
+ throw new Error('BETWEEN values cannot be null');
399
+ }
400
+ return this.where(column, 'between', [min, max]);
401
+ }
402
+ innerJoin(table, leftColumn, rightColumn, alias) {
403
+ return this.applyJoin('INNER', table, leftColumn, rightColumn, alias);
404
+ }
405
+ leftJoin(table, leftColumn, rightColumn, alias) {
406
+ return this.applyJoin('LEFT', table, leftColumn, rightColumn, alias);
407
+ }
408
+ rightJoin(table, leftColumn, rightColumn, alias) {
409
+ return this.applyJoin('RIGHT', table, leftColumn, rightColumn, alias);
410
+ }
411
+ fullJoin(table, leftColumn, rightColumn, alias) {
412
+ return this.applyJoin('FULL', table, leftColumn, rightColumn, alias);
413
+ }
414
+ applyJoin(type, table, leftColumn, rightColumn, alias) {
415
+ const nextState = {
416
+ ...this.state,
417
+ aliases: alias ? { ...this.state.aliases, [alias]: table } : this.state.aliases
418
+ };
419
+ const nextConfig = this.joins.addJoin(type, table, String(leftColumn), rightColumn, alias);
420
+ return this.fork(nextState, nextConfig);
421
+ }
422
+ // Make config accessible to features
423
+ getConfig() {
424
+ return this.config;
425
+ }
426
+ static setJoinRelationships(relationships) {
427
+ this.relationships = relationships;
428
+ }
429
+ /**
430
+ * Apply a predefined join relationship
431
+ */
432
+ withRelation(name, options) {
433
+ const relationships = QueryBuilder.relationships;
434
+ if (!relationships) {
435
+ throw new Error('Join relationships have not been initialized. Call QueryBuilder.setJoinRelationships first.');
436
+ }
437
+ const path = relationships.get(name);
438
+ if (!path) {
439
+ throw new Error(`Join relationship '${name}' not found`);
440
+ }
441
+ const applyJoin = (joinPath) => {
442
+ const type = options?.type || joinPath.type || 'INNER';
443
+ const alias = options?.alias || joinPath.alias;
444
+ const table = String(joinPath.to);
445
+ const rightColumn = `${table}.${joinPath.rightColumn}`;
446
+ this.config = this.joins.addJoin(type, table, joinPath.leftColumn, rightColumn, alias);
447
+ };
448
+ if (Array.isArray(path)) {
449
+ path.forEach(applyJoin);
450
+ }
451
+ else {
452
+ applyJoin(path);
453
+ }
454
+ return this;
455
+ }
456
+ }
457
+ function deriveNamespace(config) {
458
+ if (isClientConfig(config)) {
459
+ return 'client';
460
+ }
461
+ const host = 'host' in config ? config.host : 'unknown-host';
462
+ const database = 'database' in config ? config.database : 'default';
463
+ const username = 'username' in config ? config.username : 'default';
464
+ return `${host || 'unknown-host'}|${database || 'default'}|${username || 'default'}`;
465
+ }
466
+ export function createQueryBuilder(config) {
467
+ const { cache: cacheConfig, ...connectionConfig } = config;
468
+ ClickHouseConnection.initialize(connectionConfig);
469
+ const namespace = cacheConfig?.namespace || deriveNamespace(connectionConfig);
470
+ const { runtime, cacheController } = initializeCacheRuntime(cacheConfig, namespace);
471
+ return {
472
+ cache: cacheController,
473
+ async rawQuery(sql, params = []) {
474
+ const client = ClickHouseConnection.getClient();
475
+ const finalSQL = substituteParameters(sql, params);
476
+ const result = await client.query({
477
+ query: finalSQL,
478
+ format: 'JSONEachRow'
479
+ });
480
+ return result.json();
481
+ },
482
+ table(tableName) {
483
+ const state = {
484
+ schema: {},
485
+ tables: tableName,
486
+ output: {},
487
+ baseTable: tableName,
488
+ base: {},
489
+ aliases: {},
490
+ scalars: {}
491
+ };
492
+ return new QueryBuilder(tableName, state, runtime);
493
+ }
494
+ };
495
+ }
@@ -0,0 +1,2 @@
1
+ export * from './test-utils.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/tests/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC"}
@@ -0,0 +1 @@
1
+ export * from './test-utils.js';
@@ -0,0 +1,48 @@
1
+ export declare const TEST_CONNECTION_CONFIG: {
2
+ host: string;
3
+ user: string;
4
+ password: string;
5
+ database: string;
6
+ };
7
+ export declare const initializeTestConnection: () => Promise<{
8
+ cache: import("../../cache/controller.js").CacheController;
9
+ rawQuery<TResult = any>(sql: string, params?: unknown[]): Promise<TResult[][]>;
10
+ table<TableName extends never>(tableName: TableName): import("../../query-builder.js").SelectQB<import("../../types/builder-state.js").SchemaDefinition<unknown>, TableName, import("../../../index.js").TableRecord<import("../../types/builder-state.js").SchemaDefinition<unknown>[TableName]>, TableName>;
11
+ }>;
12
+ export declare const ensureConnectionInitialized: () => import("@clickhouse/client").ClickHouseClient | import("@clickhouse/client-web").ClickHouseClient;
13
+ export declare const isDockerAvailable: () => Promise<boolean>;
14
+ export declare const isDockerComposeAvailable: () => Promise<boolean>;
15
+ export declare const isContainerRunning: (containerName: string) => Promise<boolean>;
16
+ export declare const isClickHouseReady: () => Promise<boolean>;
17
+ export declare const startClickHouseContainer: () => Promise<void>;
18
+ export declare const waitForClickHouse: (maxAttempts?: number, retryInterval?: number) => Promise<void>;
19
+ export declare const stopClickHouseContainer: () => Promise<void>;
20
+ export interface TestSchemaType {
21
+ test_table: Array<{
22
+ id: number;
23
+ name: string;
24
+ category: string;
25
+ price: number;
26
+ created_at: string;
27
+ is_active: boolean;
28
+ }>;
29
+ users: Array<{
30
+ id: number;
31
+ user_name: string;
32
+ email: string;
33
+ status: string;
34
+ created_at: string;
35
+ }>;
36
+ orders: Array<{
37
+ id: number;
38
+ user_id: number;
39
+ product_id: number;
40
+ quantity: number;
41
+ total: number;
42
+ status: string;
43
+ created_at: string;
44
+ }>;
45
+ }
46
+ export declare const TEST_DATA: TestSchemaType;
47
+ export declare const setupTestDatabase: () => Promise<void>;
48
+ //# sourceMappingURL=setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../../../src/core/tests/integration/setup.ts"],"names":[],"mappings":"AAgDA,eAAO,MAAM,sBAAsB;;;;;CAAS,CAAC;AAG7C,eAAO,MAAM,wBAAwB;;;;EAyBpC,CAAC;AAGF,eAAO,MAAM,2BAA2B,yGAevC,CAAC;AAGF,eAAO,MAAM,iBAAiB,QAAa,OAAO,CAAC,OAAO,CAOzD,CAAC;AAGF,eAAO,MAAM,wBAAwB,QAAa,OAAO,CAAC,OAAO,CAahE,CAAC;AAGF,eAAO,MAAM,kBAAkB,GAAU,eAAe,MAAM,KAAG,OAAO,CAAC,OAAO,CAO/E,CAAC;AAGF,eAAO,MAAM,iBAAiB,QAAa,OAAO,CAAC,OAAO,CAQzD,CAAC;AAGF,eAAO,MAAM,wBAAwB,QAAa,OAAO,CAAC,IAAI,CAwC7D,CAAC;AAGF,eAAO,MAAM,iBAAiB,GAC5B,oBAAgB,EAChB,sBAAoB,KACnB,OAAO,CAAC,IAAI,CAad,CAAC;AAGF,eAAO,MAAM,uBAAuB,QAAa,OAAO,CAAC,IAAI,CA0B5D,CAAC;AAGF,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,KAAK,CAAC;QAChB,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;QACnB,SAAS,EAAE,OAAO,CAAC;KACpB,CAAC,CAAC;IACH,KAAK,EAAE,KAAK,CAAC;QACX,EAAE,EAAE,MAAM,CAAC;QACX,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;IACH,MAAM,EAAE,KAAK,CAAC;QACZ,EAAE,EAAE,MAAM,CAAC;QACX,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;CACJ;AAkDD,eAAO,MAAM,SAAS,EAAE,cAAoC,CAAC;AAK7D,eAAO,MAAM,iBAAiB,QAAa,OAAO,CAAC,IAAI,CAkGtD,CAAC"}