@hypequery/clickhouse 2.0.0 → 2.0.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.
Files changed (83) hide show
  1. package/dist/core/features/aggregations.d.ts +27 -1
  2. package/dist/core/features/aggregations.d.ts.map +1 -1
  3. package/dist/core/features/aggregations.js +43 -3
  4. package/dist/core/query-builder.d.ts +1 -0
  5. package/dist/core/query-builder.d.ts.map +1 -1
  6. package/dist/core/query-builder.js +3 -0
  7. package/dist/core/tests/integration/setup.d.ts +2 -10
  8. package/dist/core/tests/integration/setup.d.ts.map +1 -1
  9. package/dist/core/tests/integration/setup.js +30 -251
  10. package/dist/core/utils.d.ts.map +1 -1
  11. package/dist/core/utils.js +2 -1
  12. package/dist/datasets.d.ts +41 -0
  13. package/dist/datasets.d.ts.map +1 -0
  14. package/dist/datasets.js +387 -0
  15. package/dist/migrations/config/index.d.ts +3 -0
  16. package/dist/migrations/config/index.d.ts.map +1 -0
  17. package/dist/migrations/config/index.js +1 -0
  18. package/dist/migrations/config/types.d.ts +45 -0
  19. package/dist/migrations/config/types.d.ts.map +1 -0
  20. package/dist/migrations/config/types.js +28 -0
  21. package/dist/migrations/diff/diff.d.ts +11 -0
  22. package/dist/migrations/diff/diff.d.ts.map +1 -0
  23. package/dist/migrations/diff/diff.js +240 -0
  24. package/dist/migrations/diff/index.d.ts +3 -0
  25. package/dist/migrations/diff/index.d.ts.map +1 -0
  26. package/dist/migrations/diff/index.js +1 -0
  27. package/dist/migrations/diff/types.d.ts +74 -0
  28. package/dist/migrations/diff/types.d.ts.map +1 -0
  29. package/dist/migrations/diff/types.js +1 -0
  30. package/dist/migrations/introspect/index.d.ts +3 -0
  31. package/dist/migrations/introspect/index.d.ts.map +1 -0
  32. package/dist/migrations/introspect/index.js +1 -0
  33. package/dist/migrations/introspect/pull-schema.d.ts +23 -0
  34. package/dist/migrations/introspect/pull-schema.d.ts.map +1 -0
  35. package/dist/migrations/introspect/pull-schema.js +135 -0
  36. package/dist/migrations/plan/index.d.ts +3 -0
  37. package/dist/migrations/plan/index.d.ts.map +1 -0
  38. package/dist/migrations/plan/index.js +1 -0
  39. package/dist/migrations/plan/plan.d.ts +12 -0
  40. package/dist/migrations/plan/plan.d.ts.map +1 -0
  41. package/dist/migrations/plan/plan.js +416 -0
  42. package/dist/migrations/plan/types.d.ts +93 -0
  43. package/dist/migrations/plan/types.d.ts.map +1 -0
  44. package/dist/migrations/plan/types.js +1 -0
  45. package/dist/migrations/schema/column.d.ts +71 -0
  46. package/dist/migrations/schema/column.d.ts.map +1 -0
  47. package/dist/migrations/schema/column.js +123 -0
  48. package/dist/migrations/schema/define.d.ts +24 -0
  49. package/dist/migrations/schema/define.d.ts.map +1 -0
  50. package/dist/migrations/schema/define.js +47 -0
  51. package/dist/migrations/schema/index.d.ts +4 -0
  52. package/dist/migrations/schema/index.d.ts.map +1 -0
  53. package/dist/migrations/schema/index.js +2 -0
  54. package/dist/migrations/schema/types.d.ts +74 -0
  55. package/dist/migrations/schema/types.d.ts.map +1 -0
  56. package/dist/migrations/schema/types.js +1 -0
  57. package/dist/migrations/snapshot/index.d.ts +3 -0
  58. package/dist/migrations/snapshot/index.d.ts.map +1 -0
  59. package/dist/migrations/snapshot/index.js +1 -0
  60. package/dist/migrations/snapshot/serialize.d.ts +21 -0
  61. package/dist/migrations/snapshot/serialize.d.ts.map +1 -0
  62. package/dist/migrations/snapshot/serialize.js +127 -0
  63. package/dist/migrations/snapshot/types.d.ts +47 -0
  64. package/dist/migrations/snapshot/types.d.ts.map +1 -0
  65. package/dist/migrations/snapshot/types.js +1 -0
  66. package/dist/migrations/sql/index.d.ts +4 -0
  67. package/dist/migrations/sql/index.d.ts.map +1 -0
  68. package/dist/migrations/sql/index.js +2 -0
  69. package/dist/migrations/sql/render.d.ts +10 -0
  70. package/dist/migrations/sql/render.d.ts.map +1 -0
  71. package/dist/migrations/sql/render.js +347 -0
  72. package/dist/migrations/sql/types.d.ts +53 -0
  73. package/dist/migrations/sql/types.d.ts.map +1 -0
  74. package/dist/migrations/sql/types.js +1 -0
  75. package/dist/migrations/sql/write.d.ts +10 -0
  76. package/dist/migrations/sql/write.d.ts.map +1 -0
  77. package/dist/migrations/sql/write.js +35 -0
  78. package/dist/semantic-backend.d.ts +7 -0
  79. package/dist/semantic-backend.d.ts.map +1 -0
  80. package/dist/semantic-backend.js +208 -0
  81. package/dist/types/base.d.ts +1 -0
  82. package/dist/types/base.d.ts.map +1 -1
  83. package/package.json +1 -1
