@intentius/chant-lexicon-sql 0.124.31 → 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,21 @@
|
|
|
1
|
+
/** A Postgres schema diff as text (the shared core's report, with the refusal of expand-and-contract changes). */
|
|
2
|
+
import { renderChangeSet } from "../../core/classifier.js";
|
|
3
|
+
import { PG_CHANGE_CLASSES, PG_CLASSIFIER_RULES } from "./rules.js";
|
|
4
|
+
import { renderMigrationOpSuggestions } from "../migrate/handoff.js";
|
|
5
|
+
export function renderPgDiff(diff, opts = {}) {
|
|
6
|
+
const rewrites = diff.changes.filter((c) => c.class === "rewrite").length;
|
|
7
|
+
const trailer = [
|
|
8
|
+
...(rewrites > 0
|
|
9
|
+
? ["", `Warning: ${rewrites} change(s) rewrite or read a table under ACCESS EXCLUSIVE, which blocks its reads and writes until done; the rule names the form that does not.`]
|
|
10
|
+
: []),
|
|
11
|
+
...(diff.refused.length > 0
|
|
12
|
+
? [
|
|
13
|
+
"",
|
|
14
|
+
`Refused: ${diff.refused.length} change(s) keep no old reader working when made in place. Each runs as expand and contract ` +
|
|
15
|
+
"(add the new, write both, backfill, move readers, drop the old), as a migration Op, not as one statement.",
|
|
16
|
+
...renderMigrationOpSuggestions(diff.migrationOps ?? []),
|
|
17
|
+
]
|
|
18
|
+
: []),
|
|
19
|
+
];
|
|
20
|
+
return renderChangeSet(diff, { ...(opts.title ? { title: opts.title } : {}), rules: PG_CLASSIFIER_RULES, classes: PG_CHANGE_CLASSES, trailer });
|
|
21
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Postgres change classifier's rules (#3279, #3275 question 3): every way
|
|
3
|
+
* a declared Postgres object can change, the class of change it is on the
|
|
4
|
+
* server, and the restriction behind the class, cited from the Postgres 18
|
|
5
|
+
* documentation (the pinned major), with the differences across the
|
|
6
|
+
* supported majors 14 to 18 stated where there are any.
|
|
7
|
+
*
|
|
8
|
+
* The classes are by the lock a change takes and whether it reads or
|
|
9
|
+
* rewrites the table ("Explicit Locking", `ALTER TABLE`'s Notes):
|
|
10
|
+
*
|
|
11
|
+
* - `metadata`: a catalog change under a brief lock; no row is read or
|
|
12
|
+
* written. Most `ALTER TABLE` forms take `ACCESS EXCLUSIVE` even so, which
|
|
13
|
+
* queues behind every running query, so the applier (#3280) sets a
|
|
14
|
+
* `lock_timeout`.
|
|
15
|
+
* - `validate`: every row is read to check a constraint, under a lock weaker
|
|
16
|
+
* than `ACCESS EXCLUSIVE` (`SHARE UPDATE EXCLUSIVE` for `VALIDATE
|
|
17
|
+
* CONSTRAINT`, so reads and writes go on).
|
|
18
|
+
* - `concurrently`: an index build, which blocks writes for its whole run
|
|
19
|
+
* unless it is `CONCURRENTLY`, and `CONCURRENTLY` cannot run inside a
|
|
20
|
+
* transaction block.
|
|
21
|
+
* - `rewrite`: the table is rewritten, or read in full, under `ACCESS
|
|
22
|
+
* EXCLUSIVE`, blocking reads and writes for the duration.
|
|
23
|
+
* - `expand`: no in-place change keeps old readers working (a rename, a type
|
|
24
|
+
* change with no cast in place, a change of partitioning): it is made as
|
|
25
|
+
* expand and contract, by the migration Op (#3281). A plan refuses it.
|
|
26
|
+
*
|
|
27
|
+
* Creating and dropping whole objects are classed `create` and `drop`.
|
|
28
|
+
*/
|
|
29
|
+
import { classifierRule } from "../../core/classifier.js";
|
|
30
|
+
export const PG_CHANGE_CLASSES = {
|
|
31
|
+
order: ["create", "metadata", "validate", "concurrently", "rewrite", "expand", "drop"],
|
|
32
|
+
label: {
|
|
33
|
+
create: "create",
|
|
34
|
+
metadata: "metadata only",
|
|
35
|
+
validate: "validates under a weaker lock",
|
|
36
|
+
concurrently: "needs CONCURRENTLY",
|
|
37
|
+
rewrite: "ACCESS EXCLUSIVE rewrite or scan",
|
|
38
|
+
expand: "EXPAND AND CONTRACT",
|
|
39
|
+
drop: "drop",
|
|
40
|
+
},
|
|
41
|
+
disruption: { create: "in-place", metadata: "in-place", validate: "rolling", concurrently: "rolling", rewrite: "rolling", expand: "replace", drop: "destroy" },
|
|
42
|
+
};
|
|
43
|
+
const DOCS = "https://www.postgresql.org/docs/18";
|
|
44
|
+
const ALTER_TABLE = `${DOCS}/sql-altertable.html`;
|
|
45
|
+
const LOCKING = `${DOCS}/explicit-locking.html`;
|
|
46
|
+
const CREATE_INDEX = `${DOCS}/sql-createindex.html#SQL-CREATEINDEX-CONCURRENTLY`;
|
|
47
|
+
const rule = (id, cls, title, restriction, cite) => classifierRule(id, cls, title, restriction, cite);
|
|
48
|
+
export const PG_CLASSIFIER_RULES = {
|
|
49
|
+
SQLPG200: rule("SQLPG200", "create", "Create an object", "A new object is created; nothing existing changes. A table created in the same plan as its indexes needs no CONCURRENTLY.", `${DOCS}/sql-createtable.html`),
|
|
50
|
+
SQLPG201: rule("SQLPG201", "metadata", "Add a column", "ADD COLUMN with no default, or with a non-volatile default, changes only the catalog under ACCESS EXCLUSIVE: the default is stored once and read for existing rows (since 11, so in every supported major). A NOT NULL column needs that default.", ALTER_TABLE),
|
|
51
|
+
SQLPG202: rule("SQLPG202", "rewrite", "Add a column whose value has to be computed for every row", "ADD COLUMN with a volatile default (clock_timestamp(), random(), gen_random_uuid(), nextval()), a STORED generated column or an identity column rewrites the whole table under ACCESS EXCLUSIVE. A VIRTUAL generated column (18) does not.", ALTER_TABLE),
|
|
52
|
+
SQLPG203: rule("SQLPG203", "expand", "Add a NOT NULL column with no default", "ADD COLUMN ... NOT NULL with no default fails on a table that has rows. Add it nullable, backfill it, then set NOT NULL.", ALTER_TABLE),
|
|
53
|
+
SQLPG204: rule("SQLPG204", "metadata", "Drop a column", "DROP COLUMN only hides the column in the catalog under ACCESS EXCLUSIVE; its space is reclaimed as rows are rewritten. The data is gone.", ALTER_TABLE),
|
|
54
|
+
SQLPG205: rule("SQLPG205", "expand", "Rename a column", "RENAME COLUMN is a catalog change, but every reader still using the old name fails the moment it runs: add the new column, write both, move readers, drop the old.", ALTER_TABLE),
|
|
55
|
+
SQLPG206: rule("SQLPG206", "metadata", "Change a column's type without a rewrite", "ALTER COLUMN TYPE to a binary-coercible type (varchar(n) to a longer varchar or to text, numeric(p,s) to a wider precision or to unconstrained numeric, cidr to inet) needs no rewrite; indexes on the column may still be rebuilt. ACCESS EXCLUSIVE, briefly.", ALTER_TABLE),
|
|
56
|
+
SQLPG207: rule("SQLPG207", "rewrite", "Change a column's type with a rewrite", "ALTER COLUMN TYPE that is not binary-coercible (integer to bigint, a shorter varchar, real to double precision) rewrites the table and rebuilds its indexes under ACCESS EXCLUSIVE.", ALTER_TABLE),
|
|
57
|
+
SQLPG208: rule("SQLPG208", "expand", "Change a column's type across kinds", "A type change between kinds (text to integer, a type to an enum) needs a USING expression and breaks readers that expect the old type: add the new column, backfill, move readers, drop the old.", ALTER_TABLE),
|
|
58
|
+
SQLPG209: rule("SQLPG209", "metadata", "Change a column's default", "SET DEFAULT and DROP DEFAULT change only the catalog; existing rows keep their values.", ALTER_TABLE),
|
|
59
|
+
SQLPG210: rule("SQLPG210", "validate", "Set NOT NULL", "SET NOT NULL alone reads the whole table under ACCESS EXCLUSIVE, unless a valid CHECK (column IS NOT NULL) constraint already proves it (since 12). So it is made as four statements: that check added NOT VALID, validated under SHARE UPDATE EXCLUSIVE while writes go on, SET NOT NULL, which the check proves without a scan, and the check dropped. Rows where the column is NULL fail the validation; the first statement's pre-check counts them.", ALTER_TABLE),
|
|
60
|
+
SQLPG211: rule("SQLPG211", "metadata", "Drop NOT NULL", "DROP NOT NULL changes only the catalog.", ALTER_TABLE),
|
|
61
|
+
SQLPG212: rule("SQLPG212", "rewrite", "Change a generated column's expression", "ALTER COLUMN SET EXPRESSION (17 and later) rewrites a STORED generated column under ACCESS EXCLUSIVE; on 14 to 16 the column has to be dropped and added. A VIRTUAL column (18) changes only the catalog.", ALTER_TABLE),
|
|
62
|
+
SQLPG213: rule("SQLPG213", "metadata", "Change a column's identity", "ADD, SET and DROP IDENTITY change the column's sequence and the catalog, not the rows.", ALTER_TABLE),
|
|
63
|
+
SQLPG214: rule("SQLPG214", "rewrite", "Change a column's collation", "A collation change is ALTER COLUMN TYPE with COLLATE: the column's indexes are rebuilt under ACCESS EXCLUSIVE.", ALTER_TABLE),
|
|
64
|
+
SQLPG215: rule("SQLPG215", "metadata", "Change a column's storage or compression", "SET STORAGE and SET COMPRESSION apply to values written afterwards; existing values are not rewritten.", ALTER_TABLE),
|
|
65
|
+
SQLPG216: rule("SQLPG216", "metadata", "Change a comment", "COMMENT ON changes only the catalog.", `${DOCS}/sql-comment.html`),
|
|
66
|
+
SQLPG217: rule("SQLPG217", "metadata", "Add a constraint NOT VALID", "ADD CONSTRAINT ... NOT VALID checks new rows only and reads none of the existing ones; a foreign key takes SHARE ROW EXCLUSIVE on both tables, briefly. Validate it as a separate step.", ALTER_TABLE),
|
|
67
|
+
SQLPG218: rule("SQLPG218", "validate", "Add a CHECK constraint", "ADD CONSTRAINT ... CHECK alone reads every row under ACCESS EXCLUSIVE. On a table that exists it is made as two statements: the check added NOT VALID (SQLPG217), which reads no rows, then VALIDATE CONSTRAINT (SQLPG220) in a transaction of its own, which reads them under SHARE UPDATE EXCLUSIVE while writes go on. Rows the check refuses fail the validation; the first statement's pre-check counts them.", ALTER_TABLE),
|
|
68
|
+
SQLPG219: rule("SQLPG219", "validate", "Add a foreign key", "ADD FOREIGN KEY alone reads every row under SHARE ROW EXCLUSIVE on both tables, which blocks writes to them for the scan. On a table that exists it is made as two statements: the key added NOT VALID (SQLPG217), which takes that lock briefly and reads no rows, then VALIDATE CONSTRAINT (SQLPG220) in a transaction of its own, which reads them under SHARE UPDATE EXCLUSIVE while writes go on. Orphan rows fail the validation; the first statement's pre-check counts them.", ALTER_TABLE),
|
|
69
|
+
SQLPG220: rule("SQLPG220", "validate", "Validate a constraint", "VALIDATE CONSTRAINT reads every row under SHARE UPDATE EXCLUSIVE, so reads and writes go on (ROW SHARE on a foreign key's referenced table).", LOCKING),
|
|
70
|
+
SQLPG221: rule("SQLPG221", "concurrently", "Add a primary key or unique constraint", "ADD PRIMARY KEY or UNIQUE builds its index under ACCESS EXCLUSIVE. Build the unique index CONCURRENTLY first, then ADD CONSTRAINT ... USING INDEX, which changes only the catalog.", ALTER_TABLE),
|
|
71
|
+
SQLPG222: rule("SQLPG222", "rewrite", "Add an exclusion constraint", "ADD CONSTRAINT ... EXCLUDE builds its index under ACCESS EXCLUSIVE; there is no concurrent form.", ALTER_TABLE),
|
|
72
|
+
SQLPG223: rule("SQLPG223", "metadata", "Drop a constraint", "DROP CONSTRAINT changes the catalog under ACCESS EXCLUSIVE, briefly; a primary key's or unique constraint's index goes with it.", ALTER_TABLE),
|
|
73
|
+
SQLPG224: rule("SQLPG224", "metadata", "Change storage parameters", "SET and RESET of storage parameters change the catalog; fillfactor and the autovacuum parameters take only SHARE UPDATE EXCLUSIVE. Rows are not rewritten; a new fillfactor applies to pages written afterwards.", ALTER_TABLE),
|
|
74
|
+
SQLPG225: rule("SQLPG225", "rewrite", "Make a table LOGGED or UNLOGGED", "SET LOGGED and SET UNLOGGED rewrite the table under ACCESS EXCLUSIVE.", ALTER_TABLE),
|
|
75
|
+
SQLPG226: rule("SQLPG226", "rewrite", "Change a table's access method or tablespace", "SET ACCESS METHOD (15 and later) and SET TABLESPACE rewrite the table under ACCESS EXCLUSIVE.", ALTER_TABLE),
|
|
76
|
+
SQLPG227: rule("SQLPG227", "expand", "Change partitioning or inheritance", "A table's partition key, its parent or bound, INHERITS and OF type cannot be altered in place: a new table is filled and swapped in. DETACH PARTITION CONCURRENTLY (14 and later) and ATTACH PARTITION, which takes SHARE UPDATE EXCLUSIVE on the parent, are the steps.", `${DOCS}/ddl-partitioning.html`),
|
|
77
|
+
SQLPG228: rule("SQLPG228", "expand", "Rename an object or move it to another schema", "RENAME and SET SCHEMA are catalog changes, but every reader still using the old name fails at once: create the new name (a view over the old object works), move readers, drop the old.", ALTER_TABLE),
|
|
78
|
+
SQLPG229: rule("SQLPG229", "metadata", "Rename an index", "ALTER INDEX ... RENAME changes only the catalog; nothing reads an index by name.", `${DOCS}/sql-alterindex.html`),
|
|
79
|
+
SQLPG240: rule("SQLPG240", "concurrently", "Create an index CONCURRENTLY", "CREATE INDEX CONCURRENTLY builds the index without blocking writes (SHARE UPDATE EXCLUSIVE), in two scans, and cannot run inside a transaction block; a failed build leaves an INVALID index to drop.", CREATE_INDEX),
|
|
80
|
+
SQLPG241: rule("SQLPG241", "concurrently", "Create an index without CONCURRENTLY", "CREATE INDEX on an existing table takes SHARE, which blocks every write to the table until the build ends. Declare it CONCURRENTLY.", CREATE_INDEX),
|
|
81
|
+
SQLPG242: rule("SQLPG242", "concurrently", "Drop an index", "DROP INDEX takes ACCESS EXCLUSIVE on the table; DROP INDEX CONCURRENTLY waits for running queries instead, and cannot run inside a transaction block.", `${DOCS}/sql-dropindex.html`),
|
|
82
|
+
SQLPG243: rule("SQLPG243", "concurrently", "Change an index", "An index's definition cannot be altered: it is dropped and created, both CONCURRENTLY to keep writes going (SQLPG240, SQLPG242).", CREATE_INDEX),
|
|
83
|
+
SQLPG250: rule("SQLPG250", "metadata", "Change a view's query, keeping its columns", "CREATE OR REPLACE VIEW replaces the query under ACCESS EXCLUSIVE on the view when the new query keeps the old columns, by name, in order, and only appends new ones.", `${DOCS}/sql-createview.html`),
|
|
84
|
+
SQLPG251: rule("SQLPG251", "expand", "Change a view's columns", "A view whose columns are removed, renamed or reordered cannot be replaced: it is dropped and created, and every view and reader on top of it with it.", `${DOCS}/sql-createview.html`),
|
|
85
|
+
SQLPG252: rule("SQLPG252", "rewrite", "Change a materialized view's query", "A materialized view's query cannot be altered: it is dropped and created, and its rows are computed again, with every view on top of it dropped too.", `${DOCS}/sql-creatematerializedview.html`),
|
|
86
|
+
SQLPG253: rule("SQLPG253", "metadata", "Change a view's options", "ALTER VIEW ... SET (check_option, security_barrier, security_invoker) changes only the catalog.", `${DOCS}/sql-alterview.html`),
|
|
87
|
+
SQLPG260: rule("SQLPG260", "metadata", "Add an enum label", "ALTER TYPE ... ADD VALUE changes only the catalog; since 12 it can run in a transaction block, but the new label cannot be used in that transaction.", `${DOCS}/sql-altertype.html`),
|
|
88
|
+
SQLPG261: rule("SQLPG261", "expand", "Remove or reorder enum labels", "An enum label cannot be removed and labels cannot be reordered in place: a new type is created, columns are moved to it, and the old one is dropped.", `${DOCS}/sql-altertype.html`),
|
|
89
|
+
SQLPG262: rule("SQLPG262", "validate", "Add a domain constraint", "ALTER DOMAIN ADD CONSTRAINT (and SET NOT NULL) checks every column of the domain's type in every table; ADD CONSTRAINT ... NOT VALID skips existing rows and VALIDATE CONSTRAINT checks them later.", `${DOCS}/sql-alterdomain.html`),
|
|
90
|
+
SQLPG263: rule("SQLPG263", "metadata", "Change a domain's default or drop its constraint", "ALTER DOMAIN SET DEFAULT, DROP NOT NULL and DROP CONSTRAINT change only the catalog.", `${DOCS}/sql-alterdomain.html`),
|
|
91
|
+
SQLPG264: rule("SQLPG264", "expand", "Change a domain's data type", "A domain's underlying type cannot be altered: a new domain is created and columns are moved to it.", `${DOCS}/sql-alterdomain.html`),
|
|
92
|
+
SQLPG265: rule("SQLPG265", "metadata", "Change a sequence's options", "ALTER SEQUENCE changes the sequence's own row; tables are not touched. A sequence's AS type bounds its values.", `${DOCS}/sql-altersequence.html`),
|
|
93
|
+
SQLPG266: rule("SQLPG266", "metadata", "Change an extension's version or schema", "ALTER EXTENSION UPDATE runs the extension's update script, which may itself alter objects; SET SCHEMA moves its objects.", `${DOCS}/sql-alterextension.html`),
|
|
94
|
+
SQLPG267: rule("SQLPG267", "metadata", "Change a schema's owner", "ALTER SCHEMA ... OWNER TO changes only the catalog.", `${DOCS}/sql-alterschema.html`),
|
|
95
|
+
SQLPG268: rule("SQLPG268", "expand", "Change an object's kind", "A view and a materialized view, or a table and either, are different kinds of object: the old one is dropped and the new one created.", `${DOCS}/sql-createview.html`),
|
|
96
|
+
SQLPG280: rule("SQLPG280", "metadata", "Replace a function's or procedure's definition", "CREATE OR REPLACE FUNCTION (or PROCEDURE) replaces the body and attributes in the catalog; it locks no table, and a call already running finishes with the old definition. The parameter types, the result and the input parameters' names have to stay the same.", `${DOCS}/sql-createfunction.html`),
|
|
97
|
+
SQLPG281: rule("SQLPG281", "metadata", "Drop and create a function or procedure", "CREATE OR REPLACE refuses a change of the result type, the output parameters, an input parameter's name, a removed parameter default or the routine's kind: the routine is dropped and created in one transaction. Nothing on the server depends on it (a view, a trigger, a column default would make the DROP fail).", `${DOCS}/sql-createfunction.html`),
|
|
98
|
+
SQLPG282: rule("SQLPG282", "expand", "Change a function's result or parameters while other objects depend on it", "DROP FUNCTION refuses while a view, a trigger, a column default or another routine depends on the function, and CREATE OR REPLACE cannot make this change. Create the new definition under a new name (or signature), move what depends on it, then drop the old.", `${DOCS}/sql-dropfunction.html`),
|
|
99
|
+
SQLPG283: rule("SQLPG283", "metadata", "Create a trigger on an existing table", "CREATE TRIGGER takes SHARE ROW EXCLUSIVE on its table: writes and other schema changes to the table wait until the transaction commits, but no row is read. The applier's lock_timeout bounds the wait behind long-running transactions.", `${DOCS}/sql-createtrigger.html`),
|
|
100
|
+
SQLPG284: rule("SQLPG284", "metadata", "Change a trigger", "CREATE OR REPLACE TRIGGER (14 and later) replaces the trigger under SHARE ROW EXCLUSIVE on its table. A constraint trigger has no OR REPLACE, and a trigger moved to another table is another trigger: each is dropped (ACCESS EXCLUSIVE, briefly) and created in one transaction.", `${DOCS}/sql-createtrigger.html`),
|
|
101
|
+
SQLPG285: rule("SQLPG285", "drop", "Drop a trigger", "DROP TRIGGER takes ACCESS EXCLUSIVE on its table, briefly; no row is read or lost, and the table's writes no longer fire it.", `${DOCS}/sql-droptrigger.html`),
|
|
102
|
+
SQLPG290: rule("SQLPG290", "metadata", "Create a policy on an existing table", "CREATE POLICY takes ACCESS EXCLUSIVE on its table, briefly; no row is read. Where row-level security is on, the rows each role sees change when the transaction commits.", `${DOCS}/sql-createpolicy.html`),
|
|
103
|
+
SQLPG291: rule("SQLPG291", "metadata", "Change a policy", "ALTER POLICY changes its roles, USING and WITH CHECK under ACCESS EXCLUSIVE on the table, briefly. Its command, AS RESTRICTIVE and table cannot be altered, nor an expression taken away: the policy is dropped and created in one transaction, so no session sees the table without it.", `${DOCS}/sql-alterpolicy.html`),
|
|
104
|
+
SQLPG292: rule("SQLPG292", "drop", "Drop a policy", "DROP POLICY takes ACCESS EXCLUSIVE on its table, briefly; the rows it let a role see are hidden from that role at once, or, if it was the last policy, every non-owner sees none.", `${DOCS}/sql-droppolicy.html`),
|
|
105
|
+
SQLPG293: rule("SQLPG293", "metadata", "Turn row-level security on or off, or force it on the owner", "ALTER TABLE ... ENABLE, DISABLE, FORCE or NO FORCE ROW LEVEL SECURITY changes only the catalog, under ACCESS EXCLUSIVE, briefly. With it on and no policy that lets a role in, that role sees no rows; FORCE subjects the table's owner too.", `${DOCS}/ddl-rowsecurity.html`),
|
|
106
|
+
SQLPG294: rule("SQLPG294", "metadata", "Change a role's attributes", "ALTER ROLE changes the role in the cluster's catalog, for every database on the server, at once; sessions already open keep the attributes they started with until they reconnect.", `${DOCS}/sql-alterrole.html`),
|
|
107
|
+
SQLPG296: rule("SQLPG296", "metadata", "Grant a privilege", "GRANT adds to the object's privileges in the catalog; no row is read and the role can use it from its next statement.", `${DOCS}/sql-grant.html`),
|
|
108
|
+
SQLPG297: rule("SQLPG297", "metadata", "Revoke a privilege", "REVOKE removes from the object's privileges in the catalog. The role loses it at once: its next statement that needs it fails, so move every reader off it first.", `${DOCS}/sql-revoke.html`),
|
|
109
|
+
SQLPG298: rule("SQLPG298", "metadata", "Change default privileges", "ALTER DEFAULT PRIVILEGES changes what objects created afterwards are given; objects that exist keep their privileges.", `${DOCS}/sql-alterdefaultprivileges.html`),
|
|
110
|
+
SQLPG299: rule("SQLPG299", "expand", "Change a clause chant does not read", "chant's parser does not know this clause, so it cannot tell which lock the change takes or whether an in-place statement makes it; the plan refuses it rather than guess. Review it: make it by hand with the ALTER the server documents for the clause, or as expand and contract.", `${DOCS}/sql-altertable.html`),
|
|
111
|
+
SQLPG300: rule("SQLPG300", "metadata", "Drop and create a type, aggregate, collation, cast or event trigger", "A composite or range type's definition, a collation's, a cast's and an event trigger's have no ALTER, and an aggregate's argument types, state type and final function cannot be replaced: the object is dropped and created in one transaction. Nothing on the server depends on it (a column, a view, a function or a cast would make the DROP fail); the locks are on its catalog rows only.", `${DOCS}/sql-createtype.html`),
|
|
112
|
+
SQLPG301: rule("SQLPG301", "expand", "Change a type, aggregate, collation or cast that other objects depend on", "No ALTER makes the change, and DROP refuses while a column, a view, a function or another object depends on the object. Create the new definition under another name, move what depends on it, then drop the old.", `${DOCS}/sql-droptype.html`),
|
|
113
|
+
SQLPG302: rule("SQLPG302", "metadata", "Replace an aggregate's definition", "CREATE OR REPLACE AGGREGATE (14 and later) replaces the aggregate's functions and options in the catalog; no table is locked. Its argument types, its result and its kind (plain, ordered-set, hypothetical-set) have to stay the same, so a change of the state type or the final function is dropped and created instead (SQLPG300).", `${DOCS}/sql-createaggregate.html`),
|
|
114
|
+
SQLPG270: rule("SQLPG270", "drop", "Drop an object", "The object and, for a table, a materialized view or a sequence, its data are gone.", `${DOCS}/sql-droptable.html`),
|
|
115
|
+
};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two sides a Postgres diff compares: a build's output (the
|
|
3
|
+
* declarations), or what a live server's catalog prints. Both are parsed with
|
|
4
|
+
* the tag each object was declared with and brought to canonical form
|
|
5
|
+
* (`./normalize.ts`), so the diff compares like with like.
|
|
6
|
+
*/
|
|
7
|
+
import { readFileSync } from "node:fs";
|
|
8
|
+
import { previouslyIn } from "../../core/normalize.js";
|
|
9
|
+
import { liveProps } from "./deep.js";
|
|
10
|
+
import { canonicalPgObject } from "./normalize.js";
|
|
11
|
+
import { identValue } from "../parser.js";
|
|
12
|
+
/** The rename hints in a statement: the object's (a comment before the CREATE) and each column's (a comment on its line). */
|
|
13
|
+
export function renameHints(ddl) {
|
|
14
|
+
const lines = ddl.split("\n");
|
|
15
|
+
const createAt = lines.findIndex((l) => /\bCREATE\b/i.test(l.replace(/--.*$/, "")));
|
|
16
|
+
const before = lines.slice(0, Math.max(createAt, 0)).map((l) => l.trim()).filter((l) => l.startsWith("--"));
|
|
17
|
+
const columns = {};
|
|
18
|
+
for (const line of lines.slice(createAt + 1)) {
|
|
19
|
+
const m = /^\s*("(?:[^"]|"")+"|[A-Za-z_][A-Za-z0-9_$]*)\b.*?(--.*)$/.exec(line);
|
|
20
|
+
if (!m)
|
|
21
|
+
continue;
|
|
22
|
+
const prev = previouslyIn([m[2]]);
|
|
23
|
+
if (prev)
|
|
24
|
+
columns[identValue(m[1])] = identValue(prev);
|
|
25
|
+
}
|
|
26
|
+
const prev = previouslyIn(before);
|
|
27
|
+
return { ...(prev ? { previously: prev } : {}), columns };
|
|
28
|
+
}
|
|
29
|
+
/** One object, from its type and statements, in the diff's form. */
|
|
30
|
+
export function diffObject(type, ddl, defaultSchema) {
|
|
31
|
+
const props = liveProps({ type, statement: ddl });
|
|
32
|
+
const canonical = canonicalPgObject(type, props, defaultSchema);
|
|
33
|
+
const hints = renameHints(ddl);
|
|
34
|
+
const lineage = props.lineage;
|
|
35
|
+
const declaredColumns = props.columns;
|
|
36
|
+
return {
|
|
37
|
+
...canonical,
|
|
38
|
+
type,
|
|
39
|
+
...(hints.previously ? { previously: identValue(hints.previously) } : {}),
|
|
40
|
+
columnPreviously: hints.columns,
|
|
41
|
+
...(props.concurrently === true ? { concurrently: true } : {}),
|
|
42
|
+
...(canonical.kind === "view" || canonical.kind === "materializedView"
|
|
43
|
+
? { outputs: Array.isArray(declaredColumns) && declaredColumns.length > 0 ? declaredColumns : (lineage ?? []).map((e) => e.output) }
|
|
44
|
+
: {}),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/** The objects a Postgres `chant build` output holds, keyed by export name. */
|
|
48
|
+
export function pgSchemaFromBuildOutput(json, defaultSchema = "public") {
|
|
49
|
+
const doc = JSON.parse(json);
|
|
50
|
+
if (doc.dialect !== "postgres" || !Array.isArray(doc.objects))
|
|
51
|
+
throw new Error('not a Postgres build output: expected { dialect: "postgres", objects: [...] }');
|
|
52
|
+
return doc.objects.map((o) => ({ key: o.export, canonical: { ...diffObject(o.type, o.ddl, defaultSchema), exportName: o.export } }));
|
|
53
|
+
}
|
|
54
|
+
/** The major a Postgres build recorded (`postgresMajor`), or undefined for output that predates the field. */
|
|
55
|
+
export function pgBuildMajor(json) {
|
|
56
|
+
const m = JSON.parse(json).postgresMajor;
|
|
57
|
+
return typeof m === "number" ? m : undefined;
|
|
58
|
+
}
|
|
59
|
+
export function pgBuildFileMajor(path) {
|
|
60
|
+
return pgBuildMajor(readFileSync(path, "utf-8"));
|
|
61
|
+
}
|
|
62
|
+
export function pgSchemaFromBuildFile(path, defaultSchema = "public") {
|
|
63
|
+
return pgSchemaFromBuildOutput(readFileSync(path, "utf-8"), defaultSchema);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The namespace an object's name is unique in: relations (tables, views,
|
|
67
|
+
* sequences, indexes) share one per schema, types and domains another,
|
|
68
|
+
* functions, procedures and aggregates a third (told apart by their
|
|
69
|
+
* parameter types), collations a fourth; a cast is named by its two types,
|
|
70
|
+
* a trigger's or a policy's name is unique on its table, and a role's in the
|
|
71
|
+
* cluster.
|
|
72
|
+
*/
|
|
73
|
+
export function pgNamespace(kind) {
|
|
74
|
+
if (kind === "schema" || kind === "extension" || kind === "trigger" || kind === "policy" || kind === "role")
|
|
75
|
+
return kind;
|
|
76
|
+
if (kind === "collation" || kind === "cast" || kind === "eventTrigger")
|
|
77
|
+
return kind;
|
|
78
|
+
if (kind === "grant" || kind === "defaultPrivileges")
|
|
79
|
+
return "grant";
|
|
80
|
+
if (kind === "enum" || kind === "domain" || kind === "composite" || kind === "range")
|
|
81
|
+
return "type";
|
|
82
|
+
if (kind === "function" || kind === "procedure" || kind === "aggregate")
|
|
83
|
+
return "routine";
|
|
84
|
+
return "relation";
|
|
85
|
+
}
|
|
86
|
+
/** The identity an object has on a server: its kind's namespace, its qualified name, and its signature where it has one. */
|
|
87
|
+
export function qualifiedKey(o) {
|
|
88
|
+
return `${pgNamespace(o.kind)} ${o.schema ? `${o.schema}.` : ""}${o.name}${o.signature ?? ""}`;
|
|
89
|
+
}
|
|
90
|
+
/** The declared objects re-keyed by qualified name, the only identity a server has. */
|
|
91
|
+
export function keyedByQualifiedName(objects) {
|
|
92
|
+
return objects.map((o) => ({ key: qualifiedKey(o.canonical), canonical: { ...o.canonical, exportName: o.canonical.exportName ?? o.key } }));
|
|
93
|
+
}
|
|
94
|
+
/** What a server holds, in the diff's form, keyed by qualified name. */
|
|
95
|
+
export function pgSchemaFromLive(live, defaultSchema) {
|
|
96
|
+
// A routine with a SQL-standard body is not one a declaration can hold (`../parser.ts`): never chant's, never compared.
|
|
97
|
+
return live.filter((o) => !o.unsupported).map((o) => {
|
|
98
|
+
const canonical = {
|
|
99
|
+
...diffObject(o.type, o.statement, defaultSchema),
|
|
100
|
+
...(o.foreign ? { foreign: o.foreign } : {}),
|
|
101
|
+
...(o.dependents && o.dependents.length > 0 ? { dependents: o.dependents } : {}),
|
|
102
|
+
};
|
|
103
|
+
return { key: qualifiedKey(canonical), canonical };
|
|
104
|
+
});
|
|
105
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The backstop for normalization against a live server: what the rules in
|
|
3
|
+
* `./normalize.ts` cannot see (a view's `SELECT *`, which the server expands;
|
|
4
|
+
* the parentheses it adds inside a check) is asked of the server itself.
|
|
5
|
+
*
|
|
6
|
+
* For each declared table or view whose expressions still differ from the
|
|
7
|
+
* server's, the declaration's expressions are created as a temporary object
|
|
8
|
+
* (`CREATE TEMP VIEW`, or a `CREATE TEMP TABLE` holding the columns' types,
|
|
9
|
+
* defaults and generated expressions and the checks) inside a transaction
|
|
10
|
+
* that is always rolled back, with a short `lock_timeout`, and read back with
|
|
11
|
+
* the same printers the catalog read uses. A trigger's `WHEN` and a policy's
|
|
12
|
+
* `USING` and `WITH CHECK`, which the server prints with the casts it
|
|
13
|
+
* resolved, are read back from a trigger or policy on a temporary copy of the
|
|
14
|
+
* table (`CREATE TEMP TABLE ... (LIKE table)`). Nothing is left behind: Postgres
|
|
15
|
+
* DDL is transactional and a temporary object lives in the session's own
|
|
16
|
+
* schema. A server that refuses (no TEMP privilege, a lock not granted in
|
|
17
|
+
* time, an expression it rejects) leaves the rules' answer standing.
|
|
18
|
+
*/
|
|
19
|
+
import { quoteIdent } from "../keywords.js";
|
|
20
|
+
import { canonicalExpr } from "./normalize.js";
|
|
21
|
+
async function rolledBack(client, defaultSchema, body) {
|
|
22
|
+
await client.query("BEGIN");
|
|
23
|
+
try {
|
|
24
|
+
await client.query("SET LOCAL lock_timeout = '1s'");
|
|
25
|
+
await client.query("SET LOCAL statement_timeout = '10s'");
|
|
26
|
+
await client.query(`SELECT pg_catalog.set_config('search_path', $1, true)`, [quoteIdent(defaultSchema)]);
|
|
27
|
+
return await body();
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
await client.query("ROLLBACK").catch(() => undefined);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const emptyPath = (client) => client.query(`SELECT pg_catalog.set_config('search_path', '', true)`);
|
|
37
|
+
/** A view's query as the server prints it, canonical, and its output columns. */
|
|
38
|
+
async function viewQuery(client, query, defaultSchema) {
|
|
39
|
+
return rolledBack(client, defaultSchema, async () => {
|
|
40
|
+
await client.query(`CREATE TEMP VIEW chant_normalize AS ${query}`);
|
|
41
|
+
await emptyPath(client);
|
|
42
|
+
const [row] = await client.query(`SELECT pg_catalog.pg_get_viewdef('pg_temp.chant_normalize'::pg_catalog.regclass, true) AS def`);
|
|
43
|
+
const cols = await client.query(`SELECT a.attname AS name FROM pg_catalog.pg_attribute a WHERE a.attrelid = 'pg_temp.chant_normalize'::pg_catalog.regclass AND a.attnum > 0 ORDER BY a.attnum`);
|
|
44
|
+
return { query: canonicalExpr(String(row?.def ?? "").trim().replace(/;$/, "")), outputs: cols.map((c) => String(c.name)) };
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/** A table's defaults, generated expressions and checks as the server prints them, canonical. */
|
|
48
|
+
async function tableExpressions(client, o, defaultSchema) {
|
|
49
|
+
const columns = o.columns.filter((c) => c.type !== undefined);
|
|
50
|
+
if (columns.length === 0)
|
|
51
|
+
return undefined;
|
|
52
|
+
const defs = columns.map((c) => {
|
|
53
|
+
const type = /^(small|big)?serial$/.test(c.type) ? c.type.replace("serial", "int").replace("smallint", "smallint").replace(/^int$/, "integer") : c.type;
|
|
54
|
+
const parts = [quoteIdent(c.name), type];
|
|
55
|
+
if (c.collate)
|
|
56
|
+
parts.push(`COLLATE ${c.collate}`);
|
|
57
|
+
if (c.generated) {
|
|
58
|
+
const [kind, ...expr] = c.generated.split(" ");
|
|
59
|
+
parts.push(`GENERATED ALWAYS AS (${expr.join(" ")}) ${kind === "virtual" ? "VIRTUAL" : "STORED"}`);
|
|
60
|
+
}
|
|
61
|
+
else if (c.default !== undefined)
|
|
62
|
+
parts.push(`DEFAULT ${c.default}`);
|
|
63
|
+
return parts.join(" ");
|
|
64
|
+
});
|
|
65
|
+
const checks = o.constraints.filter((c) => c.kind === "CHECK").map((c, i) => `CONSTRAINT chant_check_${String(i).padStart(4, "0")} ${c.body.replace(/^check /, "CHECK (").replace(/( no inherit| not enforced)*$/, ")$&")}`);
|
|
66
|
+
return rolledBack(client, defaultSchema, async () => {
|
|
67
|
+
await client.query(`CREATE TEMP TABLE chant_normalize (${[...defs, ...checks].join(", ")})`);
|
|
68
|
+
await emptyPath(client);
|
|
69
|
+
const cols = await client.query(`SELECT a.attname AS name, a.attgenerated AS generated, pg_catalog.pg_get_expr(d.adbin, d.adrelid, true) AS expr
|
|
70
|
+
FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_attrdef d ON d.adrelid = a.attrelid AND d.adnum = a.attnum
|
|
71
|
+
WHERE a.attrelid = 'pg_temp.chant_normalize'::pg_catalog.regclass AND a.attnum > 0 AND NOT a.attisdropped`);
|
|
72
|
+
const cons = await client.query(`SELECT c.conname AS name, pg_catalog.pg_get_constraintdef(c.oid, true) AS def FROM pg_catalog.pg_constraint c
|
|
73
|
+
WHERE c.conrelid = 'pg_temp.chant_normalize'::pg_catalog.regclass AND c.contype = 'c' ORDER BY c.conname`);
|
|
74
|
+
const defaults = new Map();
|
|
75
|
+
const generated = new Map();
|
|
76
|
+
for (const r of cols) {
|
|
77
|
+
if (r.expr === null || r.expr === undefined)
|
|
78
|
+
continue;
|
|
79
|
+
if (r.generated === "s" || r.generated === "v")
|
|
80
|
+
generated.set(String(r.name), `${r.generated === "s" ? "stored" : "virtual"} ${canonicalExpr(String(r.expr))}`);
|
|
81
|
+
else
|
|
82
|
+
defaults.set(String(r.name), canonicalExpr(String(r.expr)));
|
|
83
|
+
}
|
|
84
|
+
const printed = cons.map((r) => String(r.def).replace(/^CHECK /, "")).map((d) => canonicalExpr(d));
|
|
85
|
+
return { defaults, generated, checks: printed };
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/** A trigger's WHEN condition as the server prints it, canonical. */
|
|
89
|
+
async function triggerWhen(client, o, defaultSchema) {
|
|
90
|
+
const when = o.fields.when;
|
|
91
|
+
// An INSTEAD OF trigger, on a view, takes no WHEN.
|
|
92
|
+
if (typeof when !== "string" || o.fields.timing === "instead of")
|
|
93
|
+
return undefined;
|
|
94
|
+
const events = (o.fields.events ?? []).map((e) => e.replace(/^(\w+)( of )?/, (_m, ev, of) => `${ev.toUpperCase()}${of ? " OF " : ""}`));
|
|
95
|
+
return rolledBack(client, defaultSchema, async () => {
|
|
96
|
+
await client.query(`CREATE TEMP TABLE chant_normalize (LIKE ${String(o.fields.table)})`);
|
|
97
|
+
await client.query(`CREATE FUNCTION pg_temp.chant_normalize() RETURNS trigger LANGUAGE plpgsql AS 'BEGIN RETURN NULL; END'`);
|
|
98
|
+
await client.query(`CREATE TRIGGER chant_normalize ${String(o.fields.timing).toUpperCase()} ${events.join(" OR ")} ON pg_temp.chant_normalize ` +
|
|
99
|
+
`FOR EACH ${o.fields.forEach === "row" ? "ROW" : "STATEMENT"} WHEN (${when}) EXECUTE FUNCTION pg_temp.chant_normalize()`);
|
|
100
|
+
await emptyPath(client);
|
|
101
|
+
const [row] = await client.query(`SELECT pg_catalog.pg_get_triggerdef(t.oid, true) AS def FROM pg_catalog.pg_trigger t WHERE t.tgrelid = 'pg_temp.chant_normalize'::pg_catalog.regclass`);
|
|
102
|
+
const m = / WHEN \((.*)\) EXECUTE (?:FUNCTION|PROCEDURE) /s.exec(String(row?.def ?? ""));
|
|
103
|
+
return m ? canonicalExpr(m[1]) : undefined;
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
/** A policy's USING and WITH CHECK as the server prints them, canonical, from a policy on a temporary copy of its table. */
|
|
107
|
+
async function policyExpressions(client, o, defaultSchema) {
|
|
108
|
+
const using = o.fields.using;
|
|
109
|
+
const check = o.fields.check;
|
|
110
|
+
if (using === undefined && check === undefined)
|
|
111
|
+
return undefined;
|
|
112
|
+
return rolledBack(client, defaultSchema, async () => {
|
|
113
|
+
await client.query(`CREATE TEMP TABLE chant_normalize (LIKE ${String(o.fields.table)})`);
|
|
114
|
+
await client.query([`CREATE POLICY chant_normalize ON pg_temp.chant_normalize FOR ${String(o.fields.command ?? "all").toUpperCase()}`, using !== undefined ? `USING (${using})` : "", check !== undefined ? `WITH CHECK (${check})` : ""]
|
|
115
|
+
.filter(Boolean)
|
|
116
|
+
.join(" "));
|
|
117
|
+
await emptyPath(client);
|
|
118
|
+
const [row] = await client.query(`SELECT pg_catalog.pg_get_expr(p.polqual, p.polrelid, true) AS qual, pg_catalog.pg_get_expr(p.polwithcheck, p.polrelid, true) AS withcheck
|
|
119
|
+
FROM pg_catalog.pg_policy p WHERE p.polrelid = 'pg_temp.chant_normalize'::pg_catalog.regclass`);
|
|
120
|
+
return { ...(row?.qual ? { using: canonicalExpr(String(row.qual)) } : {}), ...(row?.withcheck ? { check: canonicalExpr(String(row.withcheck)) } : {}) };
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* The declared object with its expressions in the server's own printing,
|
|
125
|
+
* when the server answers; the object unchanged otherwise. Only the
|
|
126
|
+
* expressions are touched: everything else stays the rules'.
|
|
127
|
+
*/
|
|
128
|
+
export async function serverNormalized(client, o, defaultSchema) {
|
|
129
|
+
if ((o.kind === "view" || o.kind === "materializedView") && typeof o.fields.query === "string") {
|
|
130
|
+
const q = await viewQuery(client, o.fields.query, defaultSchema);
|
|
131
|
+
// A declared column list names the outputs; otherwise the server's names do (`SELECT *` expanded).
|
|
132
|
+
return q === undefined ? o : { ...o, fields: { ...o.fields, query: q.query }, ...(o.fields.columns === undefined ? { outputs: q.outputs } : {}) };
|
|
133
|
+
}
|
|
134
|
+
if (o.kind === "policy") {
|
|
135
|
+
const e = await policyExpressions(client, o, defaultSchema);
|
|
136
|
+
return e === undefined ? o : { ...o, fields: { ...o.fields, ...e } };
|
|
137
|
+
}
|
|
138
|
+
if (o.kind === "trigger") {
|
|
139
|
+
const when = await triggerWhen(client, o, defaultSchema);
|
|
140
|
+
return when === undefined ? o : { ...o, fields: { ...o.fields, when } };
|
|
141
|
+
}
|
|
142
|
+
if (o.kind === "table") {
|
|
143
|
+
const e = await tableExpressions(client, o, defaultSchema);
|
|
144
|
+
if (!e)
|
|
145
|
+
return o;
|
|
146
|
+
let i = 0;
|
|
147
|
+
return {
|
|
148
|
+
...o,
|
|
149
|
+
columns: o.columns.map((c) => ({
|
|
150
|
+
...c,
|
|
151
|
+
...(c.default !== undefined && e.defaults.has(c.name) && !/^(small|big)?serial$/.test(c.type ?? "") ? { default: e.defaults.get(c.name) } : {}),
|
|
152
|
+
...(c.generated !== undefined && e.generated.has(c.name) ? { generated: e.generated.get(c.name) } : {}),
|
|
153
|
+
})),
|
|
154
|
+
constraints: o.constraints.map((c) => {
|
|
155
|
+
if (c.kind !== "CHECK")
|
|
156
|
+
return c;
|
|
157
|
+
const printed = e.checks[i++];
|
|
158
|
+
if (printed === undefined)
|
|
159
|
+
return c;
|
|
160
|
+
const tail = /( no inherit| not enforced)*$/.exec(c.body)?.[0] ?? "";
|
|
161
|
+
return { ...c, body: `check ${printed}${tail}` };
|
|
162
|
+
}),
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
return o;
|
|
166
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { rds } from "./rds.js";
|
|
2
|
+
import { words } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Amazon Aurora PostgreSQL. Roles and refused statements are RDS's (Aurora
|
|
5
|
+
* uses the same master-user model). The extension list is the table
|
|
6
|
+
* "Extensions supported for Aurora PostgreSQL 18" on the Aurora release-notes
|
|
7
|
+
* page, read 2026-10-03; package names were mapped to extension names as in
|
|
8
|
+
* rds.ts (h3-pg is `h3`, pgvector is `vector`, pg_roaringbitmap is
|
|
9
|
+
* `roaringbitmap`). Aurora adds apg_plan_mgmt, aurora_stat_utils, aws_ml,
|
|
10
|
+
* pg_ad_mapping and pg_columnmask.
|
|
11
|
+
*/
|
|
12
|
+
export const aurora = {
|
|
13
|
+
...rds,
|
|
14
|
+
provider: "aurora",
|
|
15
|
+
label: "Amazon Aurora PostgreSQL",
|
|
16
|
+
sources: [
|
|
17
|
+
{
|
|
18
|
+
url: "https://docs.aws.amazon.com/AmazonRDS/latest/AuroraPostgreSQLReleaseNotes/AuroraPostgreSQL.Extensions.html",
|
|
19
|
+
readOn: "2026-10-03",
|
|
20
|
+
covers: "allowedExtensions (Aurora PostgreSQL 18 table)",
|
|
21
|
+
},
|
|
22
|
+
...rds.sources.filter((s) => s.covers !== "allowedExtensions (major 18 table)"),
|
|
23
|
+
],
|
|
24
|
+
allowedExtensions: words(`
|
|
25
|
+
address_standardizer address_standardizer_data_us amcheck apg_plan_mgmt aurora_stat_utils auto_explain autoinc
|
|
26
|
+
aws_commons aws_lambda aws_ml aws_s3 bloom bool_plperl btree_gin btree_gist citext cube dblink dict_int dict_xsyn
|
|
27
|
+
earthdistance fuzzystrmatch h3 h3_postgis hll hstore hstore_plperl hypopg insert_username intagg intarray ip4r isn
|
|
28
|
+
jsonb_plperl lo log_fdw ltree moddatetime mysql_fdw oracle_fdw orafce pg_ad_mapping pg_bigm pg_buffercache
|
|
29
|
+
pg_columnmask pg_cron pg_freespacemap pg_hint_plan pg_partman pg_prewarm pg_proctab pg_repack pg_similarity
|
|
30
|
+
pg_stat_statements pg_tle pg_trgm pg_visibility pgaudit pgcrypto pgdam pglogical pgrouting pgrowlocks pgstattuple
|
|
31
|
+
pgtap plcoffee plls plperl plpgsql plprofiler pltcl plv8 postgis postgis_raster postgis_tiger_geocoder
|
|
32
|
+
postgis_topology postgres_fdw prefix rdkit rds_activity_stream rds_tools refint roaringbitmap seg sslinfo
|
|
33
|
+
tablefunc tcn tds_fdw tsm_system_rows tsm_system_time unaccent uuid-ossp vector wal2json
|
|
34
|
+
`),
|
|
35
|
+
providerExtensions: [
|
|
36
|
+
"apg_plan_mgmt", "aurora_stat_utils", "aws_commons", "aws_lambda", "aws_ml", "aws_s3", "log_fdw",
|
|
37
|
+
"pg_ad_mapping", "pg_columnmask", "pgdam", "rds_activity_stream", "rds_tools",
|
|
38
|
+
],
|
|
39
|
+
toCheck: [
|
|
40
|
+
"per-release differences inside major 18, and the other majors' tables",
|
|
41
|
+
"pgdam and pg_columnmask were listed by the page with no description read",
|
|
42
|
+
...rds.toCheck.filter((t) => !t.startsWith("per-major")),
|
|
43
|
+
],
|
|
44
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { words, COMMON_REFUSED } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Azure Database for PostgreSQL flexible server. The extension list is "List
|
|
4
|
+
* of extensions and modules by name", the entries available on major 18,
|
|
5
|
+
* read 2026-10-03. Besides being on that list, an extension must be allow-
|
|
6
|
+
* listed in the server's `azure.extensions` parameter before CREATE EXTENSION
|
|
7
|
+
* succeeds, and some also need `shared_preload_libraries`; that is server
|
|
8
|
+
* configuration, not something the list says, so it is not modelled.
|
|
9
|
+
* Roles are the three the access-control page names: azure_pg_admin (the
|
|
10
|
+
* pseudo-superuser the administrator belongs to), azuresu (Microsoft's
|
|
11
|
+
* control-plane superuser) and the administrator role the customer names.
|
|
12
|
+
*/
|
|
13
|
+
export const azure = {
|
|
14
|
+
provider: "azure",
|
|
15
|
+
label: "Azure Database for PostgreSQL flexible server",
|
|
16
|
+
sources: [
|
|
17
|
+
{
|
|
18
|
+
url: "https://learn.microsoft.com/en-us/azure/postgresql/extensions/concepts-extensions-versions",
|
|
19
|
+
readOn: "2026-10-03",
|
|
20
|
+
covers: "allowedExtensions (entries with a major 18 version)",
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
url: "https://learn.microsoft.com/en-us/azure/postgresql/security/security-access-control",
|
|
24
|
+
readOn: "2026-10-03",
|
|
25
|
+
covers: "reservedRoles (azure_pg_admin, azuresu); the public schema is owned by azure_pg_admin",
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
reservedRoles: ["azure_pg_admin", "azuresu"],
|
|
29
|
+
reservedSchemas: [],
|
|
30
|
+
allowedExtensions: words(`
|
|
31
|
+
address_standardizer address_standardizer_data_us age amcheck anon auto_explain azure_ai azure_storage bloom
|
|
32
|
+
btree_gin btree_gist citext credcheck cube dblink dict_int dict_xsyn earthdistance fuzzystrmatch hll hstore hypopg
|
|
33
|
+
intagg intarray ip4r isn lo login_hook ltree oracle_fdw orafce pageinspect pgaudit pg_buffercache pg_cron pgcrypto
|
|
34
|
+
pg_diskann pg_failover_slots pg_freespacemap pg_hint_plan pg_ivm pglogical pg_partman pg_partman_bgw pg_prewarm
|
|
35
|
+
pg_repack pgrouting pgrowlocks pg_squeeze pg_stat_statements pgstattuple pg_trgm pg_visibility plpgsql
|
|
36
|
+
plpgsql_check plv8 pointcloud postgis postgis_raster postgis_sfcgal postgis_tiger_geocoder postgis_topology
|
|
37
|
+
postgres_fdw postgres_protobuf rdkit semver session_variable sslinfo tablefunc tdigest tds_fdw temporal_tables
|
|
38
|
+
timescaledb topn tsm_system_rows tsm_system_time unaccent uuid-ossp vector wal2json
|
|
39
|
+
`),
|
|
40
|
+
extensionListComplete: true,
|
|
41
|
+
providerExtensions: ["azure_ai", "azure_storage", "pg_diskann"],
|
|
42
|
+
refusedStatements: [
|
|
43
|
+
...COMMON_REFUSED,
|
|
44
|
+
{ id: "alter-azure-pg-admin", pattern: /^(ALTER|DROP) (ROLE|USER) "?AZURE_PG_ADMIN"?\b/, reason: "azure_pg_admin is a system-managed restricted role and cannot be modified" },
|
|
45
|
+
{ id: "grant-to-azure-pg-admin", pattern: /^GRANT\b.*\bTO "?AZURE_PG_ADMIN"?\b/, reason: "azure_pg_admin is a system-managed restricted role; granting membership in it is refused" },
|
|
46
|
+
],
|
|
47
|
+
toCheck: [
|
|
48
|
+
"the azure.extensions allowlist parameter and shared_preload_libraries needs: https://learn.microsoft.com/en-us/azure/postgresql/extensions/how-to-allow-extensions (the page rendered no body)",
|
|
49
|
+
"reservedSchemas: none read from a page: https://learn.microsoft.com/en-us/azure/postgresql/security/security-access-control",
|
|
50
|
+
"wal2json and pg_partman_bgw are logical-decoding or background-worker modules that may not take CREATE EXTENSION: https://learn.microsoft.com/en-us/azure/postgresql/extensions/concepts-extensions-versions",
|
|
51
|
+
"older majors' lists",
|
|
52
|
+
],
|
|
53
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { words, COMMON_REFUSED } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Google Cloud SQL for PostgreSQL. The extension list is the tables on
|
|
4
|
+
* "Configure PostgreSQL extensions" (PostGIS, data type, language and
|
|
5
|
+
* miscellaneous groups), read 2026-10-03. Names are the `CREATE EXTENSION`
|
|
6
|
+
* names: the page's pgAudit is `pgaudit`, pgTAP is `pgtap`, pgvector is
|
|
7
|
+
* `vector`. The page says a customer cannot create their own extensions.
|
|
8
|
+
* Roles are the `cloudsql*` names the Cloud SQL users page lists.
|
|
9
|
+
*/
|
|
10
|
+
export const cloudSql = {
|
|
11
|
+
provider: "cloud-sql",
|
|
12
|
+
label: "Google Cloud SQL for PostgreSQL",
|
|
13
|
+
sources: [
|
|
14
|
+
{ url: "https://cloud.google.com/sql/docs/postgres/extensions", readOn: "2026-10-03", covers: "allowedExtensions, superuser requirement" },
|
|
15
|
+
{ url: "https://cloud.google.com/sql/docs/postgres/users", readOn: "2026-10-03", covers: "reservedRoles (the cloudsql* roles)" },
|
|
16
|
+
],
|
|
17
|
+
reservedRoles: [
|
|
18
|
+
"cloudsqlsuperuser", "cloudsqladmin", "cloudsqlagent", "cloudsqlconnpooladmin", "cloudsqliamgroup",
|
|
19
|
+
"cloudsqliamgroupserviceaccount", "cloudsqliamgroupuser", "cloudsqliamserviceaccount", "cloudsqliamuser",
|
|
20
|
+
"cloudsqlimportexport", "cloudsqlinactiveuser", "cloudsqllogical", "cloudsqlobservability", "cloudsqlreplica",
|
|
21
|
+
],
|
|
22
|
+
reservedSchemas: [],
|
|
23
|
+
allowedExtensions: words(`
|
|
24
|
+
address_standardizer address_standardizer_data_us amcheck auto_explain autoinc bloom btree_gin btree_gist chkpass
|
|
25
|
+
citext cube dblink dict_int earthdistance fuzzystrmatch google_ml_integration google_read_only_session hll hstore
|
|
26
|
+
insert_username intagg intarray ip4r isn lo ltree moddatetime oracle_fdw orafce pageinspect pg_background pg_bigm
|
|
27
|
+
pg_buffercache pg_cron pg_freespacemap pg_hint_plan pg_ivm pg_partman pg_prewarm pg_proctab pg_qualstats pg_repack
|
|
28
|
+
pg_similarity pg_squeeze pg_stat_statements pg_textsearch pg_trgm pg_visibility pg_wait_sampling pgaudit pgcrypto
|
|
29
|
+
pgfincore pglogical pgrowlocks pgstattuple pgtap pgtt plpgsql plpgsql_check plv8 postgis postgis_raster
|
|
30
|
+
postgis_sfcgal postgis_tiger_geocoder postgis_topology postgres_fdw prefix rdkit roaringbitmap sslinfo tablefunc
|
|
31
|
+
tcn tds_fdw temporal_tables tsm_system_rows tsm_system_time unaccent uuid-ossp vector
|
|
32
|
+
`),
|
|
33
|
+
extensionListComplete: true,
|
|
34
|
+
providerExtensions: ["google_ml_integration", "google_read_only_session"],
|
|
35
|
+
refusedStatements: COMMON_REFUSED,
|
|
36
|
+
toCheck: [
|
|
37
|
+
"anon (the page lists postgresql_anonymizer; the CREATE EXTENSION name was not read): https://cloud.google.com/sql/docs/postgres/extensions",
|
|
38
|
+
"the list is for the newest major; older majors drop some entries",
|
|
39
|
+
"reservedSchemas: no page read names a schema Cloud SQL owns: https://cloud.google.com/sql/docs/postgres/users",
|
|
40
|
+
],
|
|
41
|
+
};
|