@intentius/chant-lexicon-sql 0.124.30 → 0.124.32
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,593 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ClickHouse entities: `database`, `table`, `view`, `dictionary` and
|
|
3
|
+
* `func` (#3682) tagged templates that parse their DDL at fold time into one
|
|
4
|
+
* Declarable each (chant #3196, #3197).
|
|
5
|
+
*
|
|
6
|
+
* ```ts
|
|
7
|
+
* import { table, view } from "@intentius/chant-lexicon-sql/clickhouse";
|
|
8
|
+
*
|
|
9
|
+
* export const events = table`
|
|
10
|
+
* CREATE TABLE events (user_id UUID, kind LowCardinality(String), ts DateTime)
|
|
11
|
+
* ENGINE = MergeTree ORDER BY (user_id, ts)`;
|
|
12
|
+
*
|
|
13
|
+
* export const activeUsers = view`
|
|
14
|
+
* CREATE MATERIALIZED VIEW active_users
|
|
15
|
+
* ENGINE = AggregatingMergeTree ORDER BY ${events.columns.user_id} AS
|
|
16
|
+
* SELECT ${events.columns.user_id}, count() AS n FROM ${events} GROUP BY ${events.columns.user_id}`;
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* The export name is the object's identity in chant; the name in the SQL is the
|
|
20
|
+
* object's name in the database.
|
|
21
|
+
*
|
|
22
|
+
* What an interpolation means is decided by its value:
|
|
23
|
+
*
|
|
24
|
+
* - a `table`, `view` or `database` entity is a reference to that object, and
|
|
25
|
+
* renders as its (database-qualified) name;
|
|
26
|
+
* - `entity.columns.<name>` is a reference to one column, and renders as the
|
|
27
|
+
* column's name;
|
|
28
|
+
* - a string is SQL text, spliced into the statement before it is parsed, so a
|
|
29
|
+
* composite can supply a name, a type, an engine or an expression;
|
|
30
|
+
* - a number or bigint is a numeric literal, a boolean `true`/`false`, `null`
|
|
31
|
+
* is `NULL`;
|
|
32
|
+
* - `literal(value)` is a quoted, escaped string literal, for the case where a
|
|
33
|
+
* string is meant as a value (`DEFAULT ${literal(plan)}`).
|
|
34
|
+
*
|
|
35
|
+
* Anything else, `undefined` included, is refused with the position it was
|
|
36
|
+
* interpolated at. `${events.user_id}` is `undefined` (columns are reached
|
|
37
|
+
* through `.columns`), and the refusal says so.
|
|
38
|
+
*/
|
|
39
|
+
import { setInterpolationFields } from "@intentius/chant/provenance";
|
|
40
|
+
import { CLICKHOUSE_LEXICAL, isTrivia, SqlSyntaxError, untokenize } from "./tokens.js";
|
|
41
|
+
import { parseCreate, unquote } from "./parser.js";
|
|
42
|
+
import { parseAccess } from "./access.js";
|
|
43
|
+
import { feed, lineage, spanText as text, splice as spliceWith, templateSyntaxError, unparsedClauses } from "../core/interpolation.js";
|
|
44
|
+
import { SqlObject, isColumnRefOf, isSqlObjectOf, makeSqlEntity } from "../core/entity.js";
|
|
45
|
+
import { SqlLiteral, SqlTemplateError, describeValue as describe, stripUnset as strip, templateParts } from "../core/template.js";
|
|
46
|
+
import { setTypedSlots } from "../core/typed.js";
|
|
47
|
+
export { SQL_LEXICON } from "../core/entity.js";
|
|
48
|
+
export { SqlLiteral, SqlTemplateError, templateParts, unescapeTemplateDelimiters } from "../core/template.js";
|
|
49
|
+
export const CLICKHOUSE_ENTITY_TYPES = {
|
|
50
|
+
database: "ClickHouse::Database",
|
|
51
|
+
table: "ClickHouse::Table",
|
|
52
|
+
view: "ClickHouse::View",
|
|
53
|
+
materializedView: "ClickHouse::MaterializedView",
|
|
54
|
+
dictionary: "ClickHouse::Dictionary",
|
|
55
|
+
function: "ClickHouse::Function",
|
|
56
|
+
user: "ClickHouse::User",
|
|
57
|
+
role: "ClickHouse::Role",
|
|
58
|
+
rowPolicy: "ClickHouse::RowPolicy",
|
|
59
|
+
grant: "ClickHouse::Grant",
|
|
60
|
+
quota: "ClickHouse::Quota",
|
|
61
|
+
settingsProfile: "ClickHouse::SettingsProfile",
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* A string as a quoted, escaped ClickHouse string literal. An interpolated
|
|
65
|
+
* plain string is SQL text; wrap it in `literal()` when it is a value.
|
|
66
|
+
*/
|
|
67
|
+
export function literal(value) {
|
|
68
|
+
if (value === null)
|
|
69
|
+
return new SqlLiteral("NULL");
|
|
70
|
+
if (typeof value !== "string")
|
|
71
|
+
return new SqlLiteral(String(value));
|
|
72
|
+
return new SqlLiteral(`'${value.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`);
|
|
73
|
+
}
|
|
74
|
+
// ── Entities ───────────────────────────────────────────────────────────
|
|
75
|
+
/** The members a ClickHouse entity has besides its props (the shared core's {@link SqlObject}). */
|
|
76
|
+
export class ClickHouseObject extends SqlObject {
|
|
77
|
+
}
|
|
78
|
+
function makeEntity(entityType, sqlName, props, columnNames, dependsOn) {
|
|
79
|
+
return makeSqlEntity(ClickHouseObject.prototype, entityType, sqlName, props, columnNames, dependsOn);
|
|
80
|
+
}
|
|
81
|
+
export function isClickHouseObject(value) {
|
|
82
|
+
return isSqlObjectOf(value, "ClickHouse::");
|
|
83
|
+
}
|
|
84
|
+
/** A column reference: an AttrRef whose parent is a ClickHouse table or view. */
|
|
85
|
+
export function isColumnRef(value) {
|
|
86
|
+
return isColumnRefOf(value, isClickHouseObject);
|
|
87
|
+
}
|
|
88
|
+
// ── Rendering interpolations ───────────────────────────────────────────
|
|
89
|
+
/** A name as ClickHouse reads it: bare when it is a plain identifier, backquoted otherwise. */
|
|
90
|
+
export function quoteIdentifier(name) {
|
|
91
|
+
return /^[A-Za-z_][A-Za-z0-9_]*$/.test(name) ? name : `\`${name.replace(/`/g, "``")}\``;
|
|
92
|
+
}
|
|
93
|
+
function renderReference(value) {
|
|
94
|
+
if (isClickHouseObject(value))
|
|
95
|
+
return value.sqlName;
|
|
96
|
+
if (isColumnRef(value))
|
|
97
|
+
return quoteIdentifier(value.attribute);
|
|
98
|
+
throw new TypeError(`not a reference: ${describe(value)}`);
|
|
99
|
+
}
|
|
100
|
+
// ── Building ───────────────────────────────────────────────────────────
|
|
101
|
+
/** ClickHouse as the shared template machinery reads it (`../core/interpolation.ts`). */
|
|
102
|
+
export const CLICKHOUSE_TEMPLATES = {
|
|
103
|
+
lexical: CLICKHOUSE_LEXICAL,
|
|
104
|
+
isObject: isClickHouseObject,
|
|
105
|
+
isColumnRef,
|
|
106
|
+
renderReference,
|
|
107
|
+
identValue: unquote,
|
|
108
|
+
unnamedOutput: (n) => `_${n}`,
|
|
109
|
+
};
|
|
110
|
+
/** The tag's splice: plain values become SQL text, references stay `ref` tokens. */
|
|
111
|
+
const splice = (tag, parts, values) => spliceWith(CLICKHOUSE_TEMPLATES, tag, parts, values);
|
|
112
|
+
const req = (s) => s ?? "";
|
|
113
|
+
/** A `'quoted'` string literal's value. */
|
|
114
|
+
function stringValue(s) {
|
|
115
|
+
if (s === undefined)
|
|
116
|
+
return undefined;
|
|
117
|
+
if (!/^'.*'$/s.test(s))
|
|
118
|
+
return s;
|
|
119
|
+
return s.slice(1, -1).replace(/''/g, "'").replace(/\\(.)/g, "$1");
|
|
120
|
+
}
|
|
121
|
+
function columnDef(ctx, c) {
|
|
122
|
+
for (const span of [c.nameSpan, c.type, c.default?.expr, c.codec, c.ttl, c.comment, c.statistics, c.settings])
|
|
123
|
+
feed(ctx, span, "columns");
|
|
124
|
+
const def = { name: c.name || req(text(ctx, c.nameSpan)) };
|
|
125
|
+
const set = (k, v) => {
|
|
126
|
+
if (v !== undefined)
|
|
127
|
+
def[k] = v;
|
|
128
|
+
};
|
|
129
|
+
set("type", text(ctx, c.type));
|
|
130
|
+
set("nullable", c.nullable);
|
|
131
|
+
if (c.default) {
|
|
132
|
+
const expr = text(ctx, c.default.expr);
|
|
133
|
+
def.default = expr === undefined ? { kind: c.default.kind } : { kind: c.default.kind, expr };
|
|
134
|
+
}
|
|
135
|
+
set("codec", text(ctx, c.codec));
|
|
136
|
+
set("ttl", text(ctx, c.ttl));
|
|
137
|
+
set("comment", stringValue(text(ctx, c.comment)));
|
|
138
|
+
set("statistics", text(ctx, c.statistics));
|
|
139
|
+
set("settings", text(ctx, c.settings));
|
|
140
|
+
return def;
|
|
141
|
+
}
|
|
142
|
+
function storageProps(ctx, node) {
|
|
143
|
+
const out = {};
|
|
144
|
+
if (node.engine) {
|
|
145
|
+
feed(ctx, node.engine.nameSpan, "engine.name");
|
|
146
|
+
const name = node.engine.name || req(text(ctx, node.engine.nameSpan));
|
|
147
|
+
out.engine = node.engine.args ? { name, args: node.engine.args.map((a) => req(text(ctx, a, "engine.args"))) } : { name };
|
|
148
|
+
}
|
|
149
|
+
const put = (k, span) => {
|
|
150
|
+
const v = text(ctx, span, k);
|
|
151
|
+
if (v !== undefined)
|
|
152
|
+
out[k] = v;
|
|
153
|
+
};
|
|
154
|
+
put("orderBy", node.orderBy);
|
|
155
|
+
put("primaryKey", node.primaryKey);
|
|
156
|
+
put("partitionBy", node.partitionBy);
|
|
157
|
+
put("sampleBy", node.sampleBy);
|
|
158
|
+
put("ttl", node.ttl);
|
|
159
|
+
feed(ctx, node.settingsClause, "settings");
|
|
160
|
+
if (node.settings)
|
|
161
|
+
out.settings = Object.fromEntries(node.settings.map((s) => [s.key, req(text(ctx, s.value, "settings"))]));
|
|
162
|
+
return out;
|
|
163
|
+
}
|
|
164
|
+
/** `name` or `db.name` from a qualified-name span; an interpolated database entity names the database. */
|
|
165
|
+
function qualified(ctx, span) {
|
|
166
|
+
const sig = ctx.tokens.slice(span.from, span.to).filter((t) => !isTrivia(t));
|
|
167
|
+
const pieces = [];
|
|
168
|
+
const splices = [];
|
|
169
|
+
for (const t of sig) {
|
|
170
|
+
if (t.kind === "punct" && t.text === ".")
|
|
171
|
+
continue;
|
|
172
|
+
if (t.kind === "ref") {
|
|
173
|
+
const v = ctx.values[t.part];
|
|
174
|
+
const refPieces = isClickHouseObject(v) && v.entityType === CLICKHOUSE_ENTITY_TYPES.database
|
|
175
|
+
? [v.props.name]
|
|
176
|
+
: renderReference(v).split(".");
|
|
177
|
+
for (const piece of refPieces) {
|
|
178
|
+
pieces.push(piece);
|
|
179
|
+
splices.push(undefined);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
pieces.push(unquote(t.text));
|
|
184
|
+
splices.push(t.splice);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
const last = pieces.length - 1;
|
|
188
|
+
splices.forEach((splice, i) => {
|
|
189
|
+
if (splice !== undefined)
|
|
190
|
+
ctx.fed[splice].add(i === last ? "name" : "database");
|
|
191
|
+
});
|
|
192
|
+
return pieces.length >= 2 ? { database: pieces[pieces.length - 2], name: pieces[pieces.length - 1] } : { name: pieces[0] ?? "" };
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* The `${}` that stand alone as an element of a sort key (`ORDER BY ${t.columns.a}`,
|
|
196
|
+
* `ORDER BY (${t.columns.a}, b)`), each a column that must not be Nullable
|
|
197
|
+
* (#3751). An element inside an expression (`ifNull(${...}, '')`) is not one.
|
|
198
|
+
*/
|
|
199
|
+
function sortKeySlots(ctx, span) {
|
|
200
|
+
if (!span)
|
|
201
|
+
return [];
|
|
202
|
+
let sig = ctx.tokens.slice(span.from, span.to).filter((t) => !isTrivia(t));
|
|
203
|
+
const isPunct = (i, p) => sig[i]?.kind === "punct" && sig[i].text === p;
|
|
204
|
+
if (isPunct(0, "(") && isPunct(sig.length - 1, ")")) {
|
|
205
|
+
let depth = 0;
|
|
206
|
+
let closesAt = -1;
|
|
207
|
+
for (let i = 0; i < sig.length && closesAt < 0; i++) {
|
|
208
|
+
if (isPunct(i, "("))
|
|
209
|
+
depth++;
|
|
210
|
+
else if (isPunct(i, ")") && --depth === 0)
|
|
211
|
+
closesAt = i;
|
|
212
|
+
}
|
|
213
|
+
if (closesAt === sig.length - 1)
|
|
214
|
+
sig = sig.slice(1, -1);
|
|
215
|
+
}
|
|
216
|
+
const out = [];
|
|
217
|
+
let depth = 0;
|
|
218
|
+
let element = [];
|
|
219
|
+
const flush = () => {
|
|
220
|
+
const t = element[0];
|
|
221
|
+
if (element.length === 1 && t?.kind === "ref" && isColumnRef(ctx.values[t.part]))
|
|
222
|
+
out.push({ index: t.part, kind: "sortKey" });
|
|
223
|
+
element = [];
|
|
224
|
+
};
|
|
225
|
+
for (const t of sig) {
|
|
226
|
+
if (t.kind === "punct" && t.text === "(")
|
|
227
|
+
depth++;
|
|
228
|
+
else if (t.kind === "punct" && t.text === ")")
|
|
229
|
+
depth--;
|
|
230
|
+
if (depth === 0 && t.kind === "punct" && t.text === ",")
|
|
231
|
+
flush();
|
|
232
|
+
else
|
|
233
|
+
element.push(t);
|
|
234
|
+
}
|
|
235
|
+
flush();
|
|
236
|
+
return out;
|
|
237
|
+
}
|
|
238
|
+
function build(tag, strings, values) {
|
|
239
|
+
const parts = templateParts(strings);
|
|
240
|
+
const tokens = splice(tag, parts, values);
|
|
241
|
+
let node;
|
|
242
|
+
try {
|
|
243
|
+
node = parseCreate(tokens);
|
|
244
|
+
}
|
|
245
|
+
catch (err) {
|
|
246
|
+
if (!(err instanceof SqlSyntaxError))
|
|
247
|
+
throw err;
|
|
248
|
+
throw templateSyntaxError(tag, parts, err);
|
|
249
|
+
}
|
|
250
|
+
const ctx = { d: CLICKHOUSE_TEMPLATES, tokens, values, fed: values.map(() => new Set()) };
|
|
251
|
+
for (const t of tokens)
|
|
252
|
+
if (t.splice !== undefined)
|
|
253
|
+
ctx.fed[t.splice].add("ddl");
|
|
254
|
+
if (node.statement !== (tag === "func" ? "function" : tag)) {
|
|
255
|
+
const holds = node.statement === "database"
|
|
256
|
+
? "CREATE DATABASE"
|
|
257
|
+
: node.statement === "table"
|
|
258
|
+
? "CREATE TABLE"
|
|
259
|
+
: node.statement === "dictionary"
|
|
260
|
+
? "CREATE DICTIONARY"
|
|
261
|
+
: node.statement === "function"
|
|
262
|
+
? "CREATE FUNCTION"
|
|
263
|
+
: "CREATE VIEW";
|
|
264
|
+
throw new SqlTemplateError(tag, `holds a ${holds}; use the ${node.statement === "function" ? "func" : node.statement} tag`, 0, 0);
|
|
265
|
+
}
|
|
266
|
+
const { database, name } = qualified(ctx, node.name);
|
|
267
|
+
const sqlName = database ? `${quoteIdentifier(database)}.${quoteIdentifier(name)}` : quoteIdentifier(name);
|
|
268
|
+
const ddl = req(untokenize(tokens, (i) => renderReference(values[i])).trim().replace(/;\s*$/, ""));
|
|
269
|
+
const source = { strings: parts };
|
|
270
|
+
const dependsOn = [...new Set(values.filter((v) => isClickHouseObject(v) || isColumnRef(v)))];
|
|
271
|
+
const common = {
|
|
272
|
+
name,
|
|
273
|
+
database,
|
|
274
|
+
onCluster: text(ctx, node.onCluster, "onCluster"),
|
|
275
|
+
comment: stringValue(text(ctx, node.comment, "comment")),
|
|
276
|
+
unparsed: unparsedClauses(ctx, node.unknown),
|
|
277
|
+
};
|
|
278
|
+
const done = (entity) => {
|
|
279
|
+
setInterpolationFields(entity, ctx.fed.map((paths) => [...paths].sort()));
|
|
280
|
+
if (node.statement === "table" || node.statement === "view")
|
|
281
|
+
setTypedSlots(entity, [...sortKeySlots(ctx, node.orderBy), ...sortKeySlots(ctx, node.primaryKey)]);
|
|
282
|
+
return entity;
|
|
283
|
+
};
|
|
284
|
+
if (node.statement === "database") {
|
|
285
|
+
feed(ctx, node.engine?.nameSpan, "engine.name");
|
|
286
|
+
const engine = node.engine
|
|
287
|
+
? node.engine.args
|
|
288
|
+
? { name: node.engine.name, args: node.engine.args.map((a) => req(text(ctx, a, "engine.args"))) }
|
|
289
|
+
: { name: node.engine.name }
|
|
290
|
+
: undefined;
|
|
291
|
+
const settings = node.settings
|
|
292
|
+
? Object.fromEntries(node.settings.map((s) => [s.key, req(text(ctx, s.value, "settings"))]))
|
|
293
|
+
: undefined;
|
|
294
|
+
const props = strip({ ...common, engine, settings, ddl, source });
|
|
295
|
+
return done(makeEntity(CLICKHOUSE_ENTITY_TYPES.database, quoteIdentifier(name), props, undefined, dependsOn));
|
|
296
|
+
}
|
|
297
|
+
if (node.statement === "table") {
|
|
298
|
+
const columns = node.columns.map((c) => columnDef(ctx, c));
|
|
299
|
+
const props = strip({
|
|
300
|
+
...common,
|
|
301
|
+
orReplace: node.orReplace,
|
|
302
|
+
ifNotExists: node.ifNotExists,
|
|
303
|
+
columns,
|
|
304
|
+
indexes: node.indexes.map((i) => strip({
|
|
305
|
+
name: i.name,
|
|
306
|
+
expr: req(text(ctx, i.expr, "indexes")),
|
|
307
|
+
type: req(text(ctx, i.type, "indexes")),
|
|
308
|
+
granularity: text(ctx, i.granularity, "indexes"),
|
|
309
|
+
})),
|
|
310
|
+
projections: node.projections.map((p) => ({ name: p.name, definition: req(text(ctx, p.body, "projections")) })),
|
|
311
|
+
constraints: node.constraints.map((c) => ({ name: c.name, kind: c.kind, expr: req(text(ctx, c.expr, "constraints")) })),
|
|
312
|
+
asFunction: text(ctx, node.asFunction, "asFunction"),
|
|
313
|
+
...storageProps(ctx, node),
|
|
314
|
+
ddl,
|
|
315
|
+
source,
|
|
316
|
+
});
|
|
317
|
+
return done(makeEntity(CLICKHOUSE_ENTITY_TYPES.table, sqlName, props, columns.map((c) => c.name), dependsOn));
|
|
318
|
+
}
|
|
319
|
+
if (node.statement === "function") {
|
|
320
|
+
for (const p of node.params)
|
|
321
|
+
feed(ctx, p.span, "params");
|
|
322
|
+
const props = strip({
|
|
323
|
+
name,
|
|
324
|
+
onCluster: common.onCluster,
|
|
325
|
+
orReplace: node.orReplace,
|
|
326
|
+
ifNotExists: node.ifNotExists,
|
|
327
|
+
params: node.params.map((p) => p.name || req(text(ctx, p.span))),
|
|
328
|
+
body: req(text(ctx, node.body, "body")),
|
|
329
|
+
ddl,
|
|
330
|
+
source,
|
|
331
|
+
});
|
|
332
|
+
return done(makeEntity(CLICKHOUSE_ENTITY_TYPES.function, quoteIdentifier(name), props, undefined, dependsOn));
|
|
333
|
+
}
|
|
334
|
+
if (node.statement === "dictionary") {
|
|
335
|
+
const columns = node.attributes.map((a) => {
|
|
336
|
+
for (const span of [a.nameSpan, a.type, a.default, a.expression])
|
|
337
|
+
feed(ctx, span, "columns");
|
|
338
|
+
return strip({
|
|
339
|
+
name: a.name || req(text(ctx, a.nameSpan)),
|
|
340
|
+
type: req(text(ctx, a.type)),
|
|
341
|
+
default: text(ctx, a.default),
|
|
342
|
+
expression: text(ctx, a.expression),
|
|
343
|
+
flags: a.flags.length > 0 ? a.flags : undefined,
|
|
344
|
+
});
|
|
345
|
+
});
|
|
346
|
+
const settings = text(ctx, node.settings, "settings");
|
|
347
|
+
const props = strip({
|
|
348
|
+
...common,
|
|
349
|
+
orReplace: node.orReplace,
|
|
350
|
+
ifNotExists: node.ifNotExists,
|
|
351
|
+
columns,
|
|
352
|
+
primaryKey: req(text(ctx, node.primaryKey, "primaryKey")),
|
|
353
|
+
dataSource: req(text(ctx, node.source, "dataSource")),
|
|
354
|
+
layout: req(text(ctx, node.layout, "layout")),
|
|
355
|
+
lifetime: text(ctx, node.lifetime, "lifetime"),
|
|
356
|
+
range: text(ctx, node.range, "range"),
|
|
357
|
+
settings: settings === undefined ? undefined : dictionarySettings(settings),
|
|
358
|
+
ddl,
|
|
359
|
+
source,
|
|
360
|
+
});
|
|
361
|
+
return done(makeEntity(CLICKHOUSE_ENTITY_TYPES.dictionary, sqlName, props, columns.map((c) => c.name), dependsOn));
|
|
362
|
+
}
|
|
363
|
+
const lin = lineage(ctx, node.select);
|
|
364
|
+
let to;
|
|
365
|
+
if (node.to) {
|
|
366
|
+
const target = node.to.refs.length === 1 ? values[node.to.refs[0]] : undefined;
|
|
367
|
+
to = isClickHouseObject(target) ? target : text(ctx, node.to, "to");
|
|
368
|
+
}
|
|
369
|
+
const columns = node.columns.map((c) => columnDef(ctx, c));
|
|
370
|
+
const props = strip({
|
|
371
|
+
...common,
|
|
372
|
+
orReplace: node.orReplace,
|
|
373
|
+
ifNotExists: node.ifNotExists,
|
|
374
|
+
refresh: text(ctx, node.refresh, "refresh"),
|
|
375
|
+
append: node.append,
|
|
376
|
+
to,
|
|
377
|
+
columns,
|
|
378
|
+
...storageProps(ctx, node),
|
|
379
|
+
populate: node.populate,
|
|
380
|
+
empty: node.empty,
|
|
381
|
+
security: text(ctx, node.security, "security"),
|
|
382
|
+
select: req(text(ctx, node.select, "select")),
|
|
383
|
+
reads: lin.reads,
|
|
384
|
+
lineage: lin.edges,
|
|
385
|
+
ddl,
|
|
386
|
+
source,
|
|
387
|
+
});
|
|
388
|
+
const outputs = columns.length > 0 ? columns.map((c) => c.name) : lin.edges.map((e) => e.output);
|
|
389
|
+
const type = node.materialized ? CLICKHOUSE_ENTITY_TYPES.materializedView : CLICKHOUSE_ENTITY_TYPES.view;
|
|
390
|
+
feed(ctx, node.select, "lineage");
|
|
391
|
+
return done(makeEntity(type, sqlName, props, outputs, dependsOn));
|
|
392
|
+
}
|
|
393
|
+
/** `a = 1, b = 'x'` as a record of each setting's value as written. */
|
|
394
|
+
function dictionarySettings(text) {
|
|
395
|
+
const out = {};
|
|
396
|
+
let depth = 0;
|
|
397
|
+
let cur = "";
|
|
398
|
+
const flush = () => {
|
|
399
|
+
const at = cur.indexOf("=");
|
|
400
|
+
if (at > 0)
|
|
401
|
+
out[cur.slice(0, at).trim()] = cur.slice(at + 1).trim();
|
|
402
|
+
cur = "";
|
|
403
|
+
};
|
|
404
|
+
for (const ch of text) {
|
|
405
|
+
if (ch === "(")
|
|
406
|
+
depth++;
|
|
407
|
+
else if (ch === ")")
|
|
408
|
+
depth--;
|
|
409
|
+
if (ch === "," && depth === 0)
|
|
410
|
+
flush();
|
|
411
|
+
else
|
|
412
|
+
cur += ch;
|
|
413
|
+
}
|
|
414
|
+
flush();
|
|
415
|
+
return out;
|
|
416
|
+
}
|
|
417
|
+
/** `` database`CREATE DATABASE ...` ``: one ClickHouse database, parsed at build time. */
|
|
418
|
+
export function database(strings, ...values) {
|
|
419
|
+
return build("database", strings, values);
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* `` table`CREATE TABLE ...` ``: one ClickHouse table, parsed at build time.
|
|
423
|
+
* Given its qualified name as the type argument (`` table<"shop.events">`...` ``),
|
|
424
|
+
* its columns are the ones `chant sql types` declared for that name.
|
|
425
|
+
*/
|
|
426
|
+
export function table(strings, ...values) {
|
|
427
|
+
return build("table", strings, values);
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* `` view`CREATE [MATERIALIZED] VIEW ...` ``: one view or materialized view,
|
|
431
|
+
* with the tables it reads and its column lineage. Takes its qualified name
|
|
432
|
+
* as the type argument, as {@link table} does.
|
|
433
|
+
*/
|
|
434
|
+
export function view(strings, ...values) {
|
|
435
|
+
return build("view", strings, values);
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* `` dictionary`CREATE DICTIONARY ...` ``: one dictionary, its attributes,
|
|
439
|
+
* key, source, layout and lifetime, parsed at build time (#3682). Its
|
|
440
|
+
* attributes are columns: `${rates.columns.rate}`.
|
|
441
|
+
*/
|
|
442
|
+
export function dictionary(strings, ...values) {
|
|
443
|
+
return build("dictionary", strings, values);
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* `` func`CREATE FUNCTION ...` ``: one SQL user-defined function, a lambda
|
|
447
|
+
* with a name (#3682). It belongs to no database, and has no comment, so it
|
|
448
|
+
* carries no ownership marker: chant replaces it when its declaration
|
|
449
|
+
* changes and never drops it.
|
|
450
|
+
*/
|
|
451
|
+
export function func(strings, ...values) {
|
|
452
|
+
return build("func", strings, values);
|
|
453
|
+
}
|
|
454
|
+
const ACCESS_STATEMENT = { user: "user", role: "role", policy: "rowPolicy", quota: "quota", settingsProfile: "settingsProfile", grant: "grant" };
|
|
455
|
+
const ACCESS_HOLDS = {
|
|
456
|
+
user: ["CREATE USER", "user"],
|
|
457
|
+
role: ["CREATE ROLE", "role"],
|
|
458
|
+
rowPolicy: ["CREATE ROW POLICY", "policy"],
|
|
459
|
+
quota: ["CREATE QUOTA", "quota"],
|
|
460
|
+
settingsProfile: ["CREATE SETTINGS PROFILE", "settingsProfile"],
|
|
461
|
+
grant: ["GRANT", "grant"],
|
|
462
|
+
};
|
|
463
|
+
function buildAccess(tag, strings, values) {
|
|
464
|
+
const parts = templateParts(strings);
|
|
465
|
+
const tokens = splice(tag, parts, values);
|
|
466
|
+
let node;
|
|
467
|
+
try {
|
|
468
|
+
node = parseAccess(tokens);
|
|
469
|
+
}
|
|
470
|
+
catch (err) {
|
|
471
|
+
if (!(err instanceof SqlSyntaxError))
|
|
472
|
+
throw err;
|
|
473
|
+
throw templateSyntaxError(tag, parts, err);
|
|
474
|
+
}
|
|
475
|
+
if (node.statement !== ACCESS_STATEMENT[tag]) {
|
|
476
|
+
const [holds, use] = ACCESS_HOLDS[node.statement];
|
|
477
|
+
throw new SqlTemplateError(tag, `holds a ${holds}; use the ${use} tag`, 0, 0);
|
|
478
|
+
}
|
|
479
|
+
if (node.revoke)
|
|
480
|
+
throw new SqlTemplateError(tag, "holds a REVOKE; declare the grants a grantee keeps, and a plan revokes the rest", 0, 0);
|
|
481
|
+
const ctx = { d: CLICKHOUSE_TEMPLATES, tokens, values, fed: values.map(() => new Set()) };
|
|
482
|
+
for (const t of tokens)
|
|
483
|
+
if (t.splice !== undefined)
|
|
484
|
+
ctx.fed[t.splice].add("ddl");
|
|
485
|
+
const ddl = req(untokenize(tokens, (i) => renderReference(values[i])).trim().replace(/;\s*$/, ""));
|
|
486
|
+
const source = { strings: parts };
|
|
487
|
+
const dependsOn = [...new Set(values.filter((v) => isClickHouseObject(v) || isColumnRef(v)))];
|
|
488
|
+
const done = (entity) => {
|
|
489
|
+
setInterpolationFields(entity, ctx.fed.map((paths) => [...paths].sort()));
|
|
490
|
+
return entity;
|
|
491
|
+
};
|
|
492
|
+
const rendered = (span) => span ? untokenize(tokens.slice(span.from, span.to), (i) => renderReference(values[i])).trim() || undefined : undefined;
|
|
493
|
+
const name = node.name ? unquote(rendered(node.name) ?? "") : "";
|
|
494
|
+
const c = node.clauses;
|
|
495
|
+
const common = { name, onCluster: text(ctx, node.onCluster, "onCluster"), orReplace: node.orReplace || undefined, ifNotExists: node.ifNotExists || undefined };
|
|
496
|
+
if (node.statement === "user") {
|
|
497
|
+
const props = strip({
|
|
498
|
+
...common,
|
|
499
|
+
identified: c.notIdentified ? "NOT IDENTIFIED" : rendered(c.identified),
|
|
500
|
+
host: rendered(c.host),
|
|
501
|
+
validUntil: rendered(c.validUntil),
|
|
502
|
+
defaultRole: rendered(c.defaultRole),
|
|
503
|
+
defaultDatabase: rendered(c.defaultDatabase),
|
|
504
|
+
grantees: rendered(c.grantees),
|
|
505
|
+
settings: rendered(c.settings),
|
|
506
|
+
ddl,
|
|
507
|
+
source,
|
|
508
|
+
});
|
|
509
|
+
return done(makeEntity(CLICKHOUSE_ENTITY_TYPES.user, quoteIdentifier(name), props, undefined, dependsOn));
|
|
510
|
+
}
|
|
511
|
+
if (node.statement === "role") {
|
|
512
|
+
const props = strip({ ...common, settings: rendered(c.settings), ddl, source });
|
|
513
|
+
return done(makeEntity(CLICKHOUSE_ENTITY_TYPES.role, quoteIdentifier(name), props, undefined, dependsOn));
|
|
514
|
+
}
|
|
515
|
+
if (node.statement === "quota") {
|
|
516
|
+
const props = strip({ ...common, keyed: rendered(c.keyed), intervals: rendered(c.for), to: rendered(c.to), ddl, source });
|
|
517
|
+
return done(makeEntity(CLICKHOUSE_ENTITY_TYPES.quota, quoteIdentifier(name), props, undefined, dependsOn));
|
|
518
|
+
}
|
|
519
|
+
if (node.statement === "settingsProfile") {
|
|
520
|
+
const props = strip({ ...common, settings: rendered(c.settings), to: rendered(c.to), ddl, source });
|
|
521
|
+
return done(makeEntity(CLICKHOUSE_ENTITY_TYPES.settingsProfile, quoteIdentifier(name), props, undefined, dependsOn));
|
|
522
|
+
}
|
|
523
|
+
if (node.statement === "rowPolicy") {
|
|
524
|
+
const { database, name: tableName } = qualified(ctx, node.table);
|
|
525
|
+
const props = strip({
|
|
526
|
+
...common,
|
|
527
|
+
database,
|
|
528
|
+
table: tableName,
|
|
529
|
+
as: rendered(c.as)?.toUpperCase(),
|
|
530
|
+
for: rendered(c.for),
|
|
531
|
+
using: req(rendered(c.using)),
|
|
532
|
+
to: rendered(c.to),
|
|
533
|
+
ddl,
|
|
534
|
+
source,
|
|
535
|
+
});
|
|
536
|
+
return done(makeEntity(CLICKHOUSE_ENTITY_TYPES.rowPolicy, quoteIdentifier(name), props, undefined, dependsOn));
|
|
537
|
+
}
|
|
538
|
+
const items = node.items.map((i) => rendered(i) ?? "");
|
|
539
|
+
const to = node.grantees.map((g) => unquote(rendered(g) ?? ""));
|
|
540
|
+
const props = strip({
|
|
541
|
+
name: to.join(", "),
|
|
542
|
+
onCluster: common.onCluster,
|
|
543
|
+
privileges: node.target ? items : undefined,
|
|
544
|
+
roles: node.target ? undefined : items.map(unquote),
|
|
545
|
+
on: rendered(node.target),
|
|
546
|
+
to,
|
|
547
|
+
withGrantOption: node.withOption === "GRANT" || undefined,
|
|
548
|
+
withAdminOption: node.withOption === "ADMIN" || undefined,
|
|
549
|
+
ddl,
|
|
550
|
+
source,
|
|
551
|
+
});
|
|
552
|
+
return done(makeEntity(CLICKHOUSE_ENTITY_TYPES.grant, "", props, undefined, dependsOn));
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* `` user`CREATE USER ...` ``: one user (#3682). A password is the
|
|
556
|
+
* environment's: `IDENTIFIED BY` is refused, and a user declared without
|
|
557
|
+
* `IDENTIFIED` is compared without it and is not created by chant.
|
|
558
|
+
*/
|
|
559
|
+
export function user(strings, ...values) {
|
|
560
|
+
return buildAccess("user", strings, values);
|
|
561
|
+
}
|
|
562
|
+
/** `` role`CREATE ROLE ...` ``: one role (#3682). Interpolate it where a grant or a user names it. */
|
|
563
|
+
export function role(strings, ...values) {
|
|
564
|
+
return buildAccess("role", strings, values);
|
|
565
|
+
}
|
|
566
|
+
/** `` policy`CREATE ROW POLICY ...` ``: one row policy on one table (#3682). */
|
|
567
|
+
export function policy(strings, ...values) {
|
|
568
|
+
return buildAccess("policy", strings, values);
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* `` quota`CREATE QUOTA ...` ``: one quota (#3911), its key, its intervals'
|
|
572
|
+
* limits and whom it applies to. A plan changes it with `ALTER QUOTA`, one
|
|
573
|
+
* clause or interval at a time, and never drops it.
|
|
574
|
+
*/
|
|
575
|
+
export function quota(strings, ...values) {
|
|
576
|
+
return buildAccess("quota", strings, values);
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* `` settingsProfile`CREATE SETTINGS PROFILE ...` ``: one settings profile
|
|
580
|
+
* (#3911), its settings, their constraints and the profiles it inherits.
|
|
581
|
+
* Interpolate it where a user, a role or another profile names it.
|
|
582
|
+
*/
|
|
583
|
+
export function settingsProfile(strings, ...values) {
|
|
584
|
+
return buildAccess("settingsProfile", strings, values);
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* `` grant`GRANT ...` ``: privileges or roles granted to users or roles
|
|
588
|
+
* (#3682). Every grant declaration naming a grantee adds up to its complete
|
|
589
|
+
* list: a plan revokes what it holds besides.
|
|
590
|
+
*/
|
|
591
|
+
export function grant(strings, ...values) {
|
|
592
|
+
return buildAccess("grant", strings, values);
|
|
593
|
+
}
|