@@ -0,0 +1,416 @@
1
+ /**
2
+ * Converts a raw snapshot diff into an explicit migration plan.
3
+ *
4
+ * The initial planner is intentionally static: it classifies operations and
5
+ * carries diagnostics/blockers without querying a live ClickHouse instance.
6
+ * Later phases can enrich this plan with system-table cost estimates.
7
+ */
8
+ export function createMigrationPlan(diff, options = {}) {
9
+ const plannedOperations = diff.operations.map((operation, operationIndex) => planOperation(operation, operationIndex, diff, options));
10
+ const blockerDiagnostics = diff.unsupportedChanges.map((change) => ({
11
+ level: 'error',
12
+ kind: change.kind,
13
+ message: change.message,
14
+ }));
15
+ const warningDiagnostics = diff.warnings.map((warning) => ({
16
+ level: 'warning',
17
+ kind: warning.kind,
18
+ message: warning.message,
19
+ }));
20
+ const operationDiagnostics = plannedOperations.flatMap(operation => operation.diagnostics);
21
+ const requiredConfirmations = plannedOperations.flatMap(operation => operation.requiredConfirmations);
22
+ const recommendedSyncSettings = dedupeSyncSettings(plannedOperations.flatMap(operation => operation.recommendedSyncSettings));
23
+ const staticBlockers = plannedOperations.flatMap((operation, operationIndex) => operation.classification === 'forbidden'
24
+ ? [{
25
+ kind: 'ForbiddenOperation',
26
+ operationIndex,
27
+ message: forbiddenOperationMessage(operation.operation),
28
+ }]
29
+ : []);
30
+ const unsupportedBlockers = diff.unsupportedChanges.map(change => ({
31
+ kind: change.kind,
32
+ message: change.message,
33
+ tableName: change.tableName,
34
+ ...(change.columnName !== undefined ? { columnName: change.columnName } : {}),
35
+ }));
36
+ const plan = {
37
+ previousSnapshot: diff.previousSnapshot,
38
+ nextSnapshot: diff.nextSnapshot,
39
+ sourceSnapshotHash: diff.previousSnapshot.contentHash,
40
+ targetSnapshotHash: diff.nextSnapshot.contentHash,
41
+ operations: plannedOperations,
42
+ diagnostics: [
43
+ ...blockerDiagnostics,
44
+ ...warningDiagnostics,
45
+ ...operationDiagnostics,
46
+ ],
47
+ blockers: [
48
+ ...unsupportedBlockers,
49
+ ...staticBlockers,
50
+ ],
51
+ requiredConfirmations,
52
+ recommendedSyncSettings,
53
+ // Backward-compatible alias. These settings are recommendations for the future executor,
54
+ // not proof that generated SQL has applied them.
55
+ requiredSyncSettings: recommendedSyncSettings,
56
+ };
57
+ const analyzerResult = runAnalyzers(plan, diff, options.analyzers ?? [], options.context);
58
+ if (analyzerResult.diagnostics.length === 0 &&
59
+ analyzerResult.blockers.length === 0 &&
60
+ analyzerResult.confirmations.length === 0) {
61
+ return plan;
62
+ }
63
+ return {
64
+ ...plan,
65
+ diagnostics: [
66
+ ...plan.diagnostics,
67
+ ...analyzerResult.diagnostics,
68
+ ],
69
+ blockers: [
70
+ ...plan.blockers,
71
+ ...analyzerResult.blockers,
72
+ ],
73
+ requiredConfirmations: [
74
+ ...plan.requiredConfirmations,
75
+ ...analyzerResult.confirmations,
76
+ ],
77
+ };
78
+ }
79
+ export function isMigrationPlan(input) {
80
+ return 'blockers' in input &&
81
+ 'diagnostics' in input &&
82
+ 'requiredConfirmations' in input &&
83
+ Array.isArray(input.operations) &&
84
+ input.operations.every(operation => 'operation' in operation && 'classification' in operation);
85
+ }
86
+ function planOperation(operation, operationIndex, diff, options) {
87
+ const classification = classifyOperation(operation, diff.previousSnapshot.tables);
88
+ const costEstimate = estimateOperationCost(operation, options.context);
89
+ const diagnostics = diagnosticsForOperation(operation, classification, operationIndex, costEstimate, options.context);
90
+ const requiredConfirmations = confirmationsForOperation(operation, classification, operationIndex, options);
91
+ return {
92
+ operation,
93
+ classification,
94
+ costEstimate,
95
+ diagnostics,
96
+ requiredConfirmations,
97
+ recommendedSyncSettings: syncSettingsForOperation(operation, classification),
98
+ requiredSyncSettings: syncSettingsForOperation(operation, classification),
99
+ };
100
+ }
101
+ function classifyOperation(operation, previousTables) {
102
+ switch (operation.kind) {
103
+ case 'ModifyColumnType':
104
+ return isKeyColumn(previousTables, operation.tableName, operation.columnName)
105
+ ? 'forbidden'
106
+ : 'mutation';
107
+ case 'AlterTableWithDependentViews':
108
+ return classifyAlterTableWithDependentViews(operation, previousTables);
109
+ case 'DropColumn':
110
+ return isKeyColumn(previousTables, operation.tableName, operation.columnName)
111
+ ? 'forbidden'
112
+ : 'metadata';
113
+ case 'AddColumn':
114
+ case 'CreateMaterializedView':
115
+ case 'CreateTable':
116
+ case 'DropMaterializedView':
117
+ case 'DropTable':
118
+ case 'ModifyColumnDefault':
119
+ case 'RecreateMaterializedView':
120
+ return 'metadata';
121
+ default: {
122
+ const exhaustiveCheck = operation;
123
+ return exhaustiveCheck;
124
+ }
125
+ }
126
+ }
127
+ function classifyAlterTableWithDependentViews(operation, previousTables) {
128
+ const nestedClassifications = operation.operations.map(nestedOperation => classifyTableMutationOperation(nestedOperation, previousTables));
129
+ if (nestedClassifications.includes('forbidden')) {
130
+ return 'forbidden';
131
+ }
132
+ if (nestedClassifications.includes('mutation')) {
133
+ return 'mutation';
134
+ }
135
+ if (nestedClassifications.includes('data-copy')) {
136
+ return 'data-copy';
137
+ }
138
+ return 'metadata';
139
+ }
140
+ function classifyTableMutationOperation(operation, previousTables) {
141
+ if ((operation.kind === 'DropColumn' || operation.kind === 'ModifyColumnType') &&
142
+ isKeyColumn(previousTables, operation.tableName, operation.columnName)) {
143
+ return 'forbidden';
144
+ }
145
+ return operation.kind === 'ModifyColumnType' ? 'mutation' : 'metadata';
146
+ }
147
+ function diagnosticsForOperation(operation, classification, operationIndex, costEstimate, context) {
148
+ const diagnostics = [];
149
+ if (classification === 'mutation') {
150
+ diagnostics.push({
151
+ level: 'warning',
152
+ kind: 'MutationOperation',
153
+ operationIndex,
154
+ message: `Operation "${operation.kind}" may trigger a ClickHouse mutation. ` +
155
+ 'Review table size and mutation impact before applying.',
156
+ });
157
+ }
158
+ if (classification === 'forbidden') {
159
+ diagnostics.push({
160
+ level: 'error',
161
+ kind: 'ForbiddenOperation',
162
+ operationIndex,
163
+ message: forbiddenOperationMessage(operation),
164
+ });
165
+ }
166
+ if (operation.kind === 'DropTable') {
167
+ diagnostics.push({
168
+ level: 'warning',
169
+ kind: 'DestructiveDropTable',
170
+ operationIndex,
171
+ message: `Operation drops table "${operation.tableName}".`,
172
+ });
173
+ }
174
+ if (operation.kind === 'DropColumn') {
175
+ diagnostics.push({
176
+ level: 'warning',
177
+ kind: 'DestructiveDropColumn',
178
+ operationIndex,
179
+ message: `Operation drops column "${operation.tableName}.${operation.columnName}".`,
180
+ });
181
+ }
182
+ diagnostics.push(...costDiagnosticsForOperation(operation, classification, operationIndex, costEstimate, context));
183
+ return diagnostics;
184
+ }
185
+ function confirmationsForOperation(operation, classification, operationIndex, options) {
186
+ if (classification !== 'mutation' || options.requireConfirmationForMutations === false) {
187
+ return [];
188
+ }
189
+ return [
190
+ {
191
+ kind: 'MutationRequiresConfirmation',
192
+ operationIndex,
193
+ message: `Operation "${operation.kind}" is classified as a mutation and should require explicit confirmation.`,
194
+ },
195
+ ];
196
+ }
197
+ function syncSettingsForOperation(operation, classification) {
198
+ if (classification === 'forbidden') {
199
+ return [];
200
+ }
201
+ const settings = [];
202
+ if (operation.kind !== 'CreateTable' && operation.kind !== 'CreateMaterializedView') {
203
+ settings.push({
204
+ name: 'alter_sync',
205
+ value: 2,
206
+ reason: 'Wait for ClickHouse ALTER/DDL operations to finish on active replicas where supported.',
207
+ });
208
+ }
209
+ if (classification === 'mutation') {
210
+ settings.push({
211
+ name: 'mutations_sync',
212
+ value: 2,
213
+ reason: 'Wait for mutation-triggering ALTER operations to finish before marking the step complete.',
214
+ });
215
+ }
216
+ return settings;
217
+ }
218
+ function dedupeSyncSettings(settings) {
219
+ const seen = new Set();
220
+ const deduped = [];
221
+ for (const setting of settings) {
222
+ const key = `${setting.name}:${setting.value}`;
223
+ if (seen.has(key)) {
224
+ continue;
225
+ }
226
+ seen.add(key);
227
+ deduped.push(setting);
228
+ }
229
+ return deduped;
230
+ }
231
+ function runAnalyzers(plan, diff, analyzers, context) {
232
+ const results = analyzers.map((analyzer, index) => {
233
+ try {
234
+ return analyzer(plan, { diff, clickhouse: context });
235
+ }
236
+ catch (error) {
237
+ const message = error instanceof Error ? error.message : String(error);
238
+ return {
239
+ diagnostics: [{
240
+ level: 'error',
241
+ kind: 'AnalyzerError',
242
+ message: `Analyzer at index ${index} failed: ${message}`,
243
+ }],
244
+ blockers: [{
245
+ kind: 'AnalyzerError',
246
+ message: `Analyzer at index ${index} failed: ${message}`,
247
+ }],
248
+ };
249
+ }
250
+ });
251
+ return {
252
+ diagnostics: results.flatMap(result => result.diagnostics ?? []),
253
+ blockers: results.flatMap(result => result.blockers ?? []),
254
+ confirmations: results.flatMap(result => result.confirmations ?? []),
255
+ };
256
+ }
257
+ function estimateOperationCost(operation, context) {
258
+ const tableName = getOperationTableName(operation);
259
+ if (!tableName) {
260
+ return {
261
+ source: 'static',
262
+ confidence: 'none',
263
+ };
264
+ }
265
+ const stats = getTableStats(context, tableName);
266
+ if (!stats) {
267
+ return {
268
+ tableName,
269
+ source: 'static',
270
+ confidence: 'none',
271
+ };
272
+ }
273
+ return {
274
+ tableName,
275
+ affectedRows: stats.totalRows,
276
+ affectedBytes: stats.totalBytes,
277
+ activeParts: stats.activeParts,
278
+ pendingMutations: stats.pendingMutations,
279
+ replicaAbsoluteDelaySeconds: stats.replicaAbsoluteDelaySeconds,
280
+ replicaQueueSize: stats.replicaQueueSize,
281
+ source: 'provided-context',
282
+ confidence: 'medium',
283
+ };
284
+ }
285
+ function costDiagnosticsForOperation(operation, classification, operationIndex, costEstimate, context) {
286
+ if (costEstimate.source !== 'provided-context') {
287
+ return [];
288
+ }
289
+ const diagnostics = [];
290
+ const mutationWarningBytes = atLeast(context?.mutationWarningBytes, 0, 1024 ** 3);
291
+ const mutationWarningRows = atLeast(context?.mutationWarningRows, 1, 100_000_000);
292
+ const activePartsWarningThreshold = atLeast(context?.activePartsWarningThreshold, 1, 200);
293
+ const pendingMutationsWarningThreshold = atLeast(context?.pendingMutationsWarningThreshold, 0, 0);
294
+ const replicaDelayWarningSeconds = atLeast(context?.replicaDelayWarningSeconds, 0, 60);
295
+ if (classification === 'mutation' &&
296
+ costEstimate.affectedBytes !== undefined &&
297
+ costEstimate.affectedBytes >= mutationWarningBytes) {
298
+ diagnostics.push({
299
+ level: 'warning',
300
+ kind: 'ExpensiveMutationBytes',
301
+ operationIndex,
302
+ message: `Operation "${operation.kind}" may mutate ${costEstimate.affectedBytes} bytes ` +
303
+ `on table "${costEstimate.tableName}".`,
304
+ });
305
+ }
306
+ if (classification === 'mutation' &&
307
+ costEstimate.affectedRows !== undefined &&
308
+ costEstimate.affectedRows >= mutationWarningRows) {
309
+ diagnostics.push({
310
+ level: 'warning',
311
+ kind: 'ExpensiveMutationRows',
312
+ operationIndex,
313
+ message: `Operation "${operation.kind}" may mutate ${costEstimate.affectedRows} rows ` +
314
+ `on table "${costEstimate.tableName}".`,
315
+ });
316
+ }
317
+ if (costEstimate.activeParts !== undefined && costEstimate.activeParts >= activePartsWarningThreshold) {
318
+ diagnostics.push({
319
+ level: 'warning',
320
+ kind: 'HighActivePartCount',
321
+ operationIndex,
322
+ message: `Table "${costEstimate.tableName}" has ${costEstimate.activeParts} active parts. ` +
323
+ 'Consider reducing part pressure before applying DDL.',
324
+ });
325
+ }
326
+ if (costEstimate.pendingMutations !== undefined &&
327
+ costEstimate.pendingMutations > pendingMutationsWarningThreshold) {
328
+ diagnostics.push({
329
+ level: 'warning',
330
+ kind: 'PendingMutations',
331
+ operationIndex,
332
+ message: `Table "${costEstimate.tableName}" has ${costEstimate.pendingMutations} pending mutation(s).`,
333
+ });
334
+ }
335
+ if (costEstimate.replicaAbsoluteDelaySeconds !== undefined &&
336
+ costEstimate.replicaAbsoluteDelaySeconds >= replicaDelayWarningSeconds) {
337
+ diagnostics.push({
338
+ level: 'warning',
339
+ kind: 'ReplicaDelay',
340
+ operationIndex,
341
+ message: `Table "${costEstimate.tableName}" has replica delay of ` +
342
+ `${costEstimate.replicaAbsoluteDelaySeconds} second(s).`,
343
+ });
344
+ }
345
+ return diagnostics;
346
+ }
347
+ function forbiddenOperationMessage(operation) {
348
+ if (operation.kind === 'DropColumn' || operation.kind === 'ModifyColumnType') {
349
+ return [
350
+ `Cannot ${operation.kind === 'DropColumn' ? 'drop' : 'modify'} key column ` +
351
+ `"${operation.tableName}.${operation.columnName}" automatically.`,
352
+ 'ClickHouse key columns participate in ORDER BY, PRIMARY KEY, PARTITION BY, or SAMPLE BY expressions.',
353
+ 'Recommended approach: create a new table with the desired schema, backfill data in controlled batches, swap tables, then drop the old table.',
354
+ 'Use a custom SQL migration if you understand the operational risk.',
355
+ ].join(' ');
356
+ }
357
+ return [
358
+ `Operation "${operation.kind}" cannot be generated safely.`,
359
+ 'Use a custom SQL migration or split the schema evolution into supported steps.',
360
+ ].join(' ');
361
+ }
362
+ function atLeast(value, minimum, fallback) {
363
+ if (value === undefined || Number.isNaN(value)) {
364
+ return fallback;
365
+ }
366
+ return Math.max(minimum, value);
367
+ }
368
+ function getOperationTableName(operation) {
369
+ switch (operation.kind) {
370
+ case 'AddColumn':
371
+ case 'AlterTableWithDependentViews':
372
+ case 'DropColumn':
373
+ case 'DropTable':
374
+ case 'ModifyColumnDefault':
375
+ case 'ModifyColumnType':
376
+ return operation.tableName;
377
+ case 'CreateTable':
378
+ return operation.table.name;
379
+ case 'CreateMaterializedView':
380
+ case 'DropMaterializedView':
381
+ case 'RecreateMaterializedView':
382
+ return undefined;
383
+ default: {
384
+ const exhaustiveCheck = operation;
385
+ return exhaustiveCheck;
386
+ }
387
+ }
388
+ }
389
+ function getTableStats(context, tableName) {
390
+ if (!context?.tables) {
391
+ return undefined;
392
+ }
393
+ if (Array.isArray(context.tables)) {
394
+ return context.tables.find(table => table.tableName === tableName);
395
+ }
396
+ return context.tables[tableName];
397
+ }
398
+ function isKeyColumn(tables, tableName, columnName) {
399
+ const table = tables.find(candidate => candidate.name === tableName);
400
+ if (!table) {
401
+ return false;
402
+ }
403
+ return [
404
+ ...table.engine.orderBy,
405
+ ...table.engine.primaryKey,
406
+ table.engine.partitionBy,
407
+ table.engine.sampleBy,
408
+ ].some(expression => expression !== undefined && expressionReferencesColumn(expression, columnName));
409
+ }
410
+ function expressionReferencesColumn(expression, columnName) {
411
+ if (expression === columnName) {
412
+ return true;
413
+ }
414
+ const escapedColumn = columnName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
415
+ return new RegExp(`(^|[^A-Za-z0-9_])${escapedColumn}([^A-Za-z0-9_]|$)`).test(expression);
416
+ }
@@ -0,0 +1,93 @@
1
+ import type { MigrationOperation, SnapshotDiffResult } from '../diff/types.js';
2
+ import type { Snapshot } from '../snapshot/types.js';
3
+ export type MigrationOperationClassification = 'metadata' | 'mutation' | 'data-copy' | 'forbidden';
4
+ export type MigrationPlanDiagnosticLevel = 'warning' | 'error';
5
+ export interface MigrationPlanDiagnostic {
6
+ level: MigrationPlanDiagnosticLevel;
7
+ kind: string;
8
+ message: string;
9
+ operationIndex?: number;
10
+ }
11
+ export interface MigrationPlanBlocker {
12
+ kind: string;
13
+ message: string;
14
+ tableName?: string;
15
+ columnName?: string;
16
+ operationIndex?: number;
17
+ }
18
+ export interface MigrationPlanConfirmation {
19
+ kind: string;
20
+ message: string;
21
+ operationIndex: number;
22
+ }
23
+ export interface ClickHouseMigrationSyncSetting {
24
+ name: 'alter_sync' | 'mutations_sync' | 'replication_alter_partitions_sync' | 'distributed_ddl_task_timeout';
25
+ value: string | number;
26
+ reason: string;
27
+ }
28
+ export interface ClickHouseTableCostStats {
29
+ tableName: string;
30
+ totalRows?: number;
31
+ totalBytes?: number;
32
+ activeParts?: number;
33
+ pendingMutations?: number;
34
+ replicaAbsoluteDelaySeconds?: number;
35
+ replicaQueueSize?: number;
36
+ }
37
+ export interface ClickHouseMigrationPlanContext {
38
+ tables?: Record<string, ClickHouseTableCostStats> | ClickHouseTableCostStats[];
39
+ mutationWarningBytes?: number;
40
+ mutationWarningRows?: number;
41
+ activePartsWarningThreshold?: number;
42
+ pendingMutationsWarningThreshold?: number;
43
+ replicaDelayWarningSeconds?: number;
44
+ }
45
+ export interface MigrationOperationCostEstimate {
46
+ tableName?: string;
47
+ affectedRows?: number;
48
+ affectedBytes?: number;
49
+ activeParts?: number;
50
+ pendingMutations?: number;
51
+ replicaAbsoluteDelaySeconds?: number;
52
+ replicaQueueSize?: number;
53
+ source: 'static' | 'provided-context';
54
+ confidence: 'none' | 'low' | 'medium';
55
+ }
56
+ export interface PlannedMigrationOperation {
57
+ operation: MigrationOperation;
58
+ classification: MigrationOperationClassification;
59
+ costEstimate: MigrationOperationCostEstimate;
60
+ diagnostics: MigrationPlanDiagnostic[];
61
+ requiredConfirmations: MigrationPlanConfirmation[];
62
+ recommendedSyncSettings: ClickHouseMigrationSyncSetting[];
63
+ requiredSyncSettings: ClickHouseMigrationSyncSetting[];
64
+ }
65
+ export interface MigrationPlan {
66
+ previousSnapshot: Snapshot;
67
+ nextSnapshot: Snapshot;
68
+ sourceSnapshotHash: string;
69
+ targetSnapshotHash: string;
70
+ operations: PlannedMigrationOperation[];
71
+ diagnostics: MigrationPlanDiagnostic[];
72
+ blockers: MigrationPlanBlocker[];
73
+ requiredConfirmations: MigrationPlanConfirmation[];
74
+ recommendedSyncSettings: ClickHouseMigrationSyncSetting[];
75
+ requiredSyncSettings: ClickHouseMigrationSyncSetting[];
76
+ }
77
+ export interface MigrationPlanAnalyzerContext {
78
+ diff: SnapshotDiffResult;
79
+ clickhouse?: ClickHouseMigrationPlanContext;
80
+ }
81
+ export interface MigrationPlanAnalyzerResult {
82
+ diagnostics?: MigrationPlanDiagnostic[];
83
+ blockers?: MigrationPlanBlocker[];
84
+ confirmations?: MigrationPlanConfirmation[];
85
+ }
86
+ export type MigrationPlanAnalyzer = (plan: MigrationPlan, context: MigrationPlanAnalyzerContext) => MigrationPlanAnalyzerResult;
87
+ export interface CreateMigrationPlanOptions {
88
+ requireConfirmationForMutations?: boolean;
89
+ analyzers?: MigrationPlanAnalyzer[];
90
+ context?: ClickHouseMigrationPlanContext;
91
+ }
92
+ export type MigrationPlanInput = SnapshotDiffResult | MigrationPlan;
93
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/migrations/plan/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,71 @@
1
+ import type { ClickHouseColumnBuilderLike, ClickHouseColumnDefaultValue, ClickHouseDefaultInput, ClickHouseColumnDefinition, ClickHouseColumnType } from './types.js';
2
+ type NestedColumnTypeInput = string | ClickHouseColumnBuilder | ClickHouseColumnType;
3
+ /**
4
+ * Immutable builder for ClickHouse column definitions.
5
+ *
6
+ * Each modifier returns a new builder so reusable column fragments can be shared
7
+ * safely without later calls mutating earlier definitions.
8
+ */
9
+ export declare class ClickHouseColumnBuilder implements ClickHouseColumnBuilderLike {
10
+ private readonly columnType;
11
+ private readonly defaultExpression?;
12
+ constructor(columnType: ClickHouseColumnType, defaultExpression?: ClickHouseColumnDefaultValue | undefined);
13
+ /**
14
+ * Adds a column default.
15
+ *
16
+ * Primitive values are rendered as SQL literals. Use the `sql` template tag for
17
+ * database expressions such as `sql\`now()\``.
18
+ */
19
+ default(expression: ClickHouseDefaultInput): ClickHouseColumnBuilder;
20
+ /**
21
+ * Wraps this column type in `Nullable(...)`.
22
+ */
23
+ nullable(): ClickHouseColumnBuilder;
24
+ /**
25
+ * Wraps this column type in `LowCardinality(...)`.
26
+ */
27
+ lowCardinality(): ClickHouseColumnBuilder;
28
+ /**
29
+ * Materializes the builder into a named column definition for a table AST.
30
+ */
31
+ build(name: string): ClickHouseColumnDefinition;
32
+ /**
33
+ * Returns the underlying type AST for nested type builders.
34
+ */
35
+ toColumnType(): ClickHouseColumnType;
36
+ }
37
+ /**
38
+ * Factory helpers for common ClickHouse column types.
39
+ *
40
+ * Examples:
41
+ * `column.String().default('pending')`, `column.Nullable('String')`,
42
+ * `column.DateTime('UTC')`.
43
+ */
44
+ export declare const column: {
45
+ Int8: () => ClickHouseColumnBuilder;
46
+ Int16: () => ClickHouseColumnBuilder;
47
+ Int32: () => ClickHouseColumnBuilder;
48
+ Int64: () => ClickHouseColumnBuilder;
49
+ Int128: () => ClickHouseColumnBuilder;
50
+ Int256: () => ClickHouseColumnBuilder;
51
+ UInt8: () => ClickHouseColumnBuilder;
52
+ UInt16: () => ClickHouseColumnBuilder;
53
+ UInt32: () => ClickHouseColumnBuilder;
54
+ UInt64: () => ClickHouseColumnBuilder;
55
+ UInt128: () => ClickHouseColumnBuilder;
56
+ UInt256: () => ClickHouseColumnBuilder;
57
+ Float32: () => ClickHouseColumnBuilder;
58
+ Float64: () => ClickHouseColumnBuilder;
59
+ Decimal: (precision: number, scale: number) => ClickHouseColumnBuilder;
60
+ String: () => ClickHouseColumnBuilder;
61
+ FixedString: (length: number) => ClickHouseColumnBuilder;
62
+ Date: () => ClickHouseColumnBuilder;
63
+ DateTime: (timezone?: string) => ClickHouseColumnBuilder;
64
+ DateTime64: (precision: number, timezone?: string) => ClickHouseColumnBuilder;
65
+ UUID: () => ClickHouseColumnBuilder;
66
+ JSON: () => ClickHouseColumnBuilder;
67
+ LowCardinality: (inner: NestedColumnTypeInput) => ClickHouseColumnBuilder;
68
+ Nullable: (inner: NestedColumnTypeInput) => ClickHouseColumnBuilder;
69
+ };
70
+ export {};
71
+ //# sourceMappingURL=column.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"column.d.ts","sourceRoot":"","sources":["../../../src/migrations/schema/column.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,2BAA2B,EAC3B,4BAA4B,EAC5B,sBAAsB,EACtB,0BAA0B,EAC1B,oBAAoB,EAErB,MAAM,YAAY,CAAC;AAIpB,KAAK,qBAAqB,GAAG,MAAM,GAAG,uBAAuB,GAAG,oBAAoB,CAAC;AAErF;;;;;GAKG;AACH,qBAAa,uBAAwB,YAAW,2BAA2B;IAEvE,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC;gBADlB,UAAU,EAAE,oBAAoB,EAChC,iBAAiB,CAAC,EAAE,4BAA4B,YAAA;IAGnE;;;;;OAKG;IACH,OAAO,CAAC,UAAU,EAAE,sBAAsB,GAAG,uBAAuB;IAIpE;;OAEG;IACH,QAAQ,IAAI,uBAAuB;IAOnC;;OAEG;IACH,cAAc,IAAI,uBAAuB;IAOzC;;OAEG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,0BAA0B;IAQ/C;;OAEG;IACH,YAAY,IAAI,oBAAoB;CAGrC;AAED;;;;;;GAMG;AACH,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;yBAeI,MAAM,SAAS,MAAM;;0BAEpB,MAAM;;0BAEN,MAAM;4BACJ,MAAM,aAAa,MAAM;;;4BAIzB,qBAAqB;sBAK3B,qBAAqB;CAKxC,CAAC"}