@intentius/chant-lexicon-sql 0.124.31 → 0.124.33
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.
- package/dist/change-set.js +250 -0
- package/dist/clickhouse/access.js +722 -0
- package/dist/clickhouse/apply/apply.js +243 -0
- package/dist/clickhouse/apply/mutations.js +66 -0
- package/dist/clickhouse/apply/statements.js +545 -0
- package/dist/clickhouse/catalog-types.js +7 -0
- package/dist/clickhouse/container.js +152 -0
- package/dist/clickhouse/engine-syntax.js +159 -0
- package/dist/clickhouse/entities.js +593 -0
- package/dist/clickhouse/expressions.js +107 -0
- package/dist/clickhouse/http.js +55 -0
- package/dist/clickhouse/import/generator.js +192 -0
- package/dist/clickhouse/import/inlined.js +207 -0
- package/dist/clickhouse/import/ir.js +160 -0
- package/dist/clickhouse/import/live-export.js +131 -0
- package/dist/clickhouse/import/parser.js +17 -0
- package/dist/clickhouse/live/bind.js +116 -0
- package/dist/clickhouse/live/catalog.js +223 -0
- package/dist/clickhouse/live/describe-resources.js +91 -0
- package/dist/clickhouse/live/undeclarable.js +59 -0
- package/dist/clickhouse/overlays/codecs.js +57 -0
- package/dist/clickhouse/overlays/engines.js +101 -0
- package/dist/clickhouse/overlays/kinds.js +1 -0
- package/dist/clickhouse/overlays/projections.js +26 -0
- package/dist/clickhouse/overlays/skip-indexes.js +40 -0
- package/dist/clickhouse/overlays/ttl.js +34 -0
- package/dist/clickhouse/overlays/types.js +59 -0
- package/dist/clickhouse/ownership.js +83 -0
- package/dist/clickhouse/parser.js +674 -0
- package/dist/clickhouse/plan/commands.js +159 -0
- package/dist/clickhouse/plan/deep.js +345 -0
- package/dist/clickhouse/plan/diff.js +312 -0
- package/dist/clickhouse/plan/disruption.js +73 -0
- package/dist/clickhouse/plan/normalize.js +545 -0
- package/dist/clickhouse/plan/rebuild-handoff.js +57 -0
- package/dist/clickhouse/plan/report.js +26 -0
- package/dist/clickhouse/plan/rules.js +72 -0
- package/dist/clickhouse/plan/schema.js +67 -0
- package/dist/clickhouse/plan/server-format.js +35 -0
- package/dist/clickhouse/rebuild/backfill.js +265 -0
- package/dist/clickhouse/rebuild/engines.js +146 -0
- package/dist/clickhouse/rebuild/observe.js +260 -0
- package/dist/clickhouse/rebuild/op.js +152 -0
- package/dist/clickhouse/rebuild/partitions.js +65 -0
- package/dist/clickhouse/rebuild/receipts.js +120 -0
- package/dist/clickhouse/rebuild/replicas.js +121 -0
- package/dist/clickhouse/rebuild/shards.js +76 -0
- package/dist/clickhouse/rebuild/steps.js +383 -0
- package/dist/clickhouse/rebuild/verify.js +279 -0
- package/dist/clickhouse/row-types.js +151 -0
- package/dist/clickhouse/testing/fake-server.js +47 -0
- package/dist/clickhouse/testing/roundtrip-schema.js +25 -0
- package/dist/clickhouse/testing/writable-server.js +123 -0
- package/dist/clickhouse/tokens.js +23 -0
- package/dist/clickhouse/topology.js +463 -0
- package/dist/clickhouse/type-syntax.js +73 -0
- package/dist/clickhouse.js +25 -0
- package/dist/codegen/classifier-page.js +93 -0
- package/dist/codegen/clickhouse-module.js +249 -0
- package/dist/codegen/coverage-page.js +70 -0
- package/dist/codegen/docs-cli.js +3 -0
- package/dist/codegen/docs.js +222 -0
- package/dist/codegen/generate-cli.js +13 -0
- package/dist/codegen/generate.js +97 -0
- package/dist/codegen/package.js +28 -0
- package/dist/codegen/postgres-classifier-page.js +95 -0
- package/dist/codegen/postgres-module.js +418 -0
- package/dist/codegen/registry.js +52 -0
- package/dist/composites/audit-log-table.js +35 -0
- package/dist/composites/catalog.js +524 -0
- package/dist/composites/cdc-mirror.js +32 -0
- package/dist/composites/clickhouse.js +6 -0
- package/dist/composites/events-table.js +26 -0
- package/dist/composites/index.js +8 -0
- package/dist/composites/join-table.js +25 -0
- package/dist/composites/postgres.js +6 -0
- package/dist/composites/refreshed-view.js +24 -0
- package/dist/composites/replacing-table.js +28 -0
- package/dist/composites/rollup-view.js +29 -0
- package/dist/composites/sharded-table.js +27 -0
- package/dist/composites/soft-delete-table.js +34 -0
- package/dist/composites/tenant-table.js +26 -0
- package/dist/composites/typed.js +8 -0
- package/dist/config.js +206 -0
- package/dist/core/apply.js +93 -0
- package/dist/core/classifier.js +115 -0
- package/dist/core/container.js +69 -0
- package/dist/core/cursor.js +261 -0
- package/dist/core/diff.js +48 -0
- package/dist/core/entity.js +58 -0
- package/dist/core/find-templates.js +118 -0
- package/dist/core/foreign-tables.js +27 -0
- package/dist/core/handoff.js +32 -0
- package/dist/core/index.js +64 -0
- package/dist/core/interpolation.js +191 -0
- package/dist/core/normalize.js +43 -0
- package/dist/core/ownership.js +119 -0
- package/dist/core/receipts.js +22 -0
- package/dist/core/references.js +61 -0
- package/dist/core/statements.js +42 -0
- package/dist/core/template.js +79 -0
- package/dist/core/testing/http-stub.js +21 -0
- package/dist/core/tokens.js +202 -0
- package/dist/core/typed.js +40 -0
- package/dist/core/undeclarable.js +46 -0
- package/dist/coverage.js +161 -0
- package/dist/detect.js +32 -0
- package/dist/dialects.js +18 -0
- package/dist/files/build-root.js +72 -0
- package/dist/files/clickhouse.js +138 -0
- package/dist/files/common.js +58 -0
- package/dist/files/postgres.js +389 -0
- package/dist/generated/clickhouse-types.js +5 -0
- package/dist/generated/clickhouse.js +4361 -0
- package/dist/generated/postgres-types.js +227 -0
- package/dist/generated/postgres.js +3667 -0
- package/dist/import-generator.js +14 -0
- package/dist/import-parser.js +71 -0
- package/dist/index.js +24 -0
- package/dist/init-templates.js +290 -0
- package/dist/integrity.json +2 -2
- package/dist/lint/audit-catalog.js +31 -0
- package/dist/lint/audit-lineage.js +22 -0
- package/dist/lint/post-synth/audit-entries.js +34 -0
- package/dist/lint/post-synth/clickhouse-helpers.js +79 -0
- package/dist/lint/post-synth/clickhouse-type-check.js +162 -0
- package/dist/lint/post-synth/index.js +112 -0
- package/dist/lint/post-synth/postgres-audit-entries.js +35 -0
- package/dist/lint/post-synth/postgres-helpers.js +65 -0
- package/dist/lint/post-synth/postgres-names.js +507 -0
- package/dist/lint/post-synth/sql-helpers.js +23 -0
- package/dist/lint/post-synth/sql101.js +59 -0
- package/dist/lint/post-synth/sql102.js +34 -0
- package/dist/lint/post-synth/sqlch101.js +34 -0
- package/dist/lint/post-synth/sqlch102.js +23 -0
- package/dist/lint/post-synth/sqlch103.js +45 -0
- package/dist/lint/post-synth/sqlch104.js +41 -0
- package/dist/lint/post-synth/sqlch105.js +36 -0
- package/dist/lint/post-synth/sqlch106.js +27 -0
- package/dist/lint/post-synth/sqlch107.js +39 -0
- package/dist/lint/post-synth/sqlch108.js +28 -0
- package/dist/lint/post-synth/sqlch109.js +21 -0
- package/dist/lint/post-synth/sqlch110.js +31 -0
- package/dist/lint/post-synth/sqlch111.js +41 -0
- package/dist/lint/post-synth/sqlch112.js +28 -0
- package/dist/lint/post-synth/sqlch113.js +19 -0
- package/dist/lint/post-synth/sqlch114.js +26 -0
- package/dist/lint/post-synth/sqlch115.js +30 -0
- package/dist/lint/post-synth/sqlch116.js +22 -0
- package/dist/lint/post-synth/sqlch117.js +26 -0
- package/dist/lint/post-synth/sqlch118.js +27 -0
- package/dist/lint/post-synth/sqlch119.js +32 -0
- package/dist/lint/post-synth/sqlch120.js +20 -0
- package/dist/lint/post-synth/sqlch121.js +11 -0
- package/dist/lint/post-synth/sqlch122.js +11 -0
- package/dist/lint/post-synth/sqlch123.js +10 -0
- package/dist/lint/post-synth/sqlch124.js +21 -0
- package/dist/lint/post-synth/sqlch125.js +55 -0
- package/dist/lint/post-synth/sqlch126.js +62 -0
- package/dist/lint/post-synth/sqlch127.js +132 -0
- package/dist/lint/post-synth/sqlpg101.js +25 -0
- package/dist/lint/post-synth/sqlpg102.js +35 -0
- package/dist/lint/post-synth/sqlpg103.js +23 -0
- package/dist/lint/post-synth/sqlpg104.js +23 -0
- package/dist/lint/post-synth/sqlpg105.js +22 -0
- package/dist/lint/post-synth/sqlpg106.js +22 -0
- package/dist/lint/post-synth/sqlpg107.js +22 -0
- package/dist/lint/post-synth/sqlpg108.js +28 -0
- package/dist/lint/post-synth/sqlpg109.js +39 -0
- package/dist/lint/post-synth/sqlpg110.js +38 -0
- package/dist/lint/post-synth/sqlpg111.js +23 -0
- package/dist/lint/post-synth/sqlpg112.js +25 -0
- package/dist/lint/post-synth/sqlpg113.js +29 -0
- package/dist/lint/post-synth/sqlpg114.js +39 -0
- package/dist/lint/post-synth/sqlpg115.js +48 -0
- package/dist/lint/post-synth/sqlpg116.js +26 -0
- package/dist/lint/post-synth/sqlpg117.js +24 -0
- package/dist/lint/post-synth/sqlpg118.js +22 -0
- package/dist/lint/post-synth/sqlpg119.js +41 -0
- package/dist/lint/post-synth/sqlpg120.js +83 -0
- package/dist/lint/post-synth/sqlpg121.js +33 -0
- package/dist/lint/post-synth/sqlpg122.js +27 -0
- package/dist/lint/post-synth/sqlpg123.js +69 -0
- package/dist/lint/post-synth/sqlpg124.js +50 -0
- package/dist/lint/post-synth/sqlpg125.js +41 -0
- package/dist/lint/post-synth/sqlpg126.js +38 -0
- package/dist/lint/rules/index.js +12 -0
- package/dist/lint/rules/postgres-templates.js +27 -0
- package/dist/lint/rules/sqlch001.js +79 -0
- package/dist/lint/rules/sqlch002.js +124 -0
- package/dist/lint/rules/sqlch003.js +69 -0
- package/dist/lint/rules/sqlch004.js +83 -0
- package/dist/lint/rules/sqlpg001.js +68 -0
- package/dist/lint/rules/sqlpg002.js +69 -0
- package/dist/lint/rules/sqlpg003.js +58 -0
- package/dist/lint/rules/sqlpg004.js +77 -0
- package/dist/lint/rules/sqlpg005.js +86 -0
- package/dist/lint/rules/templates.js +25 -0
- package/dist/lint/rules/typed-names.js +132 -0
- package/dist/live-dialect.js +32 -0
- package/dist/lsp/catalog.js +44 -0
- package/dist/lsp/completions.js +136 -0
- package/dist/lsp/hover.js +115 -0
- package/dist/lsp/postgres-catalog.js +176 -0
- package/dist/lsp/postgres-template.js +171 -0
- package/dist/lsp/postgres.js +266 -0
- package/dist/lsp/refs.js +60 -0
- package/dist/lsp/registry.d.ts.map +1 -1
- package/dist/lsp/registry.js +19 -0
- package/dist/lsp/template.js +143 -0
- package/dist/manifest.json +1 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +293 -0
- package/dist/mcp/postgres.js +117 -0
- package/dist/migration-statements.js +243 -0
- package/dist/op/activities/clickhouse-apply.js +83 -0
- package/dist/op/activities/clickhouse-emulator.js +53 -0
- package/dist/op/activities/clickhouse-rebuild.js +119 -0
- package/dist/op/activities/index.js +26 -0
- package/dist/op/activities/postgres-apply.js +126 -0
- package/dist/op/activities/postgres-emulator.js +70 -0
- package/dist/op/activities/postgres-migration.js +156 -0
- package/dist/op/activities/sql-exec.js +57 -0
- package/dist/op/activity-contracts.js +103 -0
- package/dist/package-cli.js +14 -0
- package/dist/plugin.js +259 -0
- package/dist/postgres/access/acl.js +344 -0
- package/dist/postgres/access/changes.js +30 -0
- package/dist/postgres/access/import.js +61 -0
- package/dist/postgres/access/live.js +96 -0
- package/dist/postgres/apply/apply.js +557 -0
- package/dist/postgres/apply/statements.js +882 -0
- package/dist/postgres/catalog-types.js +18 -0
- package/dist/postgres/container.js +58 -0
- package/dist/postgres/entities.js +1390 -0
- package/dist/postgres/entity-types.js +28 -0
- package/dist/postgres/import/generator.js +283 -0
- package/dist/postgres/import/ir.js +53 -0
- package/dist/postgres/import/live-export.js +83 -0
- package/dist/postgres/keywords.js +32 -0
- package/dist/postgres/live/bind.js +130 -0
- package/dist/postgres/live/catalog-kinds.js +301 -0
- package/dist/postgres/live/catalog.js +644 -0
- package/dist/postgres/live/client.js +151 -0
- package/dist/postgres/live/describe-resources.js +135 -0
- package/dist/postgres/live/undeclarable.js +111 -0
- package/dist/postgres/migrate/backfill.js +170 -0
- package/dist/postgres/migrate/batches.js +122 -0
- package/dist/postgres/migrate/carry.js +518 -0
- package/dist/postgres/migrate/handoff.js +59 -0
- package/dist/postgres/migrate/names.js +61 -0
- package/dist/postgres/migrate/observe.js +360 -0
- package/dist/postgres/migrate/op.js +178 -0
- package/dist/postgres/migrate/receipts.js +117 -0
- package/dist/postgres/migrate/replication.js +106 -0
- package/dist/postgres/migrate/steps.js +514 -0
- package/dist/postgres/overlays/columns.js +31 -0
- package/dist/postgres/overlays/constraints.js +52 -0
- package/dist/postgres/overlays/extension-objects.js +88 -0
- package/dist/postgres/overlays/indexes.js +25 -0
- package/dist/postgres/overlays/kinds.js +2 -0
- package/dist/postgres/overlays/partitioning.js +23 -0
- package/dist/postgres/overlays/relations.js +25 -0
- package/dist/postgres/overlays/storage.js +65 -0
- package/dist/postgres/overlays/type-ddl.js +36 -0
- package/dist/postgres/overlays/types.js +74 -0
- package/dist/postgres/parser.js +1824 -0
- package/dist/postgres/plan/commands.js +137 -0
- package/dist/postgres/plan/deep.js +324 -0
- package/dist/postgres/plan/diff.js +499 -0
- package/dist/postgres/plan/disruption.js +64 -0
- package/dist/postgres/plan/normalize.js +889 -0
- package/dist/postgres/plan/report.js +21 -0
- package/dist/postgres/plan/rules.js +115 -0
- package/dist/postgres/plan/schema.js +105 -0
- package/dist/postgres/plan/server-normalize.js +166 -0
- package/dist/postgres/providers/aurora.js +44 -0
- package/dist/postgres/providers/azure.js +53 -0
- package/dist/postgres/providers/cloud-sql.js +41 -0
- package/dist/postgres/providers/index.js +73 -0
- package/dist/postgres/providers/neon.js +44 -0
- package/dist/postgres/providers/rds.js +47 -0
- package/dist/postgres/providers/supabase.js +45 -0
- package/dist/postgres/providers/types.js +21 -0
- package/dist/postgres/row-types.js +107 -0
- package/dist/postgres/testing/fake-client.js +34 -0
- package/dist/postgres/testing/migration.js +48 -0
- package/dist/postgres/testing/roundtrip-schema.js +64 -0
- package/dist/postgres/testing/server.js +47 -0
- package/dist/postgres/testing/writable-server.js +160 -0
- package/dist/postgres/tokens.js +41 -0
- package/dist/postgres.js +15 -0
- package/dist/read/clickhouse.js +82 -0
- package/dist/read/objects.js +44 -0
- package/dist/read/postgres.js +104 -0
- package/dist/read.js +52 -0
- package/dist/receipts.js +207 -0
- package/dist/serializer.js +172 -0
- package/dist/skill-defs.js +93 -0
- package/dist/spec/catalog.js +191 -0
- package/dist/spec/fetch.d.ts.map +1 -1
- package/dist/spec/fetch.js +90 -0
- package/dist/spec/pin.js +42 -0
- package/dist/spec/postgres-catalog.js +171 -0
- package/dist/spec/postgres-fetch.js +91 -0
- package/dist/spec/postgres-pin.js +82 -0
- package/dist/sql-files.js +206 -0
- package/dist/sql-types-command.js +97 -0
- package/dist/sql-types.js +323 -0
- package/dist/token-source.js +156 -0
- package/dist/validate-cli.js +5 -0
- package/dist/validate.js +151 -0
- package/dist/workspace-outputs.js +191 -0
- package/package.json +7 -7
- package/src/codegen/package.ts +2 -2
- package/src/lint/rules/typescript-7.test.ts +2 -1
- package/src/lsp/catalog.ts +2 -2
- package/src/lsp/postgres-catalog.ts +2 -2
- package/src/lsp/registry.ts +2 -1
- package/src/mcp/index.ts +4 -2
- package/src/spec/fetch.ts +2 -2
- package/src/spec/postgres-fetch.ts +2 -2
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The diff between two ClickHouse schemas, each change classified.
|
|
3
|
+
*
|
|
4
|
+
* Identity (#3047 question 3):
|
|
5
|
+
*
|
|
6
|
+
* - An object is identified by its export name between two revisions of the
|
|
7
|
+
* declarations, so a changed name in the SQL under the same export is a
|
|
8
|
+
* rename, not a drop and a create. Against a live server, which has no
|
|
9
|
+
* export names, it is identified by `database.name`, and a declaration
|
|
10
|
+
* carrying `-- previously: <old name>` before its CREATE is matched to the
|
|
11
|
+
* object of that name.
|
|
12
|
+
* - A column is identified by its name: the SQL form has no column key apart
|
|
13
|
+
* from the name. A column renamed in place says so with
|
|
14
|
+
* `-- previously: <old name>` on its line; without the hint a renamed
|
|
15
|
+
* column reads as a drop and an add, and the diff points that out when the
|
|
16
|
+
* two look alike.
|
|
17
|
+
*/
|
|
18
|
+
import { CLASSIFIER_RULES } from "./rules.js";
|
|
19
|
+
import { MERGE_TREE_SETTINGS } from "../../generated/clickhouse.js";
|
|
20
|
+
import { classifiedChange } from "../../core/classifier.js";
|
|
21
|
+
import { changedEntries, matchByIdentity } from "../../core/diff.js";
|
|
22
|
+
const qualified = (o) => (o.database ? `${o.database}.${o.name}` : o.name);
|
|
23
|
+
function change(object, field, rule, before, after, extra = {}) {
|
|
24
|
+
return classifiedChange(CLASSIFIER_RULES, object, field, rule, before, after, extra);
|
|
25
|
+
}
|
|
26
|
+
/** The column names a key expression mentions. */
|
|
27
|
+
function keyColumns(o) {
|
|
28
|
+
const out = new Set();
|
|
29
|
+
for (const k of [o.orderBy, o.primaryKey ?? o.orderBy, o.partitionBy, o.sampleBy]) {
|
|
30
|
+
for (const word of (k ?? "").split(" "))
|
|
31
|
+
if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(word))
|
|
32
|
+
out.add(word);
|
|
33
|
+
}
|
|
34
|
+
return out;
|
|
35
|
+
}
|
|
36
|
+
const elements = (key) => (key ? key.replace(/^\( (.*) \)$/, "$1").split(" , ") : []);
|
|
37
|
+
/** An ORDER BY change that only appends columns the same change adds (MODIFY ORDER BY). */
|
|
38
|
+
function appendsAddedColumns(before, after, added) {
|
|
39
|
+
const b = elements(before);
|
|
40
|
+
const a = elements(after);
|
|
41
|
+
if (a.length <= b.length || b.some((e, i) => a[i] !== e))
|
|
42
|
+
return false;
|
|
43
|
+
return a.slice(b.length).every((e) => added.has(e));
|
|
44
|
+
}
|
|
45
|
+
function diffColumns(key, before, after, out, hints) {
|
|
46
|
+
const keys = keyColumns(before);
|
|
47
|
+
const pk = new Set(elements(before.primaryKey ?? before.orderBy).flatMap((e) => e.split(" ")));
|
|
48
|
+
const byName = new Map(before.columns.map((c) => [c.name, c]));
|
|
49
|
+
const afterNames = new Set(after.columns.map((c) => c.name));
|
|
50
|
+
const matched = new Set();
|
|
51
|
+
const added = new Set();
|
|
52
|
+
for (const a of after.columns) {
|
|
53
|
+
let b = byName.get(a.name);
|
|
54
|
+
if (!b && a.previously && byName.has(a.previously) && !afterNames.has(a.previously)) {
|
|
55
|
+
b = byName.get(a.previously);
|
|
56
|
+
out.push(change(key, `columns.${a.name}`, keys.has(b.name) ? "SQLCH213" : "SQLCH212", b.name, a.name));
|
|
57
|
+
}
|
|
58
|
+
if (!b) {
|
|
59
|
+
added.add(a.name);
|
|
60
|
+
out.push(change(key, `columns.${a.name}`, "SQLCH201", undefined, a.text));
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
matched.add(b.name);
|
|
64
|
+
const field = (f) => `columns.${a.name}.${f}`;
|
|
65
|
+
if (a.type !== b.type || (a.nullable ?? false) !== (b.nullable ?? false)) {
|
|
66
|
+
out.push(change(key, field("type"), pk.has(b.name) || keys.has(b.name) ? "SQLCH211" : "SQLCH210", b.type, a.type));
|
|
67
|
+
}
|
|
68
|
+
if (a.defaultKind !== b.defaultKind || a.defaultExpr !== b.defaultExpr) {
|
|
69
|
+
out.push(change(key, field("default"), "SQLCH207", [b.defaultKind, b.defaultExpr].filter(Boolean).join(" "), [a.defaultKind, a.defaultExpr].filter(Boolean).join(" ")));
|
|
70
|
+
}
|
|
71
|
+
if (a.codec !== b.codec)
|
|
72
|
+
out.push(change(key, field("codec"), "SQLCH208", b.codec, a.codec));
|
|
73
|
+
if (a.ttl !== b.ttl)
|
|
74
|
+
out.push(change(key, field("ttl"), "SQLCH205", b.ttl, a.ttl));
|
|
75
|
+
if (a.comment !== b.comment)
|
|
76
|
+
out.push(change(key, field("comment"), "SQLCH203", b.comment, a.comment));
|
|
77
|
+
}
|
|
78
|
+
const dropped = before.columns.filter((c) => !matched.has(c.name));
|
|
79
|
+
for (const b of dropped) {
|
|
80
|
+
out.push(change(key, `columns.${b.name}`, keys.has(b.name) ? "SQLCH213" : "SQLCH202", b.text, undefined, { destructive: true }));
|
|
81
|
+
const twin = after.columns.find((a) => added.has(a.name) && a.type === b.type && a.position === b.position);
|
|
82
|
+
if (twin) {
|
|
83
|
+
hints.push(`${key}: column ${b.name} is dropped and ${twin.name} added with the same type in the same place. ` +
|
|
84
|
+
`If it is a rename, write \`-- previously: ${b.name}\` on ${twin.name}'s line.`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Order, among the columns both sides have (a renamed column by its old name).
|
|
88
|
+
const beforeOrder = before.columns.map((c) => c.name).filter((n) => matched.has(n));
|
|
89
|
+
const afterOrder = after.columns
|
|
90
|
+
.map((c) => (byName.has(c.name) ? c.name : c.previously && matched.has(c.previously) ? c.previously : undefined))
|
|
91
|
+
.filter((n) => n !== undefined && matched.has(n));
|
|
92
|
+
if (beforeOrder.join(",") !== afterOrder.join(","))
|
|
93
|
+
out.push(change(key, "columns.order", "SQLCH209", beforeOrder.join(", "), afterOrder.join(", ")));
|
|
94
|
+
return added;
|
|
95
|
+
}
|
|
96
|
+
function diffMap(key, field, before, after, rule, out) {
|
|
97
|
+
for (const name of changedEntries(before, after))
|
|
98
|
+
out.push(change(key, `${field}.${name}`, rule, before[name], after[name]));
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* The clauses chant does not read (#3872), compared as text. One the
|
|
102
|
+
* declaration has and the server does not, or has differently, is a change
|
|
103
|
+
* to review (SQLCH290). One only the server prints is a hint, not a change: a
|
|
104
|
+
* clause a new server prints with its default cannot be told from one
|
|
105
|
+
* removed from the declaration.
|
|
106
|
+
*/
|
|
107
|
+
function diffUnparsed(key, before, after, out, hints) {
|
|
108
|
+
const b = before.unparsed ?? {};
|
|
109
|
+
const a = after.unparsed ?? {};
|
|
110
|
+
for (const clause of Object.keys(a)) {
|
|
111
|
+
if (b[clause] !== a[clause])
|
|
112
|
+
out.push(change(key, `unparsed.${clause}`, "SQLCH290", b[clause], a[clause], { note: "chant does not read this clause; review the change" }));
|
|
113
|
+
}
|
|
114
|
+
for (const clause of Object.keys(b)) {
|
|
115
|
+
if (!(clause in a))
|
|
116
|
+
hints.push(`${key}: the current definition has ${clause} (${b[clause]}), a clause chant does not read and the declaration does not have; it is not compared`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function diffObject(key, before, after, out, hints) {
|
|
120
|
+
diffUnparsed(key, before, after, out, hints);
|
|
121
|
+
if (before.kind !== after.kind) {
|
|
122
|
+
out.push(change(key, "kind", "SQLCH224", before.kind, after.kind, { destructive: before.kind === "table" }));
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (qualified(before) !== qualified(after)) {
|
|
126
|
+
out.push(change(key, "name", before.kind === "database" ? "SQLCH231" : "SQLCH230", qualified(before), qualified(after)));
|
|
127
|
+
}
|
|
128
|
+
if (before.comment !== after.comment)
|
|
129
|
+
out.push(change(key, "comment", "SQLCH203", before.comment, after.comment));
|
|
130
|
+
if (before.kind === "database") {
|
|
131
|
+
// A declaration with no ENGINE takes the server's default database engine
|
|
132
|
+
// (Atomic; Replicated on Cloud), whatever the server says it is (#3645).
|
|
133
|
+
if (after.engine !== undefined && before.engine !== after.engine)
|
|
134
|
+
out.push(change(key, "engine", "SQLCH232", before.engine, after.engine));
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (before.kind === "user" || before.kind === "role" || before.kind === "rowPolicy") {
|
|
138
|
+
const rule = before.kind === "user" ? "SQLCH271" : before.kind === "role" ? "SQLCH270" : "SQLCH272";
|
|
139
|
+
const b = before.access ?? {};
|
|
140
|
+
const a = after.access ?? {};
|
|
141
|
+
const order = ["identified", "host", "validUntil", "defaultRole", "defaultDatabase", "grantees", "settings", "as", "for", "using", "to"];
|
|
142
|
+
const fields = [...new Set([...Object.keys(b), ...Object.keys(a)])].sort((x, y) => order.indexOf(x) - order.indexOf(y));
|
|
143
|
+
for (const field of fields) {
|
|
144
|
+
// A user's authentication is compared only when the declaration says it.
|
|
145
|
+
if (before.kind === "user" && field === "identified" && a.identified === undefined)
|
|
146
|
+
continue;
|
|
147
|
+
if (b[field] !== a[field])
|
|
148
|
+
out.push(change(key, field, rule, b[field], a[field]));
|
|
149
|
+
}
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (before.kind === "quota" || before.kind === "settingsProfile") {
|
|
153
|
+
const rule = before.kind === "quota" ? "SQLCH275" : "SQLCH276";
|
|
154
|
+
const b = before.access ?? {};
|
|
155
|
+
const a = after.access ?? {};
|
|
156
|
+
// In the order the server prints them: the key, each interval by length, the settings, whom it applies to.
|
|
157
|
+
const rank = (f) => (f === "keyed" ? 0 : f.startsWith("interval.") ? 1 + Number(f.slice("interval.".length)) / 1e9 : f === "settings" ? 2 : 3);
|
|
158
|
+
const fields = [...new Set([...Object.keys(b), ...Object.keys(a)])].sort((x, y) => rank(x) - rank(y));
|
|
159
|
+
for (const field of fields)
|
|
160
|
+
if (b[field] !== a[field])
|
|
161
|
+
out.push(change(key, field, rule, b[field], a[field]));
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
if (before.kind === "grants") {
|
|
165
|
+
const b = before.access ?? {};
|
|
166
|
+
const a = after.access ?? {};
|
|
167
|
+
for (const atom of Object.keys(b))
|
|
168
|
+
if (!(atom in a))
|
|
169
|
+
out.push(change(key, `grants.${atom}`, "SQLCH274", atom, undefined));
|
|
170
|
+
for (const atom of Object.keys(a))
|
|
171
|
+
if (!(atom in b))
|
|
172
|
+
out.push(change(key, `grants.${atom}`, "SQLCH273", undefined, atom));
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
if (before.kind === "function") {
|
|
176
|
+
if (before.lambda !== after.lambda)
|
|
177
|
+
out.push(change(key, "lambda", "SQLCH260", before.lambda, after.lambda));
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
if (before.kind === "dictionary") {
|
|
181
|
+
diffDictionary(key, before, after, out);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
if (before.kind === "view") {
|
|
185
|
+
if (before.select !== after.select)
|
|
186
|
+
out.push(change(key, "select", "SQLCH240", before.select, after.select));
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if (before.kind === "materializedView") {
|
|
190
|
+
if (before.to !== after.to)
|
|
191
|
+
out.push(change(key, "to", "SQLCH242", before.to, after.to));
|
|
192
|
+
if (before.select !== after.select)
|
|
193
|
+
out.push(change(key, "select", "SQLCH241", before.select, after.select, { note: "rows already in the target are not recomputed" }));
|
|
194
|
+
if (before.refresh !== after.refresh)
|
|
195
|
+
out.push(change(key, "refresh", "SQLCH244", before.refresh, after.refresh));
|
|
196
|
+
for (const f of ["engine", "orderBy", "primaryKey", "partitionBy", "sampleBy", "ttl"]) {
|
|
197
|
+
if (before[f] !== after[f])
|
|
198
|
+
out.push(change(key, f, "SQLCH243", before[f], after[f]));
|
|
199
|
+
}
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (before.asFunction !== undefined || after.asFunction !== undefined) {
|
|
203
|
+
diffTableFunction(key, before, after, out);
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
// A table.
|
|
207
|
+
if (before.engine !== after.engine)
|
|
208
|
+
out.push(change(key, "engine", "SQLCH223", before.engine, after.engine));
|
|
209
|
+
const added = diffColumns(key, before, after, out, hints);
|
|
210
|
+
const beforePk = before.primaryKey ?? before.orderBy;
|
|
211
|
+
const afterPk = after.primaryKey ?? after.orderBy;
|
|
212
|
+
if (before.orderBy !== after.orderBy) {
|
|
213
|
+
const append = appendsAddedColumns(before.orderBy, after.orderBy, added) && beforePk === afterPk;
|
|
214
|
+
out.push(change(key, "orderBy", append ? "SQLCH216" : "SQLCH220", before.orderBy, after.orderBy, {
|
|
215
|
+
...(appendsAddedColumns(before.orderBy, after.orderBy, added) && beforePk !== afterPk
|
|
216
|
+
? { note: `appending new columns keeps the primary key; declare PRIMARY KEY ${beforePk} to make this an ALTER` }
|
|
217
|
+
: {}),
|
|
218
|
+
}));
|
|
219
|
+
}
|
|
220
|
+
if (beforePk !== afterPk && !(before.orderBy !== after.orderBy && before.primaryKey === undefined && after.primaryKey === undefined)) {
|
|
221
|
+
out.push(change(key, "primaryKey", "SQLCH221", beforePk, afterPk));
|
|
222
|
+
}
|
|
223
|
+
if (before.partitionBy !== after.partitionBy)
|
|
224
|
+
out.push(change(key, "partitionBy", "SQLCH222", before.partitionBy, after.partitionBy));
|
|
225
|
+
if (before.sampleBy !== after.sampleBy)
|
|
226
|
+
out.push(change(key, "sampleBy", "SQLCH217", before.sampleBy, after.sampleBy));
|
|
227
|
+
if (before.ttl !== after.ttl)
|
|
228
|
+
out.push(change(key, "ttl", "SQLCH205", before.ttl, after.ttl));
|
|
229
|
+
for (const name of changedEntries(before.settings, after.settings)) {
|
|
230
|
+
const spec = MERGE_TREE_SETTINGS[name];
|
|
231
|
+
out.push(change(key, `settings.${name}`, spec?.readonly ? "SQLCH218" : "SQLCH206", before.settings[name], after.settings[name]));
|
|
232
|
+
}
|
|
233
|
+
diffMap(key, "indexes", before.indexes, after.indexes, "SQLCH204", out);
|
|
234
|
+
diffMap(key, "projections", before.projections, after.projections, "SQLCH214", out);
|
|
235
|
+
diffMap(key, "constraints", before.constraints, after.constraints, "SQLCH215", out);
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* A table over a table function (#3911), on either side. It stores nothing,
|
|
239
|
+
* so every change but its comment replaces it (SQLCH219); a table with data
|
|
240
|
+
* of its own becoming one would lose that data, which is a rebuild
|
|
241
|
+
* (SQLCH223). A declaration with no column list takes the columns the server
|
|
242
|
+
* infers, so they are compared only when it lists them.
|
|
243
|
+
*/
|
|
244
|
+
function diffTableFunction(key, before, after, out) {
|
|
245
|
+
if (before.asFunction === undefined) {
|
|
246
|
+
out.push(change(key, "asFunction", "SQLCH223", before.engine, `AS ${after.asFunction}`));
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
if (before.asFunction !== after.asFunction)
|
|
250
|
+
out.push(change(key, "asFunction", "SQLCH219", before.asFunction, after.asFunction));
|
|
251
|
+
if (after.asFunction !== undefined && after.columns.length === 0)
|
|
252
|
+
return;
|
|
253
|
+
const column = (c) => [c.type, c.nullable ? "NULL" : "", c.defaultKind ?? "", c.defaultExpr ?? "", c.codec ? `CODEC(${c.codec})` : "", c.comment ? `COMMENT ${c.comment}` : ""].filter(Boolean).join(" ");
|
|
254
|
+
const b = new Map(before.columns.map((c) => [c.name, column(c)]));
|
|
255
|
+
const a = new Map(after.columns.map((c) => [c.name, column(c)]));
|
|
256
|
+
for (const name of new Set([...b.keys(), ...a.keys()])) {
|
|
257
|
+
if (b.get(name) !== a.get(name))
|
|
258
|
+
out.push(change(key, `columns.${name}`, "SQLCH219", b.get(name), a.get(name)));
|
|
259
|
+
}
|
|
260
|
+
const order = (o) => o.columns.map((c) => c.name).join(", ");
|
|
261
|
+
if (order(before) !== order(after) && a.size === b.size && [...a.keys()].every((n) => b.has(n)))
|
|
262
|
+
out.push(change(key, "columns.order", "SQLCH219", order(before), order(after)));
|
|
263
|
+
}
|
|
264
|
+
/** A dictionary: every change but its comment replaces it (SQLCH245). */
|
|
265
|
+
function diffDictionary(key, before, after, out) {
|
|
266
|
+
const attribute = (c) => [c.type, c.defaultExpr !== undefined ? `DEFAULT ${c.defaultExpr}` : "", c.expression !== undefined ? `EXPRESSION ${c.expression}` : "", c.flags ?? ""].filter(Boolean).join(" ");
|
|
267
|
+
const b = new Map(before.columns.map((c) => [c.name, attribute(c)]));
|
|
268
|
+
const a = new Map(after.columns.map((c) => [c.name, attribute(c)]));
|
|
269
|
+
for (const name of new Set([...b.keys(), ...a.keys()])) {
|
|
270
|
+
if (b.get(name) !== a.get(name))
|
|
271
|
+
out.push(change(key, `columns.${name}`, "SQLCH245", b.get(name), a.get(name)));
|
|
272
|
+
}
|
|
273
|
+
const order = (o) => o.columns.map((c) => c.name).join(", ");
|
|
274
|
+
if (order(before) !== order(after) && [...a.keys()].every((n) => b.has(n)) && a.size === b.size)
|
|
275
|
+
out.push(change(key, "columns.order", "SQLCH245", order(before), order(after)));
|
|
276
|
+
for (const f of ["primaryKey", "dataSource", "layout", "lifetime", "range"]) {
|
|
277
|
+
if (before[f] !== after[f])
|
|
278
|
+
out.push(change(key, f, "SQLCH245", before[f], after[f]));
|
|
279
|
+
}
|
|
280
|
+
for (const name of changedEntries(before.settings, after.settings))
|
|
281
|
+
out.push(change(key, `settings.${name}`, "SQLCH245", before.settings[name], after.settings[name]));
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Diff two schemas. `before` and `after` are matched by key; an `after`
|
|
285
|
+
* object whose key is new but whose `previously` names a `before` object
|
|
286
|
+
* with no `after` counterpart is that object, renamed.
|
|
287
|
+
*/
|
|
288
|
+
export function diffSchemas(before, after) {
|
|
289
|
+
const changes = [];
|
|
290
|
+
const hints = [];
|
|
291
|
+
const matches = matchByIdentity(before, after, {
|
|
292
|
+
qualified,
|
|
293
|
+
// A function, a user, a role, a row policy and a grantee's grants are not renamed: one under a new name is a create.
|
|
294
|
+
previously: (o) => (o.kind === "function" || o.kind === "user" || o.kind === "role" || o.kind === "rowPolicy" || o.kind === "quota" || o.kind === "settingsProfile" || o.kind === "grants" ? undefined : o.previously),
|
|
295
|
+
previousNames: (o, prev) => [prev, `${o.database}.${prev}`],
|
|
296
|
+
});
|
|
297
|
+
for (const m of matches) {
|
|
298
|
+
if (m.kind === "created")
|
|
299
|
+
changes.push(change(m.after.key, "object", "SQLCH200", undefined, qualified(m.after.canonical)));
|
|
300
|
+
else if (m.kind === "matched")
|
|
301
|
+
diffObject(m.after.key, m.before.canonical, m.after.canonical, changes, hints);
|
|
302
|
+
else if (m.before.canonical.foreign) {
|
|
303
|
+
// A migration runner's history table (#3676): not chant's to drop.
|
|
304
|
+
hints.push(`${qualified(m.before.canonical)} is kept by ${m.before.canonical.foreign}; it is not chant's to drop and is left alone`);
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
const b = m.before;
|
|
308
|
+
changes.push(change(b.key, "object", "SQLCH250", qualified(b.canonical), undefined, { destructive: b.canonical.kind === "table" || b.canonical.kind === "materializedView" }));
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return { changes, rebuilds: changes.filter((c) => c.class === "rebuild"), hints };
|
|
312
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `classifyDisruption()`: what applying a pending update costs, from the
|
|
3
|
+
* paths `chant lifecycle plan` reports changed, mapped onto the classifier's
|
|
4
|
+
* rules (`./rules.ts`). A metadata-only change is `in-place`, a background
|
|
5
|
+
* rewrite `rolling`, a rebuild `replace`.
|
|
6
|
+
*
|
|
7
|
+
* A path alone cannot always say which rule applies: a column type change is a
|
|
8
|
+
* rewrite, or a rebuild when the column is in a key. Those answer `unknown`,
|
|
9
|
+
* naming both rules; `chant sql plan` resolves them with the whole definition
|
|
10
|
+
* in hand.
|
|
11
|
+
*/
|
|
12
|
+
import { MERGE_TREE_SETTINGS } from "../../generated/clickhouse.js";
|
|
13
|
+
import { classifyDisruptionWith } from "../../core/classifier.js";
|
|
14
|
+
import { CHANGE_CLASSES, CLASSIFIER_RULES } from "./rules.js";
|
|
15
|
+
function ruleFor(path) {
|
|
16
|
+
const root = path.split(/[.[]/)[0];
|
|
17
|
+
if (root === "name" || root === "database")
|
|
18
|
+
return "SQLCH230";
|
|
19
|
+
if (root === "comment")
|
|
20
|
+
return "SQLCH203";
|
|
21
|
+
if (root === "engine")
|
|
22
|
+
return "SQLCH223";
|
|
23
|
+
if (root === "orderBy")
|
|
24
|
+
return "ambiguous";
|
|
25
|
+
if (root === "primaryKey")
|
|
26
|
+
return "SQLCH221";
|
|
27
|
+
if (root === "partitionBy")
|
|
28
|
+
return "SQLCH222";
|
|
29
|
+
if (root === "sampleBy")
|
|
30
|
+
return "SQLCH217";
|
|
31
|
+
if (root === "ttl")
|
|
32
|
+
return "SQLCH205";
|
|
33
|
+
if (root === "indexes")
|
|
34
|
+
return "SQLCH204";
|
|
35
|
+
if (root === "projections")
|
|
36
|
+
return "SQLCH214";
|
|
37
|
+
if (root === "constraints")
|
|
38
|
+
return "SQLCH215";
|
|
39
|
+
if (root === "select")
|
|
40
|
+
return "SQLCH240";
|
|
41
|
+
if (root === "refresh")
|
|
42
|
+
return "SQLCH244";
|
|
43
|
+
if (root === "settings") {
|
|
44
|
+
const key = path.split(".")[1];
|
|
45
|
+
const spec = key ? MERGE_TREE_SETTINGS[key] : undefined;
|
|
46
|
+
return spec?.readonly ? "SQLCH218" : "SQLCH206";
|
|
47
|
+
}
|
|
48
|
+
if (root === "columns") {
|
|
49
|
+
const field = /^columns\[\d+\]\.(\w+)/.exec(path)?.[1];
|
|
50
|
+
if (field === undefined)
|
|
51
|
+
return "ambiguous"; // a column added, dropped or moved
|
|
52
|
+
if (field === "type" || field === "nullable" || field === "name")
|
|
53
|
+
return "ambiguous";
|
|
54
|
+
if (field === "default")
|
|
55
|
+
return "SQLCH207";
|
|
56
|
+
if (field === "codec")
|
|
57
|
+
return "SQLCH208";
|
|
58
|
+
if (field === "ttl")
|
|
59
|
+
return "SQLCH205";
|
|
60
|
+
if (field === "comment")
|
|
61
|
+
return "SQLCH203";
|
|
62
|
+
}
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
export function classifyDisruption(options) {
|
|
66
|
+
return classifyDisruptionWith({
|
|
67
|
+
typePrefix: "ClickHouse::",
|
|
68
|
+
rules: CLASSIFIER_RULES,
|
|
69
|
+
classes: CHANGE_CLASSES,
|
|
70
|
+
ruleFor,
|
|
71
|
+
ambiguousDetail: "a sorting-key or column change is metadata, a rewrite or a rebuild depending on the whole definition; `chant sql plan` classifies it",
|
|
72
|
+
}, options);
|
|
73
|
+
}
|