@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,644 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading a live Postgres server's schema from its catalog, and printing each
|
|
3
|
+
* object as the statement that creates it.
|
|
4
|
+
*
|
|
5
|
+
* Postgres has no `SHOW CREATE TABLE`, so the statement is assembled from the
|
|
6
|
+
* server's own printers, the way `pg_dump` assembles it: `format_type()` for
|
|
7
|
+
* every type, `pg_get_expr()` for defaults, generated columns and partition
|
|
8
|
+
* bounds, `pg_get_constraintdef()` for every constraint, `pg_get_indexdef()`,
|
|
9
|
+
* `pg_get_viewdef()` and `pg_get_partkeydef()`. The session reads with an
|
|
10
|
+
* empty `search_path` (`./client.ts`), so every name outside `pg_catalog` is
|
|
11
|
+
* schema-qualified in what they print. That printed form is the canonical
|
|
12
|
+
* one: import writes it, and normalization compares against it.
|
|
13
|
+
*
|
|
14
|
+
* Left out, each for its reason:
|
|
15
|
+
*
|
|
16
|
+
* - the server's own schemas (`pg_catalog`, `information_schema`, `pg_toast`,
|
|
17
|
+
* the temp schemas) and `public`, which every database has;
|
|
18
|
+
* - objects an extension owns (`pg_depend.deptype = 'e'`): the extension
|
|
19
|
+
* declares them, so the extension is what is imported;
|
|
20
|
+
* - `plpgsql`, installed in every database;
|
|
21
|
+
* - a trigger the server makes for itself (a foreign key's) or clones onto a
|
|
22
|
+
* partition from its parent's, and a function or cast that is part of
|
|
23
|
+
* another object (a range type's constructors and its multirange cast);
|
|
24
|
+
* - a table's row type, a multirange type (its range type creates it), and a
|
|
25
|
+
* cast the server defines (`./catalog-kinds.ts`);
|
|
26
|
+
* - a routine with a SQL-standard body (`RETURN`, `BEGIN ATOMIC`): read, but
|
|
27
|
+
* marked `unsupported`, since the server prints it rewritten and a
|
|
28
|
+
* declaration cannot hold it;
|
|
29
|
+
* - a sequence an identity column owns (it is the column's), and a `serial`
|
|
30
|
+
* column's sequence, since the column prints as `serial`;
|
|
31
|
+
* - an index that backs a constraint (the constraint creates it), and an
|
|
32
|
+
* index partition (the partitioned index creates it);
|
|
33
|
+
* - chant's own receipts tables and schema (`../../core/ownership.ts`), and the working
|
|
34
|
+
* columns, constraints and indexes of an expand-and-contract migration in
|
|
35
|
+
* progress (`../migrate/names.ts`, `../migrate/carry.ts`), so a table reads
|
|
36
|
+
* as it was before the migration until its switch, and as declared after it.
|
|
37
|
+
*
|
|
38
|
+
* Objects of the kinds no declaration holds (operators, publications, rules,
|
|
39
|
+
* ...) are not read here: import finds them with `./undeclarable.ts` and
|
|
40
|
+
* refuses them by name (#3911).
|
|
41
|
+
*
|
|
42
|
+
* Objects another tool owns, an ORM's or a migration runner's revision table
|
|
43
|
+
* ({@link FOREIGN_TABLES}), are read and marked with the tool, so import
|
|
44
|
+
* leaves them out with a warning and observation reports them foreign, never
|
|
45
|
+
* as orphans (#3047 question 10).
|
|
46
|
+
*/
|
|
47
|
+
import { POSTGRES_ENTITY_TYPES } from "../entity-types.js";
|
|
48
|
+
import { quoteIdent } from "../keywords.js";
|
|
49
|
+
import { hasChantTrailerKey, RECEIPTS_TRAILER_KEY, stripMarker } from "../../core/ownership.js";
|
|
50
|
+
import { MIGRATION_TRAILER_KEY } from "../migrate/names.js";
|
|
51
|
+
import { isProviderOwned, providerData } from "../providers/index.js";
|
|
52
|
+
import { FOREIGN_TABLES } from "../../core/foreign-tables.js";
|
|
53
|
+
import { readCollations, readRoutineKinds, readTypeKinds } from "./catalog-kinds.js";
|
|
54
|
+
/** The server's own schemas, never part of a declared schema. */
|
|
55
|
+
export const SYSTEM_SCHEMAS = ["pg_catalog", "information_schema", "pg_toast"];
|
|
56
|
+
/** Tables another tool keeps its migration history in, by name, with the tool; both dialects share the list. */
|
|
57
|
+
export { FOREIGN_TABLES };
|
|
58
|
+
const str = (v) => (v === null || v === undefined ? undefined : String(v));
|
|
59
|
+
const literal = (s) => `'${s.replace(/'/g, "''")}'`;
|
|
60
|
+
const qname = (schema, name) => (schema ? `${quoteIdent(schema)}.${quoteIdent(name)}` : quoteIdent(name));
|
|
61
|
+
/** A comment with chant's trailer taken off, or undefined when nothing is left. */
|
|
62
|
+
function ownComment(comment) {
|
|
63
|
+
const c = str(comment);
|
|
64
|
+
if (c === undefined)
|
|
65
|
+
return undefined;
|
|
66
|
+
const own = stripMarker(c);
|
|
67
|
+
return own === "" ? undefined : own;
|
|
68
|
+
}
|
|
69
|
+
const commentOn = (what, comment) => {
|
|
70
|
+
const own = ownComment(comment);
|
|
71
|
+
return own === undefined ? [] : [`COMMENT ON ${what} IS ${literal(own)}`];
|
|
72
|
+
};
|
|
73
|
+
/** The scope as a predicate on a schema name column, with its parameter. */
|
|
74
|
+
export function scopeSql(column, scope) {
|
|
75
|
+
if (scope.schemas)
|
|
76
|
+
return { sql: `${column} = ANY($1::text[])`, params: [[...scope.schemas]] };
|
|
77
|
+
return {
|
|
78
|
+
sql: `${column} NOT IN ('pg_catalog', 'information_schema', 'pg_toast') AND ${column} NOT LIKE 'pg\\_temp\\_%' AND ${column} NOT LIKE 'pg\\_toast\\_temp\\_%'`,
|
|
79
|
+
params: [],
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/** `NOT <object is owned by an extension>`, for an object of catalog `catalog` with oid column `oid`. */
|
|
83
|
+
export const notExtensionOwned = (catalog, oid) => `NOT EXISTS (SELECT 1 FROM pg_catalog.pg_depend d WHERE d.classid = 'pg_catalog.${catalog}'::pg_catalog.regclass AND d.objid = ${oid} AND d.deptype = 'e')`;
|
|
84
|
+
/** `NOT <object is part of another>` (`pg_depend.deptype = 'i'`): a range type's constructor functions, its multirange cast. */
|
|
85
|
+
const notInternal = (catalog, oid) => `NOT EXISTS (SELECT 1 FROM pg_catalog.pg_depend d WHERE d.classid = 'pg_catalog.${catalog}'::pg_catalog.regclass AND d.objid = ${oid} AND d.deptype = 'i')`;
|
|
86
|
+
/** A collation name as a clause, `pg_catalog` names unqualified. */
|
|
87
|
+
const COLLATION_NAME = (oid) => `(SELECT CASE WHEN cn.nspname = 'pg_catalog' THEN pg_catalog.quote_ident(co.collname) ELSE pg_catalog.quote_ident(cn.nspname) || '.' || pg_catalog.quote_ident(co.collname) END ` +
|
|
88
|
+
`FROM pg_catalog.pg_collation co JOIN pg_catalog.pg_namespace cn ON cn.oid = co.collnamespace WHERE co.oid = ${oid})`;
|
|
89
|
+
const TYPE_RANGE = {
|
|
90
|
+
smallint: [-32768n, 32767n],
|
|
91
|
+
integer: [-2147483648n, 2147483647n],
|
|
92
|
+
bigint: [-9223372036854775808n, 9223372036854775807n],
|
|
93
|
+
};
|
|
94
|
+
/** The defaults `CREATE SEQUENCE` gives a sequence of `type` counting by `increment`. */
|
|
95
|
+
export function sequenceDefaults(type, increment) {
|
|
96
|
+
const [lo, hi] = TYPE_RANGE[type] ?? TYPE_RANGE.bigint;
|
|
97
|
+
const min = increment > 0n ? 1n : lo;
|
|
98
|
+
const max = increment > 0n ? hi : -1n;
|
|
99
|
+
return { min: String(min), max: String(max), start: String(increment > 0n ? min : max), cache: "1" };
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* The sequence options that differ from what `CREATE SEQUENCE` would choose,
|
|
103
|
+
* in the order the reference page lists them. `withType` writes `AS type`
|
|
104
|
+
* when it is not `bigint`; an identity sequence takes its column's type.
|
|
105
|
+
*/
|
|
106
|
+
export function sequenceOptions(p, withType) {
|
|
107
|
+
const inc = BigInt(p.increment);
|
|
108
|
+
const d = sequenceDefaults(p.type, inc);
|
|
109
|
+
const out = [];
|
|
110
|
+
if (withType && p.type !== "bigint")
|
|
111
|
+
out.push(`AS ${p.type}`);
|
|
112
|
+
if (inc !== 1n)
|
|
113
|
+
out.push(`INCREMENT BY ${p.increment}`);
|
|
114
|
+
if (p.min !== d.min)
|
|
115
|
+
out.push(`MINVALUE ${p.min}`);
|
|
116
|
+
if (p.max !== d.max)
|
|
117
|
+
out.push(`MAXVALUE ${p.max}`);
|
|
118
|
+
// A sequence starts at its minimum (counting up) or its maximum (counting down) unless told otherwise.
|
|
119
|
+
if (p.start !== (inc > 0n ? p.min : p.max))
|
|
120
|
+
out.push(`START WITH ${p.start}`);
|
|
121
|
+
if (p.cache !== d.cache)
|
|
122
|
+
out.push(`CACHE ${p.cache}`);
|
|
123
|
+
if (p.cycle)
|
|
124
|
+
out.push("CYCLE");
|
|
125
|
+
return out;
|
|
126
|
+
}
|
|
127
|
+
const seqParams = (r) => ({
|
|
128
|
+
type: String(r.type),
|
|
129
|
+
start: String(r.start),
|
|
130
|
+
increment: String(r.increment),
|
|
131
|
+
min: String(r.min),
|
|
132
|
+
max: String(r.max),
|
|
133
|
+
cache: String(r.cache),
|
|
134
|
+
cycle: r.cycle === true,
|
|
135
|
+
});
|
|
136
|
+
// ── Reading ────────────────────────────────────────────────────────────
|
|
137
|
+
/**
|
|
138
|
+
* Every object in scope, in a stable order: schemas, extensions, collations,
|
|
139
|
+
* enums, composite and range types, domains, sequences, tables, views and
|
|
140
|
+
* materialized views, indexes, functions and procedures, aggregates, casts,
|
|
141
|
+
* event triggers, triggers, and where access is read, policies and the
|
|
142
|
+
* declared roles; each kind by schema then name, in byte order.
|
|
143
|
+
*/
|
|
144
|
+
export async function readLiveSchema(client, scope = {}) {
|
|
145
|
+
const out = [];
|
|
146
|
+
const C = 'COLLATE "C"';
|
|
147
|
+
// Schemas.
|
|
148
|
+
{
|
|
149
|
+
const s = scopeSql("n.nspname", scope);
|
|
150
|
+
const rows = await client.query(`SELECT n.oid::text AS oid, n.nspname AS name, pg_catalog.obj_description(n.oid, 'pg_namespace') AS comment
|
|
151
|
+
FROM pg_catalog.pg_namespace n WHERE ${s.sql} AND n.nspname <> 'public' AND ${notExtensionOwned("pg_namespace", "n.oid")}
|
|
152
|
+
ORDER BY n.nspname ${C}`, s.params);
|
|
153
|
+
for (const r of rows) {
|
|
154
|
+
const name = String(r.name);
|
|
155
|
+
// chant's own receipts schema (`../../receipts.ts`), like the receipts tables.
|
|
156
|
+
if (hasChantTrailerKey(str(r.comment), [RECEIPTS_TRAILER_KEY]))
|
|
157
|
+
continue;
|
|
158
|
+
out.push({
|
|
159
|
+
type: POSTGRES_ENTITY_TYPES.schema,
|
|
160
|
+
name,
|
|
161
|
+
oid: String(r.oid),
|
|
162
|
+
...(str(r.comment) ? { comment: str(r.comment) } : {}),
|
|
163
|
+
statement: [`CREATE SCHEMA ${quoteIdent(name)}`, ...commentOn(`SCHEMA ${quoteIdent(name)}`, r.comment)].join(";\n"),
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// Extensions, in a schema in scope.
|
|
168
|
+
{
|
|
169
|
+
const s = scopeSql("n.nspname", scope);
|
|
170
|
+
const rows = await client.query(`SELECT e.oid::text AS oid, e.extname AS name, n.nspname AS schema, e.extversion AS version, x.default_version,
|
|
171
|
+
pg_catalog.obj_description(e.oid, 'pg_extension') AS comment, x.comment AS default_comment
|
|
172
|
+
FROM pg_catalog.pg_extension e JOIN pg_catalog.pg_namespace n ON n.oid = e.extnamespace
|
|
173
|
+
LEFT JOIN pg_catalog.pg_available_extensions x ON x.name = e.extname
|
|
174
|
+
WHERE e.extname <> 'plpgsql' AND (${s.sql} OR n.nspname = 'public')
|
|
175
|
+
ORDER BY e.extname ${C}`, s.params);
|
|
176
|
+
for (const r of rows) {
|
|
177
|
+
const name = String(r.name);
|
|
178
|
+
const version = str(r.version);
|
|
179
|
+
const create = `CREATE EXTENSION ${quoteIdent(name)} WITH SCHEMA ${quoteIdent(String(r.schema))}${version && version !== str(r.default_version) ? ` VERSION ${literal(version)}` : ""}`;
|
|
180
|
+
// The comment the extension's control file sets is the extension's, not a
|
|
181
|
+
// declared one, also when chant's trailer follows it (`../apply/statements.ts`).
|
|
182
|
+
const own = str(r.comment) !== undefined && ownComment(r.comment) === str(r.default_comment) ? null : r.comment;
|
|
183
|
+
out.push({
|
|
184
|
+
type: POSTGRES_ENTITY_TYPES.extension,
|
|
185
|
+
name,
|
|
186
|
+
oid: String(r.oid),
|
|
187
|
+
...(str(r.comment) && str(r.comment) !== str(r.default_comment) ? { comment: str(r.comment) } : {}),
|
|
188
|
+
statement: [create, ...commentOn(`EXTENSION ${quoteIdent(name)}`, own)].join(";\n"),
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// Collations, before the types and columns that use them (#3911).
|
|
193
|
+
out.push(...(await readCollations(client, scope)));
|
|
194
|
+
// Enums.
|
|
195
|
+
{
|
|
196
|
+
const s = scopeSql("n.nspname", scope);
|
|
197
|
+
const rows = await client.query(`SELECT t.oid::text AS oid, n.nspname AS schema, t.typname AS name, pg_catalog.obj_description(t.oid, 'pg_type') AS comment,
|
|
198
|
+
ARRAY(SELECT pg_catalog.quote_literal(e.enumlabel) FROM pg_catalog.pg_enum e WHERE e.enumtypid = t.oid ORDER BY e.enumsortorder) AS labels
|
|
199
|
+
FROM pg_catalog.pg_type t JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
|
|
200
|
+
WHERE t.typtype = 'e' AND ${s.sql} AND ${notExtensionOwned("pg_type", "t.oid")}
|
|
201
|
+
ORDER BY n.nspname ${C}, t.typname ${C}`, s.params);
|
|
202
|
+
for (const r of rows) {
|
|
203
|
+
const q = qname(String(r.schema), String(r.name));
|
|
204
|
+
out.push({
|
|
205
|
+
type: POSTGRES_ENTITY_TYPES.enum,
|
|
206
|
+
schema: String(r.schema),
|
|
207
|
+
name: String(r.name),
|
|
208
|
+
oid: String(r.oid),
|
|
209
|
+
...(str(r.comment) ? { comment: str(r.comment) } : {}),
|
|
210
|
+
statement: [`CREATE TYPE ${q} AS ENUM (${r.labels.join(", ")})`, ...commentOn(`TYPE ${q}`, r.comment)].join(";\n"),
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
// Composite and range types (#3911).
|
|
215
|
+
out.push(...(await readTypeKinds(client, scope)));
|
|
216
|
+
// Domains.
|
|
217
|
+
{
|
|
218
|
+
const s = scopeSql("n.nspname", scope);
|
|
219
|
+
const rows = await client.query(`SELECT t.oid::text AS oid, n.nspname AS schema, t.typname AS name, pg_catalog.format_type(t.typbasetype, t.typtypmod) AS base,
|
|
220
|
+
t.typnotnull AS notnull, t.typdefault AS dflt, pg_catalog.obj_description(t.oid, 'pg_type') AS comment,
|
|
221
|
+
CASE WHEN t.typcollation <> 0 AND t.typcollation <> bt.typcollation THEN ${COLLATION_NAME("t.typcollation")} END AS collation,
|
|
222
|
+
ARRAY(SELECT 'CONSTRAINT ' || pg_catalog.quote_ident(c.conname) || ' ' || pg_catalog.pg_get_constraintdef(c.oid, true)
|
|
223
|
+
FROM pg_catalog.pg_constraint c WHERE c.contypid = t.oid AND c.contype = 'c' ORDER BY c.conname ${C}) AS checks,
|
|
224
|
+
ARRAY(SELECT pg_catalog.quote_ident(c.conname) || E'\\t' || pg_catalog.obj_description(c.oid, 'pg_constraint')
|
|
225
|
+
FROM pg_catalog.pg_constraint c WHERE c.contypid = t.oid AND c.contype = 'c' AND pg_catalog.obj_description(c.oid, 'pg_constraint') IS NOT NULL
|
|
226
|
+
ORDER BY c.conname ${C}) AS check_comments
|
|
227
|
+
FROM pg_catalog.pg_type t JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace JOIN pg_catalog.pg_type bt ON bt.oid = t.typbasetype
|
|
228
|
+
WHERE t.typtype = 'd' AND ${s.sql} AND ${notExtensionOwned("pg_type", "t.oid")}
|
|
229
|
+
ORDER BY n.nspname ${C}, t.typname ${C}`, s.params);
|
|
230
|
+
for (const r of rows) {
|
|
231
|
+
const q = qname(String(r.schema), String(r.name));
|
|
232
|
+
const parts = [`CREATE DOMAIN ${q} AS ${r.base}`];
|
|
233
|
+
if (str(r.collation))
|
|
234
|
+
parts.push(`COLLATE ${r.collation}`);
|
|
235
|
+
if (str(r.dflt))
|
|
236
|
+
parts.push(`DEFAULT ${r.dflt}`);
|
|
237
|
+
if (r.notnull === true)
|
|
238
|
+
parts.push("NOT NULL");
|
|
239
|
+
parts.push(...r.checks);
|
|
240
|
+
const constraintComments = r.check_comments.flatMap((line) => {
|
|
241
|
+
const [con, text] = line.split("\t");
|
|
242
|
+
return commentOn(`CONSTRAINT ${con} ON DOMAIN ${q}`, text);
|
|
243
|
+
});
|
|
244
|
+
out.push({
|
|
245
|
+
type: POSTGRES_ENTITY_TYPES.domain,
|
|
246
|
+
schema: String(r.schema),
|
|
247
|
+
name: String(r.name),
|
|
248
|
+
oid: String(r.oid),
|
|
249
|
+
...(str(r.comment) ? { comment: str(r.comment) } : {}),
|
|
250
|
+
statement: [parts.join(" "), ...commentOn(`DOMAIN ${q}`, r.comment), ...constraintComments].join(";\n"),
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// Tables, read first so sequences can tell a serial column's sequence.
|
|
255
|
+
const s = scopeSql("n.nspname", scope);
|
|
256
|
+
const tables = await client.query(`SELECT c.oid::text AS oid, n.nspname AS schema, c.relname AS name, c.relkind AS kind, c.relpersistence AS persistence,
|
|
257
|
+
c.relispartition AS ispartition, c.reloftype <> 0 AS typed,
|
|
258
|
+
CASE WHEN c.relkind = 'p' THEN pg_catalog.pg_get_partkeydef(c.oid) END AS partkey,
|
|
259
|
+
CASE WHEN c.relispartition THEN pg_catalog.pg_get_expr(c.relpartbound, c.oid, true) END AS bound,
|
|
260
|
+
ARRAY(SELECT pg_catalog.quote_ident(pn.nspname) || '.' || pg_catalog.quote_ident(pc.relname)
|
|
261
|
+
FROM pg_catalog.pg_inherits i JOIN pg_catalog.pg_class pc ON pc.oid = i.inhparent JOIN pg_catalog.pg_namespace pn ON pn.oid = pc.relnamespace
|
|
262
|
+
WHERE i.inhrelid = c.oid ORDER BY i.inhseqno) AS parents,
|
|
263
|
+
c.reloptions AS reloptions, (SELECT a.amname FROM pg_catalog.pg_am a WHERE a.oid = c.relam) AS am,
|
|
264
|
+
c.relrowsecurity AS rowsecurity, c.relforcerowsecurity AS forcerowsecurity,
|
|
265
|
+
(SELECT ts.spcname FROM pg_catalog.pg_tablespace ts WHERE ts.oid = c.reltablespace) AS tablespace,
|
|
266
|
+
pg_catalog.obj_description(c.oid, 'pg_class') AS comment
|
|
267
|
+
FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
268
|
+
WHERE c.relkind IN ('r', 'p') AND ${s.sql} AND ${notExtensionOwned("pg_class", "c.oid")}
|
|
269
|
+
ORDER BY n.nspname ${C}, c.relname ${C}`, s.params);
|
|
270
|
+
const tableOids = tables.map((t) => String(t.oid));
|
|
271
|
+
const columns = tableOids.length === 0 ? [] : await client.query(`SELECT a.attrelid::text AS rel, a.attnum AS num, a.attname AS name, pg_catalog.format_type(a.atttypid, a.atttypmod) AS type,
|
|
272
|
+
a.attnotnull AS notnull, a.attidentity AS identity, a.attgenerated AS generated, a.attislocal AS islocal,
|
|
273
|
+
pg_catalog.pg_get_expr(d.adbin, d.adrelid, true) AS dflt,
|
|
274
|
+
CASE WHEN a.attcollation <> 0 AND a.attcollation <> t.typcollation THEN ${COLLATION_NAME("a.attcollation")} END AS collation,
|
|
275
|
+
pg_catalog.col_description(a.attrelid, a.attnum) AS comment, a.attcompression::text AS compression, a.attstorage::text AS storage, t.typstorage::text AS typstorage
|
|
276
|
+
FROM pg_catalog.pg_attribute a JOIN pg_catalog.pg_type t ON t.oid = a.atttypid
|
|
277
|
+
LEFT JOIN pg_catalog.pg_attrdef d ON d.adrelid = a.attrelid AND d.adnum = a.attnum
|
|
278
|
+
WHERE a.attrelid = ANY($1::oid[]) AND a.attnum > 0 AND NOT a.attisdropped
|
|
279
|
+
ORDER BY a.attrelid, a.attnum`, [tableOids]);
|
|
280
|
+
const constraints = tableOids.length === 0 ? [] : await client.query(`SELECT c.conrelid::text AS rel, c.conname AS name, c.contype AS type, pg_catalog.pg_get_constraintdef(c.oid, true) AS def,
|
|
281
|
+
c.conkey AS keys, pg_catalog.obj_description(c.oid, 'pg_constraint') AS comment
|
|
282
|
+
FROM pg_catalog.pg_constraint c
|
|
283
|
+
WHERE c.conrelid = ANY($1::oid[]) AND c.contype IN ('p', 'u', 'c', 'f', 'x') AND c.conislocal AND c.conparentid = 0
|
|
284
|
+
ORDER BY c.conrelid, CASE c.contype WHEN 'p' THEN 1 WHEN 'u' THEN 2 WHEN 'c' THEN 3 WHEN 'f' THEN 4 ELSE 5 END, c.conname ${C}`, [tableOids]);
|
|
285
|
+
const identities = tableOids.length === 0 ? [] : await client.query(`SELECT d.refobjid::text AS rel, d.refobjsubid AS num, pg_catalog.format_type(s.seqtypid, NULL) AS type, s.seqstart::text AS start,
|
|
286
|
+
s.seqincrement::text AS increment, s.seqmin::text AS min, s.seqmax::text AS max, s.seqcache::text AS cache, s.seqcycle AS cycle
|
|
287
|
+
FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_sequence s ON s.seqrelid = d.objid
|
|
288
|
+
WHERE d.classid = 'pg_catalog.pg_class'::pg_catalog.regclass AND d.refclassid = 'pg_catalog.pg_class'::pg_catalog.regclass
|
|
289
|
+
AND d.deptype = 'i' AND d.refobjid = ANY($1::oid[])`, [tableOids]);
|
|
290
|
+
// Sequences: every one in scope that an identity column does not own.
|
|
291
|
+
const sequences = await client.query(`SELECT c.oid::text AS oid, n.nspname AS schema, c.relname AS name, pg_catalog.format_type(s.seqtypid, NULL) AS type,
|
|
292
|
+
s.seqstart::text AS start, s.seqincrement::text AS increment, s.seqmin::text AS min, s.seqmax::text AS max,
|
|
293
|
+
s.seqcache::text AS cache, s.seqcycle AS cycle, c.relpersistence AS persistence,
|
|
294
|
+
pg_catalog.obj_description(c.oid, 'pg_class') AS comment,
|
|
295
|
+
(SELECT d.refobjid::text || ':' || d.refobjsubid FROM pg_catalog.pg_depend d
|
|
296
|
+
WHERE d.classid = 'pg_catalog.pg_class'::pg_catalog.regclass AND d.objid = c.oid AND d.refclassid = 'pg_catalog.pg_class'::pg_catalog.regclass
|
|
297
|
+
AND d.deptype = 'a' AND d.refobjsubid > 0 LIMIT 1) AS owner
|
|
298
|
+
FROM pg_catalog.pg_class c JOIN pg_catalog.pg_sequence s ON s.seqrelid = c.oid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
299
|
+
WHERE c.relkind = 'S' AND ${s.sql} AND ${notExtensionOwned("pg_class", "c.oid")}
|
|
300
|
+
AND NOT EXISTS (SELECT 1 FROM pg_catalog.pg_depend d WHERE d.classid = 'pg_catalog.pg_class'::pg_catalog.regclass AND d.objid = c.oid AND d.deptype = 'i')
|
|
301
|
+
ORDER BY n.nspname ${C}, c.relname ${C}`, s.params);
|
|
302
|
+
// A serial column: an integer column whose default is nextval of a sequence it owns, named as Postgres names it, with default options.
|
|
303
|
+
const tableByOid = new Map(tables.map((t) => [String(t.oid), t]));
|
|
304
|
+
const serialSequences = new Set();
|
|
305
|
+
const serialColumns = new Map(); // rel:num -> serial type
|
|
306
|
+
for (const q of sequences) {
|
|
307
|
+
const owner = str(q.owner);
|
|
308
|
+
if (!owner)
|
|
309
|
+
continue;
|
|
310
|
+
const [rel, num] = owner.split(":");
|
|
311
|
+
const t = tableByOid.get(rel);
|
|
312
|
+
const col = columns.find((c) => String(c.rel) === rel && String(c.num) === num);
|
|
313
|
+
if (!t || !col)
|
|
314
|
+
continue;
|
|
315
|
+
const seqName = qname(String(q.schema), String(q.name));
|
|
316
|
+
const serial = { integer: "serial", bigint: "bigserial", smallint: "smallserial" }[String(col.type)];
|
|
317
|
+
const expected = `${String(t.name)}_${String(col.name)}_seq`;
|
|
318
|
+
const p = seqParams(q);
|
|
319
|
+
if (serial &&
|
|
320
|
+
String(q.schema) === String(t.schema) &&
|
|
321
|
+
String(q.name) === expected &&
|
|
322
|
+
str(col.dflt) === `nextval(${literal(seqName)}::regclass)` &&
|
|
323
|
+
p.type === String(col.type) &&
|
|
324
|
+
sequenceOptions(p, false).length === 0 &&
|
|
325
|
+
ownComment(q.comment) === undefined) {
|
|
326
|
+
serialSequences.add(String(q.oid));
|
|
327
|
+
serialColumns.set(owner, serial);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
for (const q of sequences) {
|
|
331
|
+
if (serialSequences.has(String(q.oid)))
|
|
332
|
+
continue;
|
|
333
|
+
const qn = qname(String(q.schema), String(q.name));
|
|
334
|
+
const opts = sequenceOptions(seqParams(q), true);
|
|
335
|
+
const owner = str(q.owner);
|
|
336
|
+
const ownerTable = owner ? tableByOid.get(owner.split(":")[0]) : undefined;
|
|
337
|
+
out.push({
|
|
338
|
+
type: POSTGRES_ENTITY_TYPES.sequence,
|
|
339
|
+
schema: String(q.schema),
|
|
340
|
+
name: String(q.name),
|
|
341
|
+
oid: String(q.oid),
|
|
342
|
+
...(str(q.comment) ? { comment: str(q.comment) } : {}),
|
|
343
|
+
...(ownerTable && FOREIGN_TABLES[String(ownerTable.name)] ? { foreign: FOREIGN_TABLES[String(ownerTable.name)] } : {}),
|
|
344
|
+
statement: [
|
|
345
|
+
`CREATE ${q.persistence === "u" ? "UNLOGGED " : ""}SEQUENCE ${qn}${opts.length ? ` ${opts.join(" ")}` : ""}`,
|
|
346
|
+
...commentOn(`SEQUENCE ${qn}`, q.comment),
|
|
347
|
+
].join(";\n"),
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
for (const t of tables) {
|
|
351
|
+
const rel = String(t.oid);
|
|
352
|
+
const q = qname(String(t.schema), String(t.name));
|
|
353
|
+
if (hasChantTrailerKey(str(t.comment), [RECEIPTS_TRAILER_KEY]))
|
|
354
|
+
continue;
|
|
355
|
+
const working = (comment) => hasChantTrailerKey(str(comment), [MIGRATION_TRAILER_KEY]);
|
|
356
|
+
const cols = columns.filter((c) => String(c.rel) === rel && !working(c.comment));
|
|
357
|
+
const cons = constraints.filter((c) => String(c.rel) === rel && !working(c.comment));
|
|
358
|
+
const pkColumns = new Set(cons.filter((c) => c.type === "p").flatMap((c) => c.keys ?? []).map((n) => String(n)));
|
|
359
|
+
const lines = [];
|
|
360
|
+
const commentLines = [];
|
|
361
|
+
for (const c of cols) {
|
|
362
|
+
if (t.ispartition === true || (Array.isArray(t.parents) && t.parents.length > 0 && c.islocal !== true)) {
|
|
363
|
+
// A partition's columns, and an inheriting table's inherited ones, are its parent's.
|
|
364
|
+
commentLines.push(...commentOn(`COLUMN ${q}.${quoteIdent(String(c.name))}`, c.comment));
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
const serial = serialColumns.get(`${rel}:${String(c.num)}`);
|
|
368
|
+
const parts = [quoteIdent(String(c.name)), serial ?? String(c.type)];
|
|
369
|
+
const compression = str(c.compression);
|
|
370
|
+
if (compression === "p")
|
|
371
|
+
parts.push("COMPRESSION pglz");
|
|
372
|
+
else if (compression === "l")
|
|
373
|
+
parts.push("COMPRESSION lz4");
|
|
374
|
+
if (str(c.storage) !== str(c.typstorage)) {
|
|
375
|
+
parts.push(`STORAGE ${{ p: "PLAIN", e: "EXTERNAL", m: "MAIN", x: "EXTENDED" }[String(c.storage)] ?? "DEFAULT"}`);
|
|
376
|
+
}
|
|
377
|
+
if (str(c.collation))
|
|
378
|
+
parts.push(`COLLATE ${c.collation}`);
|
|
379
|
+
const identity = str(c.identity);
|
|
380
|
+
if (identity === "a" || identity === "d") {
|
|
381
|
+
const seq = identities.find((x) => String(x.rel) === rel && String(x.num) === String(c.num));
|
|
382
|
+
const opts = seq ? sequenceOptions(seqParams(seq), false) : [];
|
|
383
|
+
parts.push(`GENERATED ${identity === "a" ? "ALWAYS" : "BY DEFAULT"} AS IDENTITY${opts.length ? ` (${opts.join(" ")})` : ""}`);
|
|
384
|
+
}
|
|
385
|
+
else if (str(c.generated) === "s" || str(c.generated) === "v") {
|
|
386
|
+
parts.push(`GENERATED ALWAYS AS (${c.dflt}) ${c.generated === "s" ? "STORED" : "VIRTUAL"}`);
|
|
387
|
+
}
|
|
388
|
+
else if (str(c.dflt) && !serial)
|
|
389
|
+
parts.push(`DEFAULT ${c.dflt}`);
|
|
390
|
+
// A primary key and an identity column are NOT NULL by themselves.
|
|
391
|
+
if (c.notnull === true && !pkColumns.has(String(c.num)) && identity !== "a" && identity !== "d" && !serial)
|
|
392
|
+
parts.push("NOT NULL");
|
|
393
|
+
lines.push(parts.join(" "));
|
|
394
|
+
commentLines.push(...commentOn(`COLUMN ${q}.${quoteIdent(String(c.name))}`, c.comment));
|
|
395
|
+
}
|
|
396
|
+
for (const c of cons) {
|
|
397
|
+
lines.push(`CONSTRAINT ${quoteIdent(String(c.name))} ${c.def}`);
|
|
398
|
+
commentLines.push(...commentOn(`CONSTRAINT ${quoteIdent(String(c.name))} ON ${q}`, c.comment));
|
|
399
|
+
}
|
|
400
|
+
const head = `CREATE ${t.persistence === "u" ? "UNLOGGED " : ""}TABLE ${q}`;
|
|
401
|
+
const parents = t.parents ?? [];
|
|
402
|
+
let create;
|
|
403
|
+
if (t.ispartition === true) {
|
|
404
|
+
create = `${head} PARTITION OF ${parents[0]}${lines.length ? ` (\n ${lines.join(",\n ")}\n)` : ""} ${t.bound}`;
|
|
405
|
+
}
|
|
406
|
+
else {
|
|
407
|
+
create = `${head} (\n ${lines.join(",\n ")}\n)${parents.length ? ` INHERITS (${parents.join(", ")})` : ""}`;
|
|
408
|
+
}
|
|
409
|
+
if (str(t.partkey))
|
|
410
|
+
create += ` PARTITION BY ${t.partkey}`;
|
|
411
|
+
if (str(t.am) && t.am !== "heap")
|
|
412
|
+
create += ` USING ${quoteIdent(String(t.am))}`;
|
|
413
|
+
const opts = t.reloptions ?? [];
|
|
414
|
+
if (opts.length)
|
|
415
|
+
create += ` WITH (${opts.join(", ")})`;
|
|
416
|
+
if (str(t.tablespace))
|
|
417
|
+
create += ` TABLESPACE ${quoteIdent(String(t.tablespace))}`;
|
|
418
|
+
out.push({
|
|
419
|
+
type: POSTGRES_ENTITY_TYPES.table,
|
|
420
|
+
schema: String(t.schema),
|
|
421
|
+
name: String(t.name),
|
|
422
|
+
oid: rel,
|
|
423
|
+
...(str(t.comment) ? { comment: str(t.comment) } : {}),
|
|
424
|
+
...(FOREIGN_TABLES[String(t.name)] ? { foreign: FOREIGN_TABLES[String(t.name)] } : {}),
|
|
425
|
+
statement: [
|
|
426
|
+
create,
|
|
427
|
+
...(scope.access && t.rowsecurity === true ? [`ALTER TABLE ${q} ENABLE ROW LEVEL SECURITY`] : []),
|
|
428
|
+
...(scope.access && t.forcerowsecurity === true ? [`ALTER TABLE ${q} FORCE ROW LEVEL SECURITY`] : []),
|
|
429
|
+
...commentOn(`TABLE ${q}`, t.comment),
|
|
430
|
+
...commentLines,
|
|
431
|
+
].join(";\n"),
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
// Views and materialized views.
|
|
435
|
+
{
|
|
436
|
+
const rows = await client.query(`SELECT c.oid::text AS oid, n.nspname AS schema, c.relname AS name, c.relkind AS kind, pg_catalog.pg_get_viewdef(c.oid, true) AS def,
|
|
437
|
+
c.reloptions AS reloptions, c.relispopulated AS populated, (SELECT a.amname FROM pg_catalog.pg_am a WHERE a.oid = c.relam) AS am,
|
|
438
|
+
(SELECT ts.spcname FROM pg_catalog.pg_tablespace ts WHERE ts.oid = c.reltablespace) AS tablespace,
|
|
439
|
+
pg_catalog.obj_description(c.oid, 'pg_class') AS comment,
|
|
440
|
+
ARRAY(SELECT pg_catalog.quote_ident(a.attname) || E'\\t' || pg_catalog.col_description(c.oid, a.attnum) FROM pg_catalog.pg_attribute a
|
|
441
|
+
WHERE a.attrelid = c.oid AND a.attnum > 0 AND NOT a.attisdropped AND pg_catalog.col_description(c.oid, a.attnum) IS NOT NULL ORDER BY a.attnum) AS column_comments
|
|
442
|
+
FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
443
|
+
WHERE c.relkind IN ('v', 'm') AND ${s.sql} AND ${notExtensionOwned("pg_class", "c.oid")}
|
|
444
|
+
ORDER BY n.nspname ${C}, c.relname ${C}`, s.params);
|
|
445
|
+
for (const r of rows) {
|
|
446
|
+
const q = qname(String(r.schema), String(r.name));
|
|
447
|
+
const materialized = r.kind === "m";
|
|
448
|
+
const query = String(r.def).trim().replace(/;$/, "");
|
|
449
|
+
let opts = r.reloptions ?? [];
|
|
450
|
+
const check = opts.find((o) => o.startsWith("check_option="));
|
|
451
|
+
opts = opts.filter((o) => !o.startsWith("check_option="));
|
|
452
|
+
let create = `CREATE ${materialized ? "MATERIALIZED " : ""}VIEW ${q}`;
|
|
453
|
+
if (materialized && str(r.am) && r.am !== "heap")
|
|
454
|
+
create += ` USING ${quoteIdent(String(r.am))}`;
|
|
455
|
+
if (opts.length)
|
|
456
|
+
create += ` WITH (${opts.join(", ")})`;
|
|
457
|
+
if (materialized && str(r.tablespace))
|
|
458
|
+
create += ` TABLESPACE ${quoteIdent(String(r.tablespace))}`;
|
|
459
|
+
create += ` AS\n${query}`;
|
|
460
|
+
if (check)
|
|
461
|
+
create += `\nWITH ${check.endsWith("local") ? "LOCAL" : "CASCADED"} CHECK OPTION`;
|
|
462
|
+
if (materialized && r.populated === false)
|
|
463
|
+
create += "\nWITH NO DATA";
|
|
464
|
+
const what = materialized ? "MATERIALIZED VIEW" : "VIEW";
|
|
465
|
+
const columnComments = r.column_comments.flatMap((line) => {
|
|
466
|
+
const [col, text] = line.split("\t");
|
|
467
|
+
return commentOn(`COLUMN ${q}.${col}`, text);
|
|
468
|
+
});
|
|
469
|
+
out.push({
|
|
470
|
+
type: materialized ? POSTGRES_ENTITY_TYPES.materializedView : POSTGRES_ENTITY_TYPES.view,
|
|
471
|
+
schema: String(r.schema),
|
|
472
|
+
name: String(r.name),
|
|
473
|
+
oid: String(r.oid),
|
|
474
|
+
...(str(r.comment) ? { comment: str(r.comment) } : {}),
|
|
475
|
+
statement: [create, ...commentOn(`${what} ${q}`, r.comment), ...columnComments].join(";\n"),
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
// Indexes a constraint does not create, and not index partitions.
|
|
480
|
+
{
|
|
481
|
+
const rows = await client.query(`SELECT ic.oid::text AS oid, n.nspname AS schema, ic.relname AS name, tc.relname AS table_name,
|
|
482
|
+
pg_catalog.pg_get_indexdef(i.indexrelid, 0, true) AS def, pg_catalog.obj_description(ic.oid, 'pg_class') AS comment
|
|
483
|
+
FROM pg_catalog.pg_index i JOIN pg_catalog.pg_class ic ON ic.oid = i.indexrelid JOIN pg_catalog.pg_class tc ON tc.oid = i.indrelid
|
|
484
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = ic.relnamespace
|
|
485
|
+
WHERE tc.relkind IN ('r', 'p', 'm') AND NOT ic.relispartition AND ${s.sql} AND ${notExtensionOwned("pg_class", "tc.oid")}
|
|
486
|
+
AND NOT EXISTS (SELECT 1 FROM pg_catalog.pg_constraint c WHERE c.conindid = i.indexrelid AND c.contype IN ('p', 'u', 'x'))
|
|
487
|
+
ORDER BY n.nspname ${C}, ic.relname ${C}`, s.params);
|
|
488
|
+
for (const r of rows) {
|
|
489
|
+
// An index a migration builds on its new column, or keeps on the old one until its contract.
|
|
490
|
+
if (hasChantTrailerKey(str(r.comment), [MIGRATION_TRAILER_KEY]))
|
|
491
|
+
continue;
|
|
492
|
+
const q = qname(String(r.schema), String(r.name));
|
|
493
|
+
out.push({
|
|
494
|
+
type: POSTGRES_ENTITY_TYPES.index,
|
|
495
|
+
schema: String(r.schema),
|
|
496
|
+
name: String(r.name),
|
|
497
|
+
oid: String(r.oid),
|
|
498
|
+
...(str(r.comment) ? { comment: str(r.comment) } : {}),
|
|
499
|
+
...(FOREIGN_TABLES[String(r.table_name)] ? { foreign: FOREIGN_TABLES[String(r.table_name)] } : {}),
|
|
500
|
+
statement: [String(r.def), ...commentOn(`INDEX ${q}`, r.comment)].join(";\n"),
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
// Functions and procedures (not aggregates).
|
|
505
|
+
{
|
|
506
|
+
const rows = await client.query(`SELECT p.oid::text AS oid, n.nspname AS schema, p.proname AS name, p.prokind AS kind, pg_catalog.pg_get_functiondef(p.oid) AS def,
|
|
507
|
+
'(' || pg_catalog.replace(pg_catalog.oidvectortypes(p.proargtypes), ', ', ',') || ')' AS signature,
|
|
508
|
+
p.prosqlbody IS NOT NULL AS sqlbody, pg_catalog.obj_description(p.oid, 'pg_proc') AS comment,
|
|
509
|
+
ARRAY(SELECT pg_catalog.pg_describe_object(d.classid, d.objid, d.objsubid) FROM pg_catalog.pg_depend d
|
|
510
|
+
WHERE d.refclassid = 'pg_catalog.pg_proc'::pg_catalog.regclass AND d.refobjid = p.oid AND d.deptype = 'n'
|
|
511
|
+
ORDER BY 1) AS dependents
|
|
512
|
+
FROM pg_catalog.pg_proc p JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace
|
|
513
|
+
WHERE p.prokind IN ('f', 'p', 'w') AND ${s.sql} AND ${notExtensionOwned("pg_proc", "p.oid")} AND ${notInternal("pg_proc", "p.oid")}
|
|
514
|
+
ORDER BY n.nspname ${C}, p.proname ${C}, pg_catalog.oidvectortypes(p.proargtypes) ${C}`, s.params);
|
|
515
|
+
for (const r of rows) {
|
|
516
|
+
// A migration's dual-write function, until its contract.
|
|
517
|
+
if (hasChantTrailerKey(str(r.comment), [MIGRATION_TRAILER_KEY]))
|
|
518
|
+
continue;
|
|
519
|
+
const signature = String(r.signature);
|
|
520
|
+
const q = `${qname(String(r.schema), String(r.name))}${signature}`;
|
|
521
|
+
const procedure = r.kind === "p";
|
|
522
|
+
const dependents = r.dependents ?? [];
|
|
523
|
+
out.push({
|
|
524
|
+
type: procedure ? POSTGRES_ENTITY_TYPES.procedure : POSTGRES_ENTITY_TYPES.function,
|
|
525
|
+
schema: String(r.schema),
|
|
526
|
+
name: String(r.name),
|
|
527
|
+
oid: String(r.oid),
|
|
528
|
+
signature,
|
|
529
|
+
...(dependents.length > 0 ? { dependents } : {}),
|
|
530
|
+
...(str(r.comment) ? { comment: str(r.comment) } : {}),
|
|
531
|
+
...(r.sqlbody === true ? { unsupported: "its body is SQL-standard (RETURN or BEGIN ATOMIC), which the server prints back rewritten; declare it with a string body (AS $$ ... $$)" } : {}),
|
|
532
|
+
statement: [String(r.def).trim(), ...commentOn(`${procedure ? "PROCEDURE" : "FUNCTION"} ${q}`, r.comment)].join(";\n"),
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
// Aggregates, casts and event triggers, after the functions they call (#3911).
|
|
537
|
+
out.push(...(await readRoutineKinds(client, scope)));
|
|
538
|
+
// Triggers: the ones written by hand, not a foreign key's or a partition's clone of its parent's.
|
|
539
|
+
{
|
|
540
|
+
const rows = await client.query(`SELECT t.oid::text AS oid, n.nspname AS schema, c.relname AS table_name, t.tgname AS name, pg_catalog.pg_get_triggerdef(t.oid, true) AS def,
|
|
541
|
+
pg_catalog.obj_description(t.oid, 'pg_trigger') AS comment
|
|
542
|
+
FROM pg_catalog.pg_trigger t JOIN pg_catalog.pg_class c ON c.oid = t.tgrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
543
|
+
WHERE NOT t.tgisinternal AND t.tgparentid = 0 AND ${s.sql} AND ${notExtensionOwned("pg_class", "c.oid")}
|
|
544
|
+
ORDER BY n.nspname ${C}, c.relname ${C}, t.tgname ${C}`, s.params);
|
|
545
|
+
for (const r of rows) {
|
|
546
|
+
// A migration's dual-write trigger, until its contract.
|
|
547
|
+
if (hasChantTrailerKey(str(r.comment), [MIGRATION_TRAILER_KEY]))
|
|
548
|
+
continue;
|
|
549
|
+
const table = qname(String(r.schema), String(r.table_name));
|
|
550
|
+
out.push({
|
|
551
|
+
type: POSTGRES_ENTITY_TYPES.trigger,
|
|
552
|
+
schema: String(r.schema),
|
|
553
|
+
name: String(r.name),
|
|
554
|
+
oid: String(r.oid),
|
|
555
|
+
signature: ` ON ${table}`,
|
|
556
|
+
...(str(r.comment) ? { comment: str(r.comment) } : {}),
|
|
557
|
+
...(FOREIGN_TABLES[String(r.table_name)] ? { foreign: FOREIGN_TABLES[String(r.table_name)] } : {}),
|
|
558
|
+
statement: [String(r.def), ...commentOn(`TRIGGER ${quoteIdent(String(r.name))} ON ${table}`, r.comment)].join(";\n"),
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
if (scope.access) {
|
|
563
|
+
// Policies, on the tables in scope.
|
|
564
|
+
const policies = await client.query(`SELECT p.oid::text AS oid, n.nspname AS schema, c.relname AS table_name, p.polname AS name, p.polpermissive AS permissive, p.polcmd AS cmd,
|
|
565
|
+
ARRAY(SELECT CASE WHEN r = 0 THEN 'PUBLIC' ELSE pg_catalog.quote_ident(pg_catalog.pg_get_userbyid(r)) END FROM pg_catalog.unnest(p.polroles) r ORDER BY 1) AS roles,
|
|
566
|
+
pg_catalog.pg_get_expr(p.polqual, p.polrelid, true) AS qual, pg_catalog.pg_get_expr(p.polwithcheck, p.polrelid, true) AS withcheck,
|
|
567
|
+
pg_catalog.obj_description(p.oid, 'pg_policy') AS comment
|
|
568
|
+
FROM pg_catalog.pg_policy p JOIN pg_catalog.pg_class c ON c.oid = p.polrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
569
|
+
WHERE ${s.sql} AND ${notExtensionOwned("pg_class", "c.oid")}
|
|
570
|
+
ORDER BY n.nspname ${C}, c.relname ${C}, p.polname ${C}`, s.params);
|
|
571
|
+
const COMMAND = { r: "SELECT", a: "INSERT", w: "UPDATE", d: "DELETE" };
|
|
572
|
+
for (const r of policies) {
|
|
573
|
+
const table = qname(String(r.schema), String(r.table_name));
|
|
574
|
+
const roles = r.roles ?? [];
|
|
575
|
+
const parts = [`CREATE POLICY ${quoteIdent(String(r.name))} ON ${table}`];
|
|
576
|
+
if (r.permissive === false)
|
|
577
|
+
parts.push("AS RESTRICTIVE");
|
|
578
|
+
if (COMMAND[String(r.cmd)])
|
|
579
|
+
parts.push(`FOR ${COMMAND[String(r.cmd)]}`);
|
|
580
|
+
if (!(roles.length === 1 && roles[0] === "PUBLIC"))
|
|
581
|
+
parts.push(`TO ${roles.join(", ")}`);
|
|
582
|
+
if (str(r.qual))
|
|
583
|
+
parts.push(`USING (${r.qual})`);
|
|
584
|
+
if (str(r.withcheck))
|
|
585
|
+
parts.push(`WITH CHECK (${r.withcheck})`);
|
|
586
|
+
out.push({
|
|
587
|
+
type: POSTGRES_ENTITY_TYPES.policy,
|
|
588
|
+
schema: String(r.schema),
|
|
589
|
+
name: String(r.name),
|
|
590
|
+
oid: String(r.oid),
|
|
591
|
+
signature: ` ON ${table}`,
|
|
592
|
+
...(str(r.comment) ? { comment: str(r.comment) } : {}),
|
|
593
|
+
statement: [parts.join(" "), ...commentOn(`POLICY ${quoteIdent(String(r.name))} ON ${table}`, r.comment)].join(";\n"),
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
// The declared roles, by name: a role is the cluster's, and the ones the declarations do not name are the environment's.
|
|
597
|
+
if (scope.roles && scope.roles.length > 0) {
|
|
598
|
+
const roles = await client.query(`SELECT r.oid::text AS oid, r.rolname AS name, r.rolsuper AS superuser, r.rolinherit AS inherit, r.rolcreaterole AS createrole, r.rolcreatedb AS createdb,
|
|
599
|
+
r.rolcanlogin AS login, r.rolreplication AS replication, r.rolbypassrls AS bypassrls, r.rolconnlimit AS connlimit,
|
|
600
|
+
pg_catalog.shobj_description(r.oid, 'pg_authid') AS comment
|
|
601
|
+
FROM pg_catalog.pg_roles r WHERE r.rolname = ANY($1::text[]) ORDER BY r.rolname ${C}`, [[...scope.roles]]);
|
|
602
|
+
for (const r of roles) {
|
|
603
|
+
const words = [];
|
|
604
|
+
for (const [k, dflt] of [["superuser", false], ["createdb", false], ["createrole", false], ["inherit", true], ["login", false], ["replication", false], ["bypassrls", false]]) {
|
|
605
|
+
if ((r[k] === true) !== dflt)
|
|
606
|
+
words.push(r[k] === true ? k.toUpperCase() : `NO${k.toUpperCase()}`);
|
|
607
|
+
}
|
|
608
|
+
if (Number(r.connlimit) !== -1)
|
|
609
|
+
words.push(`CONNECTION LIMIT ${Number(r.connlimit)}`);
|
|
610
|
+
const name = quoteIdent(String(r.name));
|
|
611
|
+
out.push({
|
|
612
|
+
type: POSTGRES_ENTITY_TYPES.role,
|
|
613
|
+
name: String(r.name),
|
|
614
|
+
oid: String(r.oid),
|
|
615
|
+
...(str(r.comment) ? { comment: str(r.comment) } : {}),
|
|
616
|
+
statement: [`CREATE ROLE ${name}${words.length > 0 ? ` WITH ${words.join(" ")}` : ""}`, ...commentOn(`ROLE ${name}`, r.comment)].join(";\n"),
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
return out;
|
|
622
|
+
}
|
|
623
|
+
/** The key an object is found by: its type and schema-qualified name, and a routine's parameter types or a trigger's table. */
|
|
624
|
+
export const liveKey = (type, schema, name, signature) => `${type} ${schema ?? ""}.${name}${signature ?? ""}`;
|
|
625
|
+
/**
|
|
626
|
+
* Mark what the managed service owns (#3282) as foreign: its reserved
|
|
627
|
+
* schemas and everything in them, and the extensions it installs itself
|
|
628
|
+
* (`rdsadmin`, Supabase's `auth` and `storage`, ...). They are the
|
|
629
|
+
* provider's, so import leaves them out and observation never reports them
|
|
630
|
+
* as chant's.
|
|
631
|
+
*/
|
|
632
|
+
export function markProviderOwned(objects, provider) {
|
|
633
|
+
if (!provider)
|
|
634
|
+
return [...objects];
|
|
635
|
+
const label = providerData(provider).label;
|
|
636
|
+
return objects.map((o) => {
|
|
637
|
+
if (o.foreign)
|
|
638
|
+
return o;
|
|
639
|
+
const owned = (o.type === POSTGRES_ENTITY_TYPES.schema && isProviderOwned(provider, { kind: "schema", name: o.name })) ||
|
|
640
|
+
(o.type === POSTGRES_ENTITY_TYPES.extension && isProviderOwned(provider, { kind: "extension", name: o.name })) ||
|
|
641
|
+
(o.schema !== undefined && isProviderOwned(provider, { kind: "schema", name: o.schema }));
|
|
642
|
+
return owned ? { ...o, foreign: label } : o;
|
|
643
|
+
});
|
|
644
|
+
}
|