@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,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checking a ClickHouse column type against the pinned catalog, for SQLCH121
|
|
3
|
+
* (the family), SQLCH122 (its parameters) and SQLCH123 (a wrapper the server
|
|
4
|
+
* refuses). One walk over the type answers all three; each check keeps the
|
|
5
|
+
* findings with its own id.
|
|
6
|
+
*
|
|
7
|
+
* Families come from `system.data_type_families` (`TYPE_FAMILIES`): a name is
|
|
8
|
+
* case-sensitive unless the catalog marks it case-insensitive, and an alias
|
|
9
|
+
* resolves to its family. Parameters come from the `TYPE_PARAMETERS` overlay.
|
|
10
|
+
* The walk goes into every parameter that is itself a type, so `UInt46`
|
|
11
|
+
* inside `Array(Map(String, UInt46))` is found.
|
|
12
|
+
*/
|
|
13
|
+
import { CLICKHOUSE_VERSION, TYPE_FAMILIES } from "../../generated/clickhouse.js";
|
|
14
|
+
import { TYPE_PARAMETERS } from "../../clickhouse/overlays/types.js";
|
|
15
|
+
import { numericParam, readType, typeTokens } from "../../clickhouse/type-syntax.js";
|
|
16
|
+
import { clickhouseObjects } from "./sql-helpers.js";
|
|
17
|
+
const FAMILIES = TYPE_FAMILIES;
|
|
18
|
+
const FOLDED = new Map();
|
|
19
|
+
const BY_LOWER = new Map();
|
|
20
|
+
for (const [name, spec] of Object.entries(FAMILIES)) {
|
|
21
|
+
if (spec?.caseInsensitive)
|
|
22
|
+
FOLDED.set(name.toLowerCase(), spec);
|
|
23
|
+
BY_LOWER.set(name.toLowerCase(), name);
|
|
24
|
+
}
|
|
25
|
+
/** A family by name as the server resolves it: exactly, or in any case when the catalog marks it case-insensitive. */
|
|
26
|
+
export function familyOf(name) {
|
|
27
|
+
return FAMILIES[name] ?? FOLDED.get(name.toLowerCase());
|
|
28
|
+
}
|
|
29
|
+
/** The MySQL-compatible words the parser keeps after a type (`INT(11) UNSIGNED`); the server ignores them. */
|
|
30
|
+
const MODIFIERS = new Set(["UNSIGNED", "SIGNED", "AUTO_INCREMENT", "ZEROFILL"]);
|
|
31
|
+
/**
|
|
32
|
+
* What `Nullable(...)` cannot hold. `Tuple` is not here: the pinned server
|
|
33
|
+
* takes `Nullable(Tuple(...))` when `enable_nullable_tuple_type` is on, a
|
|
34
|
+
* setting of the server's profile that the build cannot see.
|
|
35
|
+
*/
|
|
36
|
+
const NOT_INSIDE_NULLABLE = {
|
|
37
|
+
Array: "make the elements Nullable instead, Array(Nullable(T)), or drop Nullable: an empty array stands for no value",
|
|
38
|
+
Map: "make the values Nullable instead, Map(K, Nullable(V)), or drop Nullable",
|
|
39
|
+
Nested: "make the fields Nullable instead",
|
|
40
|
+
LowCardinality: "write LowCardinality(Nullable(T)) instead",
|
|
41
|
+
Variant: "drop Nullable: a Variant already holds NULL",
|
|
42
|
+
Nullable: "drop one Nullable",
|
|
43
|
+
};
|
|
44
|
+
/** Families whose parameters are settings or expressions rather than types and literals; left to the server. */
|
|
45
|
+
const OPAQUE = new Set(["JSON", "Dynamic"]);
|
|
46
|
+
/** Families whose one repeated parameter needs at least one value. */
|
|
47
|
+
const NEEDS_ONE = new Set(["Enum", "Enum8", "Enum16", "Nested", "Variant"]);
|
|
48
|
+
const isName = (t) => t?.kind === "ident" || t?.kind === "qident";
|
|
49
|
+
/** Every finding for one column type, in the order the walk meets them. */
|
|
50
|
+
export function typeFindings(type, version) {
|
|
51
|
+
const tokens = typeTokens(type);
|
|
52
|
+
if (!tokens)
|
|
53
|
+
return [];
|
|
54
|
+
const out = [];
|
|
55
|
+
walk(tokens, version, out);
|
|
56
|
+
return out;
|
|
57
|
+
}
|
|
58
|
+
function walk(tokens, version, out, named = false) {
|
|
59
|
+
const syntax = readType(tokens);
|
|
60
|
+
if (!syntax)
|
|
61
|
+
return undefined;
|
|
62
|
+
let words = syntax.words;
|
|
63
|
+
// A named tuple or Nested element, `a String`: the first word is the element's name.
|
|
64
|
+
if (named && words.length >= 2 && !familyOf(words.map((w) => w.text).join(" "))) {
|
|
65
|
+
const at = tokens.indexOf(words[0]);
|
|
66
|
+
return walk(tokens.slice(at + 1), version, out);
|
|
67
|
+
}
|
|
68
|
+
if (words.some((w) => w.kind !== "ident"))
|
|
69
|
+
return undefined;
|
|
70
|
+
// `INT UNSIGNED` is a family of its own; `Int32 UNSIGNED` is Int32 with a modifier the server ignores.
|
|
71
|
+
let spec = familyOf(words.map((w) => w.text).join(" "));
|
|
72
|
+
while (!spec && words.length > 1 && MODIFIERS.has(words[words.length - 1].text.toUpperCase())) {
|
|
73
|
+
words = words.slice(0, -1);
|
|
74
|
+
spec = familyOf(words.map((w) => w.text).join(" "));
|
|
75
|
+
}
|
|
76
|
+
const written = words.map((w) => w.text).join(" ");
|
|
77
|
+
if (!spec) {
|
|
78
|
+
const other = BY_LOWER.get(written.toLowerCase());
|
|
79
|
+
const hint = other ? ` (it has ${other}, and that name is case-sensitive)` : "";
|
|
80
|
+
out.push({ checkId: "SQLCH121", detail: `${written} is not a type family ClickHouse ${version} has${hint}` });
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
const family = spec.canonical;
|
|
84
|
+
if (OPAQUE.has(family))
|
|
85
|
+
return family;
|
|
86
|
+
const expected = TYPE_PARAMETERS[family] ?? [];
|
|
87
|
+
let params = syntax.params;
|
|
88
|
+
const say = (detail) => out.push({ checkId: "SQLCH122", detail });
|
|
89
|
+
if (params === undefined) {
|
|
90
|
+
const required = expected.filter((p) => !p.optional && !p.repeated);
|
|
91
|
+
if (required.length > 0)
|
|
92
|
+
say(`${written} needs its ${required.map((p) => p.name).join(" and ")}`);
|
|
93
|
+
else if (NEEDS_ONE.has(family))
|
|
94
|
+
say(`${written} needs at least one ${expected[0].name}`);
|
|
95
|
+
return family;
|
|
96
|
+
}
|
|
97
|
+
// `AggregateFunction(1, uniq, UInt64)`: a leading number is the function's state version.
|
|
98
|
+
if (family === "AggregateFunction" && params[0] && numericParam(params[0]) !== undefined)
|
|
99
|
+
params = params.slice(1);
|
|
100
|
+
if (expected.length === 0) {
|
|
101
|
+
if (params.length > 0)
|
|
102
|
+
say(`${written} takes no parameters, and ${written}(${params.map((p) => p.text).join(", ")}) gives ${params.length}`);
|
|
103
|
+
return family;
|
|
104
|
+
}
|
|
105
|
+
const repeated = expected[expected.length - 1].repeated === true;
|
|
106
|
+
if (!repeated && params.length > expected.length) {
|
|
107
|
+
say(`${written} takes at most ${expected.length} parameter${expected.length === 1 ? "" : "s"} (${expected.map((p) => p.name).join(", ")}), and is given ${params.length}`);
|
|
108
|
+
return family;
|
|
109
|
+
}
|
|
110
|
+
const required = expected.filter((p) => !p.optional && !p.repeated).length;
|
|
111
|
+
if (params.length < required) {
|
|
112
|
+
say(`${written} needs its ${expected.slice(params.length, required).map((p) => p.name).join(" and ")}`);
|
|
113
|
+
return family;
|
|
114
|
+
}
|
|
115
|
+
if (params.length === 0 && NEEDS_ONE.has(family)) {
|
|
116
|
+
say(`${written} needs at least one ${expected[0].name}`);
|
|
117
|
+
return family;
|
|
118
|
+
}
|
|
119
|
+
params.forEach((param, i) => {
|
|
120
|
+
const p = expected[Math.min(i, expected.length - 1)];
|
|
121
|
+
checkParam(written, family, p, param, version, out);
|
|
122
|
+
});
|
|
123
|
+
return family;
|
|
124
|
+
}
|
|
125
|
+
function checkParam(written, family, p, param, version, out) {
|
|
126
|
+
if (p.kind === "number" && p.range) {
|
|
127
|
+
const value = numericParam(param);
|
|
128
|
+
if (value !== undefined && (value < p.range[0] || value > p.range[1])) {
|
|
129
|
+
out.push({ checkId: "SQLCH122", detail: `${written} ${p.name} ${param.text} is outside ${p.range[0]} to ${p.range[1]}` });
|
|
130
|
+
}
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (p.kind !== "type" && p.kind !== "named-type")
|
|
134
|
+
return;
|
|
135
|
+
const inner = walk(param.tokens, version, out, p.kind === "named-type" && isName(param.tokens.find((t) => t.kind !== "ws" && t.kind !== "comment")));
|
|
136
|
+
if (family === "Nullable" && inner !== undefined && NOT_INSIDE_NULLABLE[inner]) {
|
|
137
|
+
out.push({ checkId: "SQLCH123", detail: `Nullable cannot hold ${inner}; ${NOT_INSIDE_NULLABLE[inner]}` });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/** Each declared column type in the build: tables, views that list their columns, and dictionary attributes. */
|
|
141
|
+
export function* columnTypes(objects) {
|
|
142
|
+
for (const o of objects) {
|
|
143
|
+
if (!/^ClickHouse::(Table|View|MaterializedView|Dictionary)$/.test(o.type))
|
|
144
|
+
continue;
|
|
145
|
+
const columns = (o.columns ?? []);
|
|
146
|
+
for (const c of columns)
|
|
147
|
+
if (c.type)
|
|
148
|
+
yield { object: o, column: c.name, type: c.type };
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/** The body of one of the three type checks: every column type's findings with that id. */
|
|
152
|
+
export function reportTypeFindings(id) {
|
|
153
|
+
return (ctx, report) => {
|
|
154
|
+
for (const { object, column, type } of columnTypes(clickhouseObjects(ctx))) {
|
|
155
|
+
for (const f of typeFindings(type, CLICKHOUSE_VERSION)) {
|
|
156
|
+
if (f.checkId !== id)
|
|
157
|
+
continue;
|
|
158
|
+
report({ severity: "error", message: `${object.export} (${object.name}): column ${column} ${type}: ${f.detail}`, entity: object.export });
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { sql101 } from "./sql101.js";
|
|
2
|
+
import { sql102 } from "./sql102.js";
|
|
3
|
+
import { sqlch101 } from "./sqlch101.js";
|
|
4
|
+
import { sqlch102 } from "./sqlch102.js";
|
|
5
|
+
import { sqlch103 } from "./sqlch103.js";
|
|
6
|
+
import { sqlch104 } from "./sqlch104.js";
|
|
7
|
+
import { sqlch105 } from "./sqlch105.js";
|
|
8
|
+
import { sqlch106 } from "./sqlch106.js";
|
|
9
|
+
import { sqlch107 } from "./sqlch107.js";
|
|
10
|
+
import { sqlch108 } from "./sqlch108.js";
|
|
11
|
+
import { sqlch109 } from "./sqlch109.js";
|
|
12
|
+
import { sqlch110 } from "./sqlch110.js";
|
|
13
|
+
import { sqlch111 } from "./sqlch111.js";
|
|
14
|
+
import { sqlch112 } from "./sqlch112.js";
|
|
15
|
+
import { sqlch113 } from "./sqlch113.js";
|
|
16
|
+
import { sqlch114 } from "./sqlch114.js";
|
|
17
|
+
import { sqlch115 } from "./sqlch115.js";
|
|
18
|
+
import { sqlch116 } from "./sqlch116.js";
|
|
19
|
+
import { sqlch117 } from "./sqlch117.js";
|
|
20
|
+
import { sqlch118 } from "./sqlch118.js";
|
|
21
|
+
import { sqlch119 } from "./sqlch119.js";
|
|
22
|
+
import { sqlch120 } from "./sqlch120.js";
|
|
23
|
+
import { sqlch121 } from "./sqlch121.js";
|
|
24
|
+
import { sqlch122 } from "./sqlch122.js";
|
|
25
|
+
import { sqlch123 } from "./sqlch123.js";
|
|
26
|
+
import { sqlch124 } from "./sqlch124.js";
|
|
27
|
+
import { sqlch125 } from "./sqlch125.js";
|
|
28
|
+
import { sqlch126 } from "./sqlch126.js";
|
|
29
|
+
import { sqlch127 } from "./sqlch127.js";
|
|
30
|
+
import { sqlpg101 } from "./sqlpg101.js";
|
|
31
|
+
import { sqlpg102 } from "./sqlpg102.js";
|
|
32
|
+
import { sqlpg103 } from "./sqlpg103.js";
|
|
33
|
+
import { sqlpg104 } from "./sqlpg104.js";
|
|
34
|
+
import { sqlpg105 } from "./sqlpg105.js";
|
|
35
|
+
import { sqlpg106 } from "./sqlpg106.js";
|
|
36
|
+
import { sqlpg107 } from "./sqlpg107.js";
|
|
37
|
+
import { sqlpg108 } from "./sqlpg108.js";
|
|
38
|
+
import { sqlpg109 } from "./sqlpg109.js";
|
|
39
|
+
import { sqlpg110 } from "./sqlpg110.js";
|
|
40
|
+
import { sqlpg111 } from "./sqlpg111.js";
|
|
41
|
+
import { sqlpg112 } from "./sqlpg112.js";
|
|
42
|
+
import { sqlpg113 } from "./sqlpg113.js";
|
|
43
|
+
import { sqlpg114 } from "./sqlpg114.js";
|
|
44
|
+
import { sqlpg115 } from "./sqlpg115.js";
|
|
45
|
+
import { sqlpg116 } from "./sqlpg116.js";
|
|
46
|
+
import { sqlpg117 } from "./sqlpg117.js";
|
|
47
|
+
import { sqlpg118 } from "./sqlpg118.js";
|
|
48
|
+
import { sqlpg119 } from "./sqlpg119.js";
|
|
49
|
+
import { sqlpg120 } from "./sqlpg120.js";
|
|
50
|
+
import { sqlpg121 } from "./sqlpg121.js";
|
|
51
|
+
import { sqlpg122 } from "./sqlpg122.js";
|
|
52
|
+
import { sqlpg123 } from "./sqlpg123.js";
|
|
53
|
+
import { sqlpg124 } from "./sqlpg124.js";
|
|
54
|
+
import { sqlpg125 } from "./sqlpg125.js";
|
|
55
|
+
import { sqlpg126 } from "./sqlpg126.js";
|
|
56
|
+
export const postSynthChecks = [
|
|
57
|
+
sql101,
|
|
58
|
+
sql102,
|
|
59
|
+
sqlch101,
|
|
60
|
+
sqlch102,
|
|
61
|
+
sqlch103,
|
|
62
|
+
sqlch104,
|
|
63
|
+
sqlch105,
|
|
64
|
+
sqlch106,
|
|
65
|
+
sqlch107,
|
|
66
|
+
sqlch108,
|
|
67
|
+
sqlch109,
|
|
68
|
+
sqlch110,
|
|
69
|
+
sqlch111,
|
|
70
|
+
sqlch112,
|
|
71
|
+
sqlch113,
|
|
72
|
+
sqlch114,
|
|
73
|
+
sqlch115,
|
|
74
|
+
sqlch116,
|
|
75
|
+
sqlch117,
|
|
76
|
+
sqlch118,
|
|
77
|
+
sqlch119,
|
|
78
|
+
sqlch120,
|
|
79
|
+
sqlch121,
|
|
80
|
+
sqlch122,
|
|
81
|
+
sqlch123,
|
|
82
|
+
sqlch124,
|
|
83
|
+
sqlch125,
|
|
84
|
+
sqlch126,
|
|
85
|
+
sqlch127,
|
|
86
|
+
sqlpg101,
|
|
87
|
+
sqlpg102,
|
|
88
|
+
sqlpg103,
|
|
89
|
+
sqlpg104,
|
|
90
|
+
sqlpg105,
|
|
91
|
+
sqlpg106,
|
|
92
|
+
sqlpg107,
|
|
93
|
+
sqlpg108,
|
|
94
|
+
sqlpg109,
|
|
95
|
+
sqlpg110,
|
|
96
|
+
sqlpg111,
|
|
97
|
+
sqlpg112,
|
|
98
|
+
sqlpg113,
|
|
99
|
+
sqlpg114,
|
|
100
|
+
sqlpg115,
|
|
101
|
+
sqlpg116,
|
|
102
|
+
sqlpg117,
|
|
103
|
+
sqlpg118,
|
|
104
|
+
sqlpg119,
|
|
105
|
+
sqlpg120,
|
|
106
|
+
sqlpg121,
|
|
107
|
+
sqlpg122,
|
|
108
|
+
sqlpg123,
|
|
109
|
+
sqlpg124,
|
|
110
|
+
sqlpg125,
|
|
111
|
+
sqlpg126,
|
|
112
|
+
];
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audit metadata for the Postgres post-synth checks (SQLPG1xx). Spread into
|
|
3
|
+
* `sqlAuditCatalog` by ../audit-catalog.ts. Deprecation checks are filed under
|
|
4
|
+
* best-practice or correctness: the audit categories have no deprecation of
|
|
5
|
+
* their own.
|
|
6
|
+
*/
|
|
7
|
+
import { auditRule } from "@intentius/chant/audit/catalog";
|
|
8
|
+
export const postgresPostSynthAuditEntries = {
|
|
9
|
+
SQLPG101: auditRule("SQLPG101", "merge-worthy", "guidance", "A table declares no primary key", "Add a primary key, or a UNIQUE constraint over NOT NULL columns.", { category: "best-practice" }),
|
|
10
|
+
SQLPG102: auditRule("SQLPG102", "report-only", "guidance", "A foreign key's referencing columns have no index", "Create an index whose leading columns are the foreign key's columns.", { category: "best-practice" }),
|
|
11
|
+
SQLPG103: auditRule("SQLPG103", "report-only", "guidance", "A serial column where an identity column is preferred", "Declare the column GENERATED ALWAYS AS IDENTITY.", { category: "best-practice" }),
|
|
12
|
+
SQLPG104: auditRule("SQLPG104", "report-only", "guidance", "A timestamp column without time zone", "Use timestamptz.", { category: "correctness" }),
|
|
13
|
+
SQLPG105: auditRule("SQLPG105", "report-only", "guidance", "A json column where jsonb fits", "Use jsonb unless the exact text must be kept.", { category: "best-practice" }),
|
|
14
|
+
SQLPG106: auditRule("SQLPG106", "report-only", "guidance", "A char(n) column", "Use text, or varchar(n) for a length rule.", { category: "best-practice" }),
|
|
15
|
+
SQLPG107: auditRule("SQLPG107", "report-only", "guidance", "A money column", "Use numeric(p, s) and a currency column.", { category: "correctness" }),
|
|
16
|
+
SQLPG108: auditRule("SQLPG108", "report-only", "guidance", "A varchar(n) column with a habitual length limit", "Use text, with a CHECK where the length is a rule.", { category: "best-practice" }),
|
|
17
|
+
SQLPG109: auditRule("SQLPG109", "report-only", "guidance", "An index duplicates a constraint or another index", "Drop the duplicate index.", { category: "best-practice" }),
|
|
18
|
+
SQLPG110: auditRule("SQLPG110", "report-only", "guidance", "A btree index is a prefix of another index", "Drop the prefix index; the wider one serves the same queries.", { category: "best-practice" }),
|
|
19
|
+
SQLPG111: auditRule("SQLPG111", "report-only", "guidance", "A materialized view has no unique index, so it cannot refresh concurrently", "Add a unique index over plain columns.", { category: "correctness" }),
|
|
20
|
+
SQLPG112: auditRule("SQLPG112", "report-only", "guidance", "A secret- or PII-named column with no comment on it or its table", "Add a COMMENT saying what it holds and how it is protected.", { category: "security" }),
|
|
21
|
+
SQLPG113: auditRule("SQLPG113", "report-only", "guidance", "An object is in the public schema though the project declares schemas", "Qualify the object with a declared schema.", { category: "best-practice" }),
|
|
22
|
+
SQLPG114: auditRule("SQLPG114", "merge-worthy", "guidance", "A storage parameter the object's kind or the target major does not accept", "Fix the parameter name, or use one from the target major's catalog.", { category: "correctness" }),
|
|
23
|
+
SQLPG115: auditRule("SQLPG115", "report-only", "guidance", "A feature newer than the target major", "Avoid the feature, or raise sql.postgresMajor.", { category: "correctness" }),
|
|
24
|
+
SQLPG116: auditRule("SQLPG116", "merge-worthy", "guidance", "An extension the target major no longer ships", "Remove the extension, or set sql.postgresMajor to an older major.", { category: "best-practice" }),
|
|
25
|
+
SQLPG117: auditRule("SQLPG117", "report-only", "guidance", "A view without security_invoker runs with its owner's privileges", "Set security_invoker = true in the view's WITH options.", { category: "security" }),
|
|
26
|
+
SQLPG118: auditRule("SQLPG118", "report-only", "guidance", "A table uses INHERITS where declarative partitioning replaced it", "Use PARTITION BY and PARTITION OF.", { category: "best-practice" }),
|
|
27
|
+
SQLPG119: auditRule("SQLPG119", "merge-worthy", "guidance", "A column, domain or sequence type the target major does not have", "Fix the type name, declare the type, or declare the extension that provides it.", { category: "correctness" }),
|
|
28
|
+
SQLPG120: auditRule("SQLPG120", "merge-worthy", "guidance", "A type modifier that does not fit the type", "Drop the modifier, or bring it into the type's range.", { category: "correctness" }),
|
|
29
|
+
SQLPG121: auditRule("SQLPG121", "merge-worthy", "guidance", "An identity column whose type is not smallint, integer or bigint", "Declare the identity column smallint, integer or bigint.", { category: "correctness" }),
|
|
30
|
+
SQLPG122: auditRule("SQLPG122", "merge-worthy", "guidance", "A table declares a column twice, or an enum lists a label twice", "Remove or rename the repeated column or label.", { category: "correctness" }),
|
|
31
|
+
SQLPG123: auditRule("SQLPG123", "merge-worthy", "guidance", "A key, index or grant column list names a column the table does not declare", "Fix the column name, or write it as ${table.columns.name}.", { category: "correctness" }),
|
|
32
|
+
SQLPG124: auditRule("SQLPG124", "merge-worthy", "guidance", "A foreign key names a column the referenced table lacks, or one of another type category", "Reference a declared column, and give the referencing column a type of the same category.", { category: "correctness" }),
|
|
33
|
+
SQLPG125: auditRule("SQLPG125", "merge-worthy", "guidance", "An index access method or operator class the target major does not have, or not for that method", "Use a method and operator class the target major has, or declare the extension that provides them.", { category: "correctness" }),
|
|
34
|
+
SQLPG126: auditRule("SQLPG126", "merge-worthy", "guidance", "An expression calls a function the target major does not have", "Fix the function name, declare the function, or declare the extension that provides it.", { category: "correctness" }),
|
|
35
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading the Postgres objects in the sql build output for post-synth checks:
|
|
3
|
+
* typed views of the serialized objects and the small scanners the checks
|
|
4
|
+
* share. Expressions are kept as written; the scanners answer narrow questions
|
|
5
|
+
* ("is this a bare column"), not what an expression evaluates to.
|
|
6
|
+
*/
|
|
7
|
+
import { POSTGRES_MAJORS, VERSION_RANGES } from "../../generated/postgres.js";
|
|
8
|
+
import { readPostgres } from "../../read/postgres.js";
|
|
9
|
+
import { postgresMajorOf } from "./sql-helpers.js";
|
|
10
|
+
export const isTable = (o) => o.type === "Postgres::Table";
|
|
11
|
+
export const isIndex = (o) => o.type === "Postgres::Index";
|
|
12
|
+
export const isView = (o) => o.type === "Postgres::View";
|
|
13
|
+
export const isMaterializedView = (o) => o.type === "Postgres::MaterializedView";
|
|
14
|
+
export const tablesOf = (ctx) => readPostgres(ctx).tables;
|
|
15
|
+
export const indexesOf = (ctx) => readPostgres(ctx).indexes;
|
|
16
|
+
/** The major the build targets (`postgresMajor` in the output), else the newest pinned. */
|
|
17
|
+
export const targetMajor = (ctx) => postgresMajorOf(ctx, PINNED_MAJOR);
|
|
18
|
+
/** The newest and oldest majors the lexicon carries a catalog for. */
|
|
19
|
+
export const PINNED_MAJOR = POSTGRES_MAJORS[POSTGRES_MAJORS.length - 1];
|
|
20
|
+
export const OLDEST_MAJOR = POSTGRES_MAJORS[0];
|
|
21
|
+
/** The version range the generated catalog records for `name` in `table`, if it is not in every major. */
|
|
22
|
+
export function rangeOf(table, name) {
|
|
23
|
+
return VERSION_RANGES[table][name];
|
|
24
|
+
}
|
|
25
|
+
/** A column type's leading words, lower-cased, without a length or array suffix: `timestamp(3)[]` is `timestamp`. */
|
|
26
|
+
export function baseType(type) {
|
|
27
|
+
return (type ?? "").toLowerCase().replace(/\[.*$/, "").replace(/\(.*?\)/g, "").replace(/\s+/g, " ").trim();
|
|
28
|
+
}
|
|
29
|
+
/** The `name = value` pairs of a `(a = 1, b)` storage parameter list, names lower-cased. */
|
|
30
|
+
export function storageParams(text) {
|
|
31
|
+
const out = new Map();
|
|
32
|
+
const inner = (text ?? "").trim().replace(/^\(/, "").replace(/\)$/, "");
|
|
33
|
+
for (const part of inner.split(",")) {
|
|
34
|
+
const [k, ...v] = part.split("=");
|
|
35
|
+
const name = k.trim().toLowerCase();
|
|
36
|
+
if (name)
|
|
37
|
+
out.set(name, v.join("=").trim());
|
|
38
|
+
}
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
41
|
+
/** The leading bare-column elements of an index, in order; stops at the first expression. */
|
|
42
|
+
export const leadingColumns = (index) => index.columns;
|
|
43
|
+
/** A check over the build's Postgres objects, so each check file holds its rule. */
|
|
44
|
+
export function checkOf(meta, run) {
|
|
45
|
+
return {
|
|
46
|
+
...meta,
|
|
47
|
+
check(ctx) {
|
|
48
|
+
const out = [];
|
|
49
|
+
run(ctx, (d) => out.push({ checkId: meta.id, lexicon: "sql", ...d }));
|
|
50
|
+
return out;
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/** A table that owns its columns: not a partition, not typed (`OF type`), not temporary. */
|
|
55
|
+
export const ownsColumns = (t) => t.partitionOf === undefined && t.ofType === undefined && t.persistence !== "temporary";
|
|
56
|
+
/** The catalog's relation kind for a storage-parameter list on this object, as `STORAGE_PARAMETERS.targets` spells it. */
|
|
57
|
+
export function storageTarget(o) {
|
|
58
|
+
switch (o.type) {
|
|
59
|
+
case "Postgres::Table": return "table";
|
|
60
|
+
case "Postgres::View": return "view";
|
|
61
|
+
case "Postgres::MaterializedView": return "materialized view";
|
|
62
|
+
case "Postgres::Index": return `index:${o.method ?? "btree"}`;
|
|
63
|
+
default: return undefined;
|
|
64
|
+
}
|
|
65
|
+
}
|