@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,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ClickHouse applier (#3208): make a server hold what a build declares,
|
|
3
|
+
* for every change the classifier (`../plan/rules.ts`) puts in the metadata
|
|
4
|
+
* or background-rewrite class, and refuse the rest.
|
|
5
|
+
*
|
|
6
|
+
* Per declared object, exactly one verdict (lexicon-authoring/applying.mdx):
|
|
7
|
+
*
|
|
8
|
+
* - APPLIED `created`: absent on the server; created from its declared
|
|
9
|
+
* `CREATE`, with chant's ownership marker in its comment.
|
|
10
|
+
* - APPLIED `updated`: present, and the `ALTER`s for its changes ran. A
|
|
11
|
+
* background rewrite (a column type change, a TTL change) is waited on in
|
|
12
|
+
* `system.mutations` before the object counts as applied.
|
|
13
|
+
* - APPLIED `unchanged`: present, nothing to change, the marker in place.
|
|
14
|
+
* - NOT-ATTEMPTED `unsupported-kind`: a change ClickHouse cannot make with
|
|
15
|
+
* `ALTER` (a sorting key, a partition key, an engine, a key column's type).
|
|
16
|
+
* Nothing is sent for the object; the detail names each rule and the
|
|
17
|
+
* restriction behind it, and names the rebuild migration Op to run instead
|
|
18
|
+
* (`ClickHouseRebuildOp`, #3198).
|
|
19
|
+
* - NOT-ATTEMPTED `filtered`: the change drops a column, which destroys its
|
|
20
|
+
* data, and the apply was not allowed to delete.
|
|
21
|
+
* - NOT-ATTEMPTED `dependency-failed`: an object it references is not on the
|
|
22
|
+
* server, because creating it failed or was not attempted.
|
|
23
|
+
* - NOT-ATTEMPTED with the binding's reason: no server to apply to.
|
|
24
|
+
*
|
|
25
|
+
* A statement the server refuses is neither: the object was attempted and did
|
|
26
|
+
* not converge. The rest of the apply goes on, and the applier then throws
|
|
27
|
+
* {@link ClickHouseApplyError}, which carries the outcome so far.
|
|
28
|
+
*
|
|
29
|
+
* Prune, when asked for, drops the objects in the declared databases that the
|
|
30
|
+
* build no longer declares and whose comment carries this project's marker
|
|
31
|
+
* (stack and env). Nothing else is ever dropped: an object without the marker
|
|
32
|
+
* is not chant's, and one with another stack's marker is that stack's.
|
|
33
|
+
*/
|
|
34
|
+
import { SqlApplyError, dependencyFailedDetail, missingDependencies, readBuildObjects, } from "../../core/apply.js";
|
|
35
|
+
import { clickhouseQuery } from "../http.js";
|
|
36
|
+
import { ACCESS_KINDS, ACCESS_UNMANAGED_DETAIL, declaredAccess, readLiveAccess, readLiveSchema } from "../live/catalog.js";
|
|
37
|
+
import { canonicalObject, grantsObject, liveCanonical, objectKey, scopeOf } from "../plan/normalize.js";
|
|
38
|
+
import { grantsByGrantee } from "../access.js";
|
|
39
|
+
import { diffSchemas } from "../plan/diff.js";
|
|
40
|
+
import { dropFormattingOnly } from "../plan/server-format.js";
|
|
41
|
+
import { CLICKHOUSE_ENTITY_TYPES } from "../entities.js";
|
|
42
|
+
import { carriesMarker, isChantManaged } from "../ownership.js";
|
|
43
|
+
import { planStatements, sqlString } from "./statements.js";
|
|
44
|
+
import { waitForMutations } from "./mutations.js";
|
|
45
|
+
import { renderFor } from "../topology.js";
|
|
46
|
+
/**
|
|
47
|
+
* The objects a `chant build` output declares, in creation order. Takes the
|
|
48
|
+
* sql lexicon's primary output, or a multi-lexicon output holding it under
|
|
49
|
+
* `sql` (the shared core's reader).
|
|
50
|
+
*
|
|
51
|
+
* With a `topology`, each declaration is rendered for it first
|
|
52
|
+
* (`../topology.ts`): the engine and `ON CLUSTER` it runs with there, which
|
|
53
|
+
* is what the changes compare and every `CREATE` sends.
|
|
54
|
+
*/
|
|
55
|
+
export function declaredObjects(json, defaultDatabase = "default", topology) {
|
|
56
|
+
const objects = readBuildObjects(json, "clickhouse", (o) => o);
|
|
57
|
+
const declared = objects
|
|
58
|
+
.filter((o) => o.type !== CLICKHOUSE_ENTITY_TYPES.grant)
|
|
59
|
+
.map((o) => {
|
|
60
|
+
const ddl = renderFor(o.ddl, topology);
|
|
61
|
+
const canonical = canonicalObject(ddl, defaultDatabase);
|
|
62
|
+
return { exportName: o.export, type: o.type, key: objectKey(canonical), ddl, canonical, dependsOn: o.dependsOn };
|
|
63
|
+
});
|
|
64
|
+
// Grants are compared per grantee (`../access.ts`), once every other object exists, and wait for the
|
|
65
|
+
// grantee when the build declares it: a user the environment has still to create gets none.
|
|
66
|
+
const principals = new Map(declared.filter((o) => o.canonical.kind === "user" || o.canonical.kind === "role").map((o) => [o.canonical.name, o.exportName]));
|
|
67
|
+
for (const g of grantsByGrantee(objects)) {
|
|
68
|
+
const canonical = grantsObject(g.ddl, g.grantee, defaultDatabase);
|
|
69
|
+
const key = objectKey(canonical);
|
|
70
|
+
const grantee = principals.get(g.grantee);
|
|
71
|
+
const dependsOn = grantee !== undefined && !g.dependsOn.includes(grantee) ? [...g.dependsOn, grantee] : g.dependsOn;
|
|
72
|
+
declared.push({ exportName: key, type: CLICKHOUSE_ENTITY_TYPES.grant, key, ddl: g.ddl, canonical, dependsOn });
|
|
73
|
+
}
|
|
74
|
+
return declared;
|
|
75
|
+
}
|
|
76
|
+
/** The plan axis: one ref per declared object, by its kind and its name on the server. */
|
|
77
|
+
export const planRefs = (declared) => declared.map((o) => ({ kind: o.type, name: o.key }));
|
|
78
|
+
/** An apply in which the server refused a statement. The outcome says what did and did not happen. */
|
|
79
|
+
export class ClickHouseApplyError extends SqlApplyError {
|
|
80
|
+
constructor(outcome) {
|
|
81
|
+
super("ClickHouse", outcome);
|
|
82
|
+
this.name = "ClickHouseApplyError";
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const liveKey = (o) => objectKey(o);
|
|
86
|
+
/**
|
|
87
|
+
* The server's objects in the databases the declarations use, as the diff
|
|
88
|
+
* compares them. The `default` database itself is never part of a schema.
|
|
89
|
+
*/
|
|
90
|
+
async function liveSchema(target, declared) {
|
|
91
|
+
const scope = new Set(declared.map((o) => scopeOf(o.canonical)));
|
|
92
|
+
const objects = [...(await readLiveSchema(target)), ...(await readLiveAccess(target, declaredAccess(declared.map((o) => o.canonical))))]
|
|
93
|
+
.filter((o) => !(o.type === CLICKHOUSE_ENTITY_TYPES.database && o.name === "default"))
|
|
94
|
+
.filter((o) => scope.has(scopeOf(o)));
|
|
95
|
+
return { objects, canonical: new Map(objects.map((o) => [liveKey(o), liveCanonical(o, target.defaultDatabase)])) };
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* The classified changes from the server to the declarations, keyed by the
|
|
99
|
+
* declared object (`database.name`), with formatting-only differences dropped
|
|
100
|
+
* by asking the server, as `chant sql plan` does.
|
|
101
|
+
*/
|
|
102
|
+
export async function plannedChanges(target, declared, live) {
|
|
103
|
+
const diff = diffSchemas([...live].map(([key, canonical]) => ({ key, canonical })), declared.map((o) => ({ key: o.key, canonical: o.canonical })));
|
|
104
|
+
return dropFormattingOnly(target.endpoint, diff.changes);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Apply the declarations to the server. Returns every declared object in
|
|
108
|
+
* exactly one of applied or not attempted, and every pruned orphan; throws
|
|
109
|
+
* {@link ClickHouseApplyError} when the server refused a statement.
|
|
110
|
+
*/
|
|
111
|
+
export async function applyClickHouse(target, all, opts = {}) {
|
|
112
|
+
const log = opts.log ?? (() => undefined);
|
|
113
|
+
// Access declarations a profile does not manage are not applied, and the server's access is not read (#3716).
|
|
114
|
+
const managed = target.access === true;
|
|
115
|
+
const declared = managed ? all : all.filter((o) => !ACCESS_KINDS.has(o.canonical.kind));
|
|
116
|
+
const { objects: liveObjects, canonical: live } = await liveSchema(target, declared);
|
|
117
|
+
const liveByKey = new Map(liveObjects.map((o) => [liveKey(o), o]));
|
|
118
|
+
const changes = await plannedChanges(target, declared, live);
|
|
119
|
+
const outcome = { target: target.endpoint.url, source: target.source, applied: [], pruned: [], notAttempted: [], failed: [] };
|
|
120
|
+
if (!managed) {
|
|
121
|
+
for (const o of all)
|
|
122
|
+
if (ACCESS_KINDS.has(o.canonical.kind))
|
|
123
|
+
outcome.notAttempted.push({ kind: o.type, name: o.key, reason: "filtered", detail: ACCESS_UNMANAGED_DETAIL });
|
|
124
|
+
}
|
|
125
|
+
/** Export names whose object is on the server. */
|
|
126
|
+
const onServer = new Set();
|
|
127
|
+
const exportNames = new Set(declared.map((o) => o.exportName));
|
|
128
|
+
const run = async (step, obj, ran) => {
|
|
129
|
+
opts.signal?.throwIfAborted();
|
|
130
|
+
log(step.sql);
|
|
131
|
+
await clickhouseQuery(target.endpoint, step.sql);
|
|
132
|
+
ran.push(step.sql);
|
|
133
|
+
if (step.rewrite && obj.database !== undefined) {
|
|
134
|
+
const ids = await waitForMutations(target.endpoint, obj.database, obj.name, {
|
|
135
|
+
...(opts.mutationTimeoutMs !== undefined ? { timeoutMs: opts.mutationTimeoutMs } : {}),
|
|
136
|
+
...(opts.signal ? { signal: opts.signal } : {}),
|
|
137
|
+
});
|
|
138
|
+
if (ids.length > 0)
|
|
139
|
+
log(`-- waited for ${ids.join(", ")}`);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
const plan = planStatements({
|
|
143
|
+
declared,
|
|
144
|
+
changes,
|
|
145
|
+
current: live,
|
|
146
|
+
allowDestructive: opts.prune === true,
|
|
147
|
+
carriesMarker: (key) => carriesMarker(liveByKey.get(key)?.comment, opts.marker),
|
|
148
|
+
...(target.topology ? { topology: target.topology } : {}),
|
|
149
|
+
...(opts.marker ? { marker: opts.marker } : {}),
|
|
150
|
+
});
|
|
151
|
+
for (const entry of plan.objects) {
|
|
152
|
+
const obj = entry.obj;
|
|
153
|
+
const ref = { kind: obj.type, name: obj.key };
|
|
154
|
+
const renamedFrom = entry.changes.find((c) => c.rule === "SQLCH230" || c.rule === "SQLCH231")?.before;
|
|
155
|
+
const liveObject = liveByKey.get(renamedFrom ?? obj.key);
|
|
156
|
+
if (entry.verdict === "rebuild") {
|
|
157
|
+
if (liveObject)
|
|
158
|
+
onServer.add(obj.exportName);
|
|
159
|
+
outcome.notAttempted.push({ ...ref, reason: "unsupported-kind", detail: entry.detail });
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
if (entry.verdict === "withheld") {
|
|
163
|
+
// A user the environment has still to create is not on the server, so nothing that needs it is attempted.
|
|
164
|
+
if (liveObject)
|
|
165
|
+
onServer.add(obj.exportName);
|
|
166
|
+
outcome.notAttempted.push({ ...ref, reason: "filtered", detail: entry.detail });
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
const created = entry.verdict === "create";
|
|
170
|
+
const steps = entry.steps;
|
|
171
|
+
if (steps.length === 0) {
|
|
172
|
+
onServer.add(obj.exportName);
|
|
173
|
+
outcome.applied.push({ ...ref, action: "unchanged", ...(liveObject?.uuid ? { physicalId: liveObject.uuid } : {}), statements: [] });
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
const missing = missingDependencies(obj.exportName, obj.dependsOn, exportNames, onServer);
|
|
177
|
+
if (missing.length > 0) {
|
|
178
|
+
if (!created)
|
|
179
|
+
onServer.add(obj.exportName);
|
|
180
|
+
outcome.notAttempted.push({ ...ref, reason: "dependency-failed", detail: dependencyFailedDetail(missing) });
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
const ran = [];
|
|
184
|
+
try {
|
|
185
|
+
for (const step of steps)
|
|
186
|
+
await run(step, { ...(obj.canonical.database !== undefined ? { database: obj.canonical.database } : {}), name: obj.canonical.name }, ran);
|
|
187
|
+
onServer.add(obj.exportName);
|
|
188
|
+
outcome.applied.push({ ...ref, action: created ? "created" : "updated", ...(liveObject?.uuid ? { physicalId: liveObject.uuid } : {}), statements: ran });
|
|
189
|
+
}
|
|
190
|
+
catch (err) {
|
|
191
|
+
if (opts.signal?.aborted)
|
|
192
|
+
throw err;
|
|
193
|
+
if (!created)
|
|
194
|
+
onServer.add(obj.exportName);
|
|
195
|
+
outcome.failed.push({ ...ref, error: err instanceof Error ? err.message : String(err), statements: ran });
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (opts.prune)
|
|
199
|
+
await prune(target, plan.drops, liveByKey, opts, outcome, log);
|
|
200
|
+
if (outcome.failed.length > 0)
|
|
201
|
+
throw new ClickHouseApplyError(outcome);
|
|
202
|
+
return outcome;
|
|
203
|
+
}
|
|
204
|
+
async function prune(target, drops, liveByKey, opts, outcome, log) {
|
|
205
|
+
for (const drop of drops) {
|
|
206
|
+
const o = liveByKey.get(drop.key);
|
|
207
|
+
if (!o)
|
|
208
|
+
continue;
|
|
209
|
+
const ref = { kind: o.type, name: liveKey(o) };
|
|
210
|
+
if (!isChantManaged(o.comment))
|
|
211
|
+
continue; // not chant's: never touched, never reported
|
|
212
|
+
if (!opts.marker?.stack) {
|
|
213
|
+
outcome.notAttempted.push({
|
|
214
|
+
...ref,
|
|
215
|
+
reason: "not-prunable",
|
|
216
|
+
detail: "the project declares no ownership.stack, so this project's objects cannot be told from another chant project's",
|
|
217
|
+
});
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
if (!carriesMarker(o.comment, opts.marker))
|
|
221
|
+
continue; // another stack's or env's
|
|
222
|
+
try {
|
|
223
|
+
if (o.type === CLICKHOUSE_ENTITY_TYPES.database) {
|
|
224
|
+
const [row] = await clickhouseQuery(target.endpoint, `SELECT count() AS n FROM system.tables WHERE database = ${sqlString(o.name)}`);
|
|
225
|
+
const n = Number(row?.n ?? 0);
|
|
226
|
+
if (n > 0) {
|
|
227
|
+
outcome.notAttempted.push({ ...ref, reason: "not-prunable", detail: `still holds ${n} object(s) this apply does not drop; dropping the database would drop them` });
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
const sql = drop.step.sql;
|
|
232
|
+
opts.signal?.throwIfAborted();
|
|
233
|
+
log(sql);
|
|
234
|
+
await clickhouseQuery(target.endpoint, sql);
|
|
235
|
+
outcome.pruned.push({ ...ref, deleted: true, statement: sql });
|
|
236
|
+
}
|
|
237
|
+
catch (err) {
|
|
238
|
+
if (opts.signal?.aborted)
|
|
239
|
+
throw err;
|
|
240
|
+
outcome.failed.push({ ...ref, error: err instanceof Error ? err.message : String(err), statements: [] });
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Waiting on a background rewrite (#3208).
|
|
3
|
+
*
|
|
4
|
+
* A column type change or a TTL change returns as soon as the server has
|
|
5
|
+
* recorded it; the rewrite of existing parts runs afterwards as a mutation,
|
|
6
|
+
* listed in `system.mutations` until it is done. An apply that reported
|
|
7
|
+
* APPLIED at that point would claim a table had converged while its parts
|
|
8
|
+
* were still being rewritten, so the applier waits for every unfinished
|
|
9
|
+
* mutation on the table, and reports the ids when it stops waiting.
|
|
10
|
+
*/
|
|
11
|
+
import { clickhouseQuery } from "../http.js";
|
|
12
|
+
import { sqlString } from "./statements.js";
|
|
13
|
+
/** A mutation that did not finish in time. The ids are what `system.mutations` and `KILL MUTATION` name. */
|
|
14
|
+
export class MutationTimeoutError extends Error {
|
|
15
|
+
constructor(table, mutationIds, timeoutMs) {
|
|
16
|
+
super(`${table}: mutation ${mutationIds.join(", ")} was still running after ${Math.round(timeoutMs / 1000)}s. ` +
|
|
17
|
+
`It goes on in the background; follow it in system.mutations, or stop it with KILL MUTATION WHERE mutation_id = '${mutationIds[0]}'.`);
|
|
18
|
+
this.table = table;
|
|
19
|
+
this.mutationIds = mutationIds;
|
|
20
|
+
this.timeoutMs = timeoutMs;
|
|
21
|
+
this.name = "MutationTimeoutError";
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/** A mutation the server keeps failing. It is retried forever, so waiting longer does not help. */
|
|
25
|
+
export class MutationFailedError extends Error {
|
|
26
|
+
constructor(table, mutationId, reason) {
|
|
27
|
+
super(`${table}: mutation ${mutationId} is failing: ${reason.split("\n")[0]}. ` +
|
|
28
|
+
`The server retries it until it is killed: KILL MUTATION WHERE mutation_id = '${mutationId}'.`);
|
|
29
|
+
this.table = table;
|
|
30
|
+
this.mutationId = mutationId;
|
|
31
|
+
this.reason = reason;
|
|
32
|
+
this.name = "MutationFailedError";
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/** The table's mutations that are not done. */
|
|
36
|
+
export async function pendingMutations(endpoint, database, table, source = "system.mutations") {
|
|
37
|
+
return clickhouseQuery(endpoint, `SELECT mutation_id, command, latest_fail_reason FROM ${source} ` +
|
|
38
|
+
`WHERE database = ${sqlString(database)} AND table = ${sqlString(table)} AND NOT is_done ORDER BY create_time`);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Wait until the table has no unfinished mutation. Returns the ids waited on.
|
|
42
|
+
* Throws {@link MutationFailedError} as soon as one reports a failure, and
|
|
43
|
+
* {@link MutationTimeoutError} naming the ids still running at the deadline.
|
|
44
|
+
*/
|
|
45
|
+
export async function waitForMutations(endpoint, database, table, opts = {}) {
|
|
46
|
+
const timeoutMs = opts.timeoutMs ?? 600000;
|
|
47
|
+
const deadline = Date.now() + timeoutMs;
|
|
48
|
+
let interval = opts.intervalMs ?? 100;
|
|
49
|
+
const seen = new Set();
|
|
50
|
+
const name = `${database}.${table}`;
|
|
51
|
+
for (;;) {
|
|
52
|
+
opts.signal?.throwIfAborted();
|
|
53
|
+
const pending = await pendingMutations(endpoint, database, table, opts.source);
|
|
54
|
+
for (const m of pending)
|
|
55
|
+
seen.add(m.mutation_id);
|
|
56
|
+
if (pending.length === 0)
|
|
57
|
+
return [...seen];
|
|
58
|
+
const failing = pending.find((m) => m.latest_fail_reason);
|
|
59
|
+
if (failing)
|
|
60
|
+
throw new MutationFailedError(name, failing.mutation_id, failing.latest_fail_reason);
|
|
61
|
+
if (Date.now() >= deadline)
|
|
62
|
+
throw new MutationTimeoutError(name, pending.map((m) => m.mutation_id), timeoutMs);
|
|
63
|
+
await new Promise((r) => setTimeout(r, Math.min(interval, Math.max(0, deadline - Date.now()))));
|
|
64
|
+
interval = Math.min(interval * 2, 2000);
|
|
65
|
+
}
|
|
66
|
+
}
|