@intentius/chant-lexicon-sql 0.124.46 → 0.124.49
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.d.ts +1 -1
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +2 -1
- package/dist/clickhouse/apply/statements.d.ts +9 -0
- package/dist/clickhouse/apply/statements.d.ts.map +1 -1
- package/dist/clickhouse/apply/statements.js +24 -0
- package/dist/clickhouse/import/live-export.d.ts +7 -3
- package/dist/clickhouse/import/live-export.d.ts.map +1 -1
- package/dist/clickhouse/import/live-export.js +21 -5
- package/dist/clickhouse/live/named-collections.d.ts +27 -0
- package/dist/clickhouse/live/named-collections.d.ts.map +1 -0
- package/dist/clickhouse/live/named-collections.js +53 -0
- package/dist/clickhouse/live/undeclarable.d.ts +15 -3
- package/dist/clickhouse/live/undeclarable.d.ts.map +1 -1
- package/dist/clickhouse/live/undeclarable.js +18 -4
- package/dist/clickhouse/named-collections.d.ts +83 -0
- package/dist/clickhouse/named-collections.d.ts.map +1 -0
- package/dist/clickhouse/named-collections.js +203 -0
- package/dist/clickhouse/parser.d.ts.map +1 -1
- package/dist/clickhouse/parser.js +1 -1
- package/dist/clickhouse/plan/commands.d.ts +2 -1
- package/dist/clickhouse/plan/commands.d.ts.map +1 -1
- package/dist/clickhouse/plan/commands.js +17 -2
- package/dist/clickhouse/plan/diff.d.ts +15 -0
- package/dist/clickhouse/plan/diff.d.ts.map +1 -1
- package/dist/clickhouse/plan/diff.js +43 -1
- package/dist/clickhouse/plan/disruption.d.ts.map +1 -1
- package/dist/clickhouse/plan/disruption.js +5 -0
- package/dist/clickhouse/plan/rebuild-handoff.d.ts.map +1 -1
- package/dist/clickhouse/plan/rebuild-handoff.js +4 -0
- package/dist/clickhouse/plan/report.d.ts.map +1 -1
- package/dist/clickhouse/plan/report.js +6 -1
- package/dist/clickhouse/plan/rules.d.ts +5 -1
- package/dist/clickhouse/plan/rules.d.ts.map +1 -1
- package/dist/clickhouse/plan/rules.js +7 -3
- package/dist/clickhouse/plan/statements.d.ts +29 -0
- package/dist/clickhouse/plan/statements.d.ts.map +1 -0
- package/dist/clickhouse/plan/statements.js +334 -0
- package/dist/clickhouse/plan/stream.d.ts +40 -0
- package/dist/clickhouse/plan/stream.d.ts.map +1 -0
- package/dist/clickhouse/plan/stream.js +107 -0
- package/dist/clickhouse/rebuild/observe.d.ts +3 -1
- package/dist/clickhouse/rebuild/observe.d.ts.map +1 -1
- package/dist/clickhouse/rebuild/observe.js +14 -1
- package/dist/clickhouse/review-catalog.d.ts +213 -0
- package/dist/clickhouse/review-catalog.d.ts.map +1 -0
- package/dist/clickhouse/review-catalog.js +330 -0
- package/dist/codegen/classifier-page.d.ts.map +1 -1
- package/dist/codegen/classifier-page.js +44 -4
- package/dist/codegen/clickhouse-module.d.ts.map +1 -1
- package/dist/codegen/clickhouse-module.js +3 -0
- package/dist/codegen/coverage-page.d.ts.map +1 -1
- package/dist/codegen/coverage-page.js +5 -3
- package/dist/codegen/docs.js +1 -1
- package/dist/codegen/postgres-classifier-page.js +2 -2
- package/dist/codegen/registry.d.ts.map +1 -1
- package/dist/codegen/registry.js +10 -0
- package/dist/composites/catalog.d.ts.map +1 -1
- package/dist/composites/catalog.js +100 -0
- package/dist/composites/clickhouse.d.ts +2 -0
- package/dist/composites/clickhouse.d.ts.map +1 -1
- package/dist/composites/clickhouse.js +2 -0
- package/dist/composites/otel-tables.d.ts +100 -0
- package/dist/composites/otel-tables.d.ts.map +1 -0
- package/dist/composites/otel-tables.js +282 -0
- package/dist/composites/stream-ingest.d.ts +63 -0
- package/dist/composites/stream-ingest.d.ts.map +1 -0
- package/dist/composites/stream-ingest.js +39 -0
- package/dist/core/statement-class.d.ts +55 -0
- package/dist/core/statement-class.d.ts.map +1 -0
- package/dist/core/statement-class.js +43 -0
- package/dist/coverage/clickhouse.json +1 -1
- package/dist/coverage/postgres.json +12 -12
- package/dist/coverage.d.ts +16 -6
- package/dist/coverage.d.ts.map +1 -1
- package/dist/coverage.js +10 -5
- package/dist/drizzle-command.d.ts +17 -0
- package/dist/drizzle-command.d.ts.map +1 -0
- package/dist/drizzle-command.js +91 -0
- package/dist/drizzle.d.ts +70 -0
- package/dist/drizzle.d.ts.map +1 -0
- package/dist/drizzle.js +689 -0
- package/dist/files/clickhouse.d.ts.map +1 -1
- package/dist/files/clickhouse.js +8 -1
- package/dist/files/common.d.ts +13 -0
- package/dist/files/common.d.ts.map +1 -1
- package/dist/files/common.js +5 -0
- package/dist/files/postgres.d.ts.map +1 -1
- package/dist/files/postgres.js +35 -3
- package/dist/generated/clickhouse.d.ts +2 -0
- package/dist/generated/clickhouse.d.ts.map +1 -1
- package/dist/generated/clickhouse.js +2 -0
- package/dist/import-parser.d.ts +21 -1
- package/dist/import-parser.d.ts.map +1 -1
- package/dist/import-parser.js +79 -5
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/integrity.json +7 -6
- package/dist/lint/post-synth/audit-entries.d.ts.map +1 -1
- package/dist/lint/post-synth/audit-entries.js +1 -0
- package/dist/lint/post-synth/index.d.ts.map +1 -1
- package/dist/lint/post-synth/index.js +2 -0
- package/dist/lint/post-synth/postgres-names.d.ts.map +1 -1
- package/dist/lint/post-synth/postgres-names.js +3 -0
- package/dist/lint/post-synth/sqlch128.d.ts +18 -0
- package/dist/lint/post-synth/sqlch128.d.ts.map +1 -0
- package/dist/lint/post-synth/sqlch128.js +222 -0
- package/dist/lint/rules/postgres-templates.js +2 -2
- package/dist/lsp/postgres-template.d.ts.map +1 -1
- package/dist/lsp/postgres-template.js +21 -0
- package/dist/manifest.json +1 -1
- package/dist/mcp/postgres.js +1 -1
- package/dist/meta.json +50 -0
- package/dist/migrate/from-postgres/detect.d.ts +4 -0
- package/dist/migrate/from-postgres/detect.d.ts.map +1 -0
- package/dist/migrate/from-postgres/detect.js +7 -0
- package/dist/migrate/from-postgres/emit.d.ts +26 -0
- package/dist/migrate/from-postgres/emit.d.ts.map +1 -0
- package/dist/migrate/from-postgres/emit.js +86 -0
- package/dist/migrate/from-postgres/index.d.ts +51 -0
- package/dist/migrate/from-postgres/index.d.ts.map +1 -0
- package/dist/migrate/from-postgres/index.js +115 -0
- package/dist/migrate/from-postgres/translate.d.ts +73 -0
- package/dist/migrate/from-postgres/translate.d.ts.map +1 -0
- package/dist/migrate/from-postgres/translate.js +261 -0
- package/dist/migrate/from-postgres/types.d.ts +51 -0
- package/dist/migrate/from-postgres/types.d.ts.map +1 -0
- package/dist/migrate/from-postgres/types.js +211 -0
- package/dist/migration-classify.d.ts +23 -0
- package/dist/migration-classify.d.ts.map +1 -0
- package/dist/migration-classify.js +44 -0
- package/dist/migration-replay.d.ts +73 -0
- package/dist/migration-replay.d.ts.map +1 -0
- package/dist/migration-replay.js +294 -0
- package/dist/okf/index.md +11 -0
- package/dist/okf/rules/SQLCH128.md +11 -0
- package/dist/okf/types/PostgresForeignDataWrapper.md +9 -0
- package/dist/okf/types/PostgresForeignServer.md +9 -0
- package/dist/okf/types/PostgresForeignTable.md +9 -0
- package/dist/okf/types/PostgresOperator.md +9 -0
- package/dist/okf/types/PostgresOperatorClass.md +9 -0
- package/dist/okf/types/PostgresOperatorFamily.md +9 -0
- package/dist/okf/types/PostgresPublication.md +9 -0
- package/dist/okf/types/PostgresStatistics.md +9 -0
- package/dist/okf/types/PostgresTextSearchConfiguration.md +9 -0
- package/dist/okf/types/PostgresTextSearchDictionary.md +9 -0
- package/dist/op/activities/postgres-migration.d.ts.map +1 -1
- package/dist/op/activities/postgres-migration.js +4 -0
- package/dist/op/activity-contracts.d.ts +20 -0
- package/dist/op/activity-contracts.d.ts.map +1 -1
- package/dist/op/activity-contracts.js +2 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +49 -2
- package/dist/postgres/access/acl.js +1 -1
- package/dist/postgres/access/import.js +1 -1
- package/dist/postgres/apply/apply.js +13 -1
- package/dist/postgres/apply/statements.d.ts.map +1 -1
- package/dist/postgres/apply/statements.js +243 -4
- package/dist/postgres/constraint-names.d.ts +30 -0
- package/dist/postgres/constraint-names.d.ts.map +1 -0
- package/dist/postgres/constraint-names.js +72 -0
- package/dist/postgres/entities.d.ts +229 -2
- package/dist/postgres/entities.d.ts.map +1 -1
- package/dist/postgres/entities.js +326 -5
- package/dist/postgres/entity-types.d.ts +10 -0
- package/dist/postgres/entity-types.d.ts.map +1 -1
- package/dist/postgres/entity-types.js +10 -0
- package/dist/postgres/import/generator.d.ts.map +1 -1
- package/dist/postgres/import/generator.js +65 -2
- package/dist/postgres/import/live-export.d.ts.map +1 -1
- package/dist/postgres/import/live-export.js +2 -0
- package/dist/postgres/live/catalog-kinds.d.ts +20 -0
- package/dist/postgres/live/catalog-kinds.d.ts.map +1 -1
- package/dist/postgres/live/catalog-kinds.js +452 -0
- package/dist/postgres/live/catalog.d.ts +2 -0
- package/dist/postgres/live/catalog.d.ts.map +1 -1
- package/dist/postgres/live/catalog.js +3 -1
- package/dist/postgres/live/describe-resources.d.ts.map +1 -1
- package/dist/postgres/live/describe-resources.js +12 -2
- package/dist/postgres/migrate/backfill.d.ts +2 -0
- package/dist/postgres/migrate/backfill.d.ts.map +1 -1
- package/dist/postgres/migrate/backfill.js +7 -1
- package/dist/postgres/migrate/carry.d.ts +45 -1
- package/dist/postgres/migrate/carry.d.ts.map +1 -1
- package/dist/postgres/migrate/carry.js +72 -8
- package/dist/postgres/migrate/observe.d.ts +28 -4
- package/dist/postgres/migrate/observe.d.ts.map +1 -1
- package/dist/postgres/migrate/observe.js +45 -13
- package/dist/postgres/migrate/op.d.ts +26 -1
- package/dist/postgres/migrate/op.d.ts.map +1 -1
- package/dist/postgres/migrate/op.js +3 -1
- package/dist/postgres/migrate/replication.d.ts +42 -0
- package/dist/postgres/migrate/replication.d.ts.map +1 -1
- package/dist/postgres/migrate/replication.js +53 -0
- package/dist/postgres/migrate/steps.d.ts +35 -2
- package/dist/postgres/migrate/steps.d.ts.map +1 -1
- package/dist/postgres/migrate/steps.js +158 -12
- package/dist/postgres/parser.d.ts +144 -1
- package/dist/postgres/parser.d.ts.map +1 -1
- package/dist/postgres/parser.js +370 -4
- package/dist/postgres/plan/deep.d.ts.map +1 -1
- package/dist/postgres/plan/deep.js +43 -4
- package/dist/postgres/plan/diff.d.ts.map +1 -1
- package/dist/postgres/plan/diff.js +154 -1
- package/dist/postgres/plan/normalize.d.ts +28 -2
- package/dist/postgres/plan/normalize.d.ts.map +1 -1
- package/dist/postgres/plan/normalize.js +260 -1
- package/dist/postgres/plan/rules.d.ts +4 -0
- package/dist/postgres/plan/rules.d.ts.map +1 -1
- package/dist/postgres/plan/rules.js +6 -2
- package/dist/postgres/plan/schema.js +1 -1
- package/dist/postgres/plan/statements.d.ts +26 -0
- package/dist/postgres/plan/statements.d.ts.map +1 -0
- package/dist/postgres/plan/statements.js +364 -0
- package/dist/postgres/secret-options.d.ts +10 -0
- package/dist/postgres/secret-options.d.ts.map +1 -0
- package/dist/postgres/secret-options.js +9 -0
- package/dist/postgres/testing/roundtrip-schema.d.ts +4 -2
- package/dist/postgres/testing/roundtrip-schema.d.ts.map +1 -1
- package/dist/postgres/testing/roundtrip-schema.js +24 -1
- package/dist/postgres.d.ts +1 -1
- package/dist/postgres.d.ts.map +1 -1
- package/dist/postgres.js +1 -1
- package/dist/rules/audit-entries.ts +1 -0
- package/dist/rules/postgres-names.ts +2 -0
- package/dist/rules/postgres-templates.ts +2 -2
- package/dist/rules/sqlch128.ts +230 -0
- package/dist/serializer.d.ts +5 -0
- package/dist/serializer.d.ts.map +1 -1
- package/dist/serializer.js +13 -2
- package/dist/sql-types-command.d.ts +13 -0
- package/dist/sql-types-command.d.ts.map +1 -1
- package/dist/sql-types-command.js +31 -16
- package/dist/sql-types.d.ts +22 -0
- package/dist/sql-types.d.ts.map +1 -1
- package/dist/sql-types.js +10 -6
- package/dist/sqlserver/lexer.d.ts +37 -0
- package/dist/sqlserver/lexer.d.ts.map +1 -0
- package/dist/sqlserver/lexer.js +254 -0
- package/dist/sqlserver/parse.d.ts +185 -0
- package/dist/sqlserver/parse.d.ts.map +1 -0
- package/dist/sqlserver/parse.js +767 -0
- package/dist/sqlserver/translate.d.ts +34 -0
- package/dist/sqlserver/translate.d.ts.map +1 -0
- package/dist/sqlserver/translate.js +495 -0
- package/dist/sqlserver/types.d.ts +44 -0
- package/dist/sqlserver/types.d.ts.map +1 -0
- package/dist/sqlserver/types.js +133 -0
- package/dist/sqlserver/validate.d.ts +31 -0
- package/dist/sqlserver/validate.d.ts.map +1 -0
- package/dist/sqlserver/validate.js +67 -0
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +10 -0
- package/package.json +2 -2
- package/src/change-set.ts +2 -1
- package/src/clickhouse/apply/statements.ts +25 -0
- package/src/clickhouse/import/live-export.ts +20 -5
- package/src/clickhouse/live/named-collections.e2e.test.ts +88 -0
- package/src/clickhouse/live/named-collections.ts +61 -0
- package/src/clickhouse/live/undeclarable.e2e.test.ts +5 -3
- package/src/clickhouse/live/undeclarable.ts +20 -4
- package/src/clickhouse/named-collections.test.ts +187 -0
- package/src/clickhouse/named-collections.ts +224 -0
- package/src/clickhouse/parser.ts +2 -1
- package/src/clickhouse/plan/commands.ts +17 -2
- package/src/clickhouse/plan/diff.ts +57 -1
- package/src/clickhouse/plan/disruption.ts +5 -0
- package/src/clickhouse/plan/rebuild-handoff.ts +3 -0
- package/src/clickhouse/plan/report.ts +6 -1
- package/src/clickhouse/plan/rules.ts +14 -4
- package/src/clickhouse/plan/statements.test.ts +61 -0
- package/src/clickhouse/plan/statements.ts +306 -0
- package/src/clickhouse/plan/stream-s3queue.e2e.test.ts +148 -0
- package/src/clickhouse/plan/stream.test.ts +237 -0
- package/src/clickhouse/plan/stream.ts +111 -0
- package/src/clickhouse/rebuild/observe.ts +15 -1
- package/src/clickhouse/review-catalog.test.ts +181 -0
- package/src/clickhouse/review-catalog.ts +489 -0
- package/src/codegen/classifier-page.ts +44 -4
- package/src/codegen/clickhouse-module.ts +3 -0
- package/src/codegen/coverage-page.ts +5 -3
- package/src/codegen/docs.ts +1 -1
- package/src/codegen/postgres-classifier-page.ts +2 -2
- package/src/codegen/registry.ts +10 -0
- package/src/composites/catalog.test.ts +6 -1
- package/src/composites/catalog.ts +100 -0
- package/src/composites/clickhouse.ts +2 -0
- package/src/composites/composites.test.ts +1 -1
- package/src/composites/otel-tables.test.ts +118 -0
- package/src/composites/otel-tables.ts +339 -0
- package/src/composites/stream-ingest.test.ts +114 -0
- package/src/composites/stream-ingest.ts +82 -0
- package/src/core/statement-class.ts +88 -0
- package/src/core/undeclarable.test.ts +6 -5
- package/src/coverage/clickhouse.json +1 -1
- package/src/coverage/postgres.json +12 -12
- package/src/coverage.test.ts +27 -2
- package/src/coverage.ts +18 -7
- package/src/drizzle-command.ts +94 -0
- package/src/drizzle-kit.e2e.test.ts +89 -0
- package/src/drizzle-types.e2e.test.ts +67 -0
- package/src/drizzle.test.ts +358 -0
- package/src/drizzle.ts +742 -0
- package/src/files/clickhouse.ts +8 -1
- package/src/files/common.ts +14 -1
- package/src/files/postgres.ts +34 -3
- package/src/generated/clickhouse.ts +3 -0
- package/src/generated/lexicon-sql.json +50 -0
- package/src/import-parser.sqlserver.test.ts +92 -0
- package/src/import-parser.ts +79 -5
- package/src/index.ts +1 -1
- package/src/lint/post-synth/audit-entries.ts +1 -0
- package/src/lint/post-synth/index.ts +2 -0
- package/src/lint/post-synth/postgres-checks.test.ts +8 -1
- package/src/lint/post-synth/postgres-names.ts +2 -0
- package/src/lint/post-synth/sqlch128.test.ts +107 -0
- package/src/lint/post-synth/sqlch128.ts +230 -0
- package/src/lint/rules/postgres-templates.ts +2 -2
- package/src/lsp/postgres-template.ts +20 -0
- package/src/mcp/postgres.ts +1 -1
- package/src/migrate/from-postgres/cli.test.ts +165 -0
- package/src/migrate/from-postgres/detect.ts +7 -0
- package/src/migrate/from-postgres/emit.ts +96 -0
- package/src/migrate/from-postgres/example.test.ts +205 -0
- package/src/migrate/from-postgres/index.ts +122 -0
- package/src/migrate/from-postgres/translate.test.ts +242 -0
- package/src/migrate/from-postgres/translate.ts +359 -0
- package/src/migrate/from-postgres/types.test.ts +116 -0
- package/src/migrate/from-postgres/types.ts +236 -0
- package/src/migrate/from-postgres/validate.e2e.test.ts +44 -0
- package/src/migrate/from-postgres/workspace.e2e.test.ts +114 -0
- package/src/migration-classify.test.ts +150 -0
- package/src/migration-classify.ts +45 -0
- package/src/migration-replay.e2e.test.ts +155 -0
- package/src/migration-replay.test.ts +173 -0
- package/src/migration-replay.ts +289 -0
- package/src/op/__snapshots__/watch-pipeline.test.ts.snap +2 -2
- package/src/op/activities/postgres-migration.ts +4 -0
- package/src/op/activity-contracts.ts +2 -0
- package/src/op/watch-pipeline.test.ts +8 -1
- package/src/plugin.ts +51 -2
- package/src/postgres/access/acl.ts +1 -1
- package/src/postgres/access/import.ts +1 -1
- package/src/postgres/apply/apply.ts +14 -1
- package/src/postgres/apply/statements.ts +225 -4
- package/src/postgres/constraint-names.ts +71 -0
- package/src/postgres/database-kinds.test.ts +865 -0
- package/src/postgres/entities.ts +541 -5
- package/src/postgres/entity-types.ts +10 -0
- package/src/postgres/import/generator.ts +54 -2
- package/src/postgres/import/live-export.ts +1 -0
- package/src/postgres/live/catalog-kinds.ts +494 -0
- package/src/postgres/live/catalog.ts +6 -1
- package/src/postgres/live/database-kinds.e2e.test.ts +164 -0
- package/src/postgres/live/describe-resources.ts +13 -2
- package/src/postgres/live/undeclarable.e2e.test.ts +13 -6
- package/src/postgres/migrate/backfill.ts +13 -1
- package/src/postgres/migrate/carry.ts +103 -8
- package/src/postgres/migrate/followups.e2e.test.ts +218 -0
- package/src/postgres/migrate/followups.test.ts +199 -0
- package/src/postgres/migrate/kinds.e2e.test.ts +5 -5
- package/src/postgres/migrate/observe.ts +56 -15
- package/src/postgres/migrate/op.ts +28 -1
- package/src/postgres/migrate/partition.e2e.test.ts +129 -0
- package/src/postgres/migrate/partition.test.ts +206 -0
- package/src/postgres/migrate/replication.ts +66 -0
- package/src/postgres/migrate/steps.ts +157 -12
- package/src/postgres/migrate/subscriber.e2e.test.ts +141 -0
- package/src/postgres/migrate/subscriber.test.ts +59 -0
- package/src/postgres/parser.ts +455 -4
- package/src/postgres/plan/deep.ts +42 -4
- package/src/postgres/plan/diff.ts +137 -1
- package/src/postgres/plan/normalize.ts +277 -1
- package/src/postgres/plan/rules.ts +32 -4
- package/src/postgres/plan/schema.ts +1 -1
- package/src/postgres/plan/statements.test.ts +78 -0
- package/src/postgres/plan/statements.ts +299 -0
- package/src/postgres/secret-options.ts +10 -0
- package/src/postgres/testing/roundtrip-schema.ts +24 -1
- package/src/postgres.ts +33 -0
- package/src/serializer.ts +13 -2
- package/src/skills/chant-sql-postgres-migration.md +4 -3
- package/src/skills/chant-sql-postgres.md +11 -1
- package/src/skills/chant-sql.md +2 -1
- package/src/sql-files.test.ts +33 -0
- package/src/sql-types-command.ts +38 -16
- package/src/sql-types.ts +11 -6
- package/src/sqlserver/fixture.test.ts +186 -0
- package/src/sqlserver/import-cli.test.ts +61 -0
- package/src/sqlserver/lexer.ts +260 -0
- package/src/sqlserver/parse.ts +865 -0
- package/src/sqlserver/translate.test.ts +396 -0
- package/src/sqlserver/translate.ts +558 -0
- package/src/sqlserver/types.test.ts +102 -0
- package/src/sqlserver/types.ts +154 -0
- package/src/sqlserver/validate.e2e.test.ts +32 -0
- package/src/sqlserver/validate.test.ts +49 -0
- package/src/sqlserver/validate.ts +76 -0
- package/src/validate.ts +10 -0
- package/src/workspace-kinds.test.ts +11 -0
- package/workspace-kinds.json +100 -2
|
@@ -18,6 +18,16 @@ export const POSTGRES_ENTITY_TYPES = {
|
|
|
18
18
|
collation: "Postgres::Collation",
|
|
19
19
|
cast: "Postgres::Cast",
|
|
20
20
|
eventTrigger: "Postgres::EventTrigger",
|
|
21
|
+
operator: "Postgres::Operator",
|
|
22
|
+
operatorClass: "Postgres::OperatorClass",
|
|
23
|
+
operatorFamily: "Postgres::OperatorFamily",
|
|
24
|
+
foreignTable: "Postgres::ForeignTable",
|
|
25
|
+
textSearchConfiguration: "Postgres::TextSearchConfiguration",
|
|
26
|
+
textSearchDictionary: "Postgres::TextSearchDictionary",
|
|
27
|
+
statistics: "Postgres::Statistics",
|
|
28
|
+
foreignDataWrapper: "Postgres::ForeignDataWrapper",
|
|
29
|
+
foreignServer: "Postgres::ForeignServer",
|
|
30
|
+
publication: "Postgres::Publication",
|
|
21
31
|
extension: "Postgres::Extension",
|
|
22
32
|
function: "Postgres::Function",
|
|
23
33
|
procedure: "Postgres::Procedure",
|
|
@@ -60,6 +60,16 @@ const TAG: Record<string, string> = {
|
|
|
60
60
|
[POSTGRES_ENTITY_TYPES.collation]: "collation",
|
|
61
61
|
[POSTGRES_ENTITY_TYPES.cast]: "cast",
|
|
62
62
|
[POSTGRES_ENTITY_TYPES.eventTrigger]: "eventTrigger",
|
|
63
|
+
[POSTGRES_ENTITY_TYPES.operator]: "operator",
|
|
64
|
+
[POSTGRES_ENTITY_TYPES.operatorClass]: "operatorClass",
|
|
65
|
+
[POSTGRES_ENTITY_TYPES.operatorFamily]: "operatorFamily",
|
|
66
|
+
[POSTGRES_ENTITY_TYPES.foreignTable]: "foreignTable",
|
|
67
|
+
[POSTGRES_ENTITY_TYPES.textSearchConfiguration]: "textSearchConfiguration",
|
|
68
|
+
[POSTGRES_ENTITY_TYPES.textSearchDictionary]: "textSearchDictionary",
|
|
69
|
+
[POSTGRES_ENTITY_TYPES.statistics]: "statistics",
|
|
70
|
+
[POSTGRES_ENTITY_TYPES.foreignDataWrapper]: "foreignDataWrapper",
|
|
71
|
+
[POSTGRES_ENTITY_TYPES.foreignServer]: "foreignServer",
|
|
72
|
+
[POSTGRES_ENTITY_TYPES.publication]: "publication",
|
|
63
73
|
[POSTGRES_ENTITY_TYPES.extension]: "extension",
|
|
64
74
|
[POSTGRES_ENTITY_TYPES.function]: "func",
|
|
65
75
|
[POSTGRES_ENTITY_TYPES.procedure]: "procedure",
|
|
@@ -81,6 +91,12 @@ const UNREFERENCED: ReadonlySet<string> = new Set([
|
|
|
81
91
|
POSTGRES_ENTITY_TYPES.extension,
|
|
82
92
|
POSTGRES_ENTITY_TYPES.cast,
|
|
83
93
|
POSTGRES_ENTITY_TYPES.eventTrigger,
|
|
94
|
+
POSTGRES_ENTITY_TYPES.operator,
|
|
95
|
+
// An index names a class, but a class and its own family share a name; a configuration shares one with a dictionary, which a mapping names.
|
|
96
|
+
POSTGRES_ENTITY_TYPES.operatorClass,
|
|
97
|
+
POSTGRES_ENTITY_TYPES.textSearchConfiguration,
|
|
98
|
+
POSTGRES_ENTITY_TYPES.statistics,
|
|
99
|
+
POSTGRES_ENTITY_TYPES.publication,
|
|
84
100
|
]);
|
|
85
101
|
|
|
86
102
|
/** The tag each entity type is declared with (`table`, `func`, ...). */
|
|
@@ -131,6 +147,17 @@ function bareSpots(tokens: Token[]): Set<number> {
|
|
|
131
147
|
name(node.function);
|
|
132
148
|
}
|
|
133
149
|
if (node.statement === "eventTrigger") name(node.function);
|
|
150
|
+
if (node.statement === "foreignServer") name(node.wrapper);
|
|
151
|
+
if (node.statement === "foreignTable") {
|
|
152
|
+
for (const c of node.columns) if (c.type) first(c.type.from, c.type.to);
|
|
153
|
+
name(node.server);
|
|
154
|
+
}
|
|
155
|
+
if (node.statement === "operatorClass") first(node.type.from, node.type.to);
|
|
156
|
+
if (node.statement === "foreignDataWrapper") {
|
|
157
|
+
name(node.handler ?? undefined);
|
|
158
|
+
name(node.validator ?? undefined);
|
|
159
|
+
}
|
|
160
|
+
if (node.statement === "statistics") name(node.table);
|
|
134
161
|
if (node.statement === "index" || node.statement === "policy") name(node.table);
|
|
135
162
|
if (node.statement === "trigger") {
|
|
136
163
|
name(node.table);
|
|
@@ -141,6 +168,25 @@ function bareSpots(tokens: Token[]): Set<number> {
|
|
|
141
168
|
return out;
|
|
142
169
|
}
|
|
143
170
|
|
|
171
|
+
/** The tokens that name a schema in a publication's `TABLES IN SCHEMA a, b`, past the first, which follows the word SCHEMA. */
|
|
172
|
+
function schemaSpots(tokens: Token[]): Set<number> {
|
|
173
|
+
const out = new Set<number>();
|
|
174
|
+
let nodes: StatementNode[];
|
|
175
|
+
try {
|
|
176
|
+
nodes = parseStatements(tokens);
|
|
177
|
+
} catch {
|
|
178
|
+
return out;
|
|
179
|
+
}
|
|
180
|
+
for (const node of nodes) {
|
|
181
|
+
if (node.statement !== "publication") continue;
|
|
182
|
+
for (const i of node.items) {
|
|
183
|
+
if (i.kind !== "schema") continue;
|
|
184
|
+
for (let k = i.schema.span.from; k < i.schema.span.to; k++) if (!isTrivia(tokens[k]!)) { out.add(k); break; }
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return out;
|
|
188
|
+
}
|
|
189
|
+
|
|
144
190
|
function templateBody(item: Item, byQualified: Map<string, Item>, schemaExport: Map<string, string>): TemplateBody {
|
|
145
191
|
const tokens = tokenizeText(item.ddl, 0);
|
|
146
192
|
const sig: number[] = [];
|
|
@@ -148,6 +194,7 @@ function templateBody(item: Item, byQualified: Map<string, Item>, schemaExport:
|
|
|
148
194
|
if (!isTrivia(t)) sig.push(i);
|
|
149
195
|
});
|
|
150
196
|
const spots = bareSpots(tokens);
|
|
197
|
+
const schemaAt = item.type === POSTGRES_ENTITY_TYPES.publication ? schemaSpots(tokens) : new Set<number>();
|
|
151
198
|
const replace = new Map<number, { to: number; ref: string }>();
|
|
152
199
|
const isSelf = (target: Item) => target === item || (target.exportName === item.exportName && target.type === item.type);
|
|
153
200
|
for (let k = 0; k < sig.length; k++) {
|
|
@@ -169,10 +216,15 @@ function templateBody(item: Item, byQualified: Map<string, Item>, schemaExport:
|
|
|
169
216
|
}
|
|
170
217
|
const dot = tokens[sig[k + 1] ?? -1];
|
|
171
218
|
const b = tokens[sig[k + 2] ?? -1];
|
|
219
|
+
// An operator's own `schema.symbol`: the schema is the schema's export, though the symbol is no name.
|
|
220
|
+
if (item.type === POSTGRES_ENTITY_TYPES.operator && isName(a) && dot?.kind === "punct" && dot.text === "." && b?.kind === "op" && identValue(a) === item.schema && schemaExport.has(item.schema)) {
|
|
221
|
+
replace.set(sig[k]!, { to: sig[k]! + 1, ref: schemaExport.get(item.schema)! });
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
172
224
|
if (!isName(a) || dot?.kind !== "punct" || dot.text !== "." || !isName(b)) {
|
|
173
225
|
// An extension's `WITH SCHEMA app`, a grant's `ON SCHEMA app`, default privileges' `IN SCHEMA app`.
|
|
174
|
-
const bySchemaWord = item.type === POSTGRES_ENTITY_TYPES.extension || item.type === POSTGRES_ENTITY_TYPES.grant || item.type === POSTGRES_ENTITY_TYPES.defaultPrivileges;
|
|
175
|
-
if (bySchemaWord && isName(a) && prev?.kind === "ident" && prev.text.toUpperCase() === "SCHEMA") {
|
|
226
|
+
const bySchemaWord = item.type === POSTGRES_ENTITY_TYPES.extension || item.type === POSTGRES_ENTITY_TYPES.grant || item.type === POSTGRES_ENTITY_TYPES.defaultPrivileges || schemaAt.size > 0;
|
|
227
|
+
if (bySchemaWord && isName(a) && ((prev?.kind === "ident" && prev.text.toUpperCase() === "SCHEMA") || schemaAt.has(sig[k]!))) {
|
|
176
228
|
const s = schemaExport.get(identValue(a));
|
|
177
229
|
if (s) replace.set(sig[k]!, { to: sig[k]! + 1, ref: s });
|
|
178
230
|
continue;
|
|
@@ -84,6 +84,7 @@ export async function exportResources(options: ExportOptions): Promise<ExportedT
|
|
|
84
84
|
warnings.push(`${o.schema ? `${o.schema}.` : ""}${o.name} is kept by ${o.foreign}; left out, since declaring it would have chant change what that tool owns`);
|
|
85
85
|
continue;
|
|
86
86
|
}
|
|
87
|
+
for (const w of o.warnings ?? []) warnings.push(`${o.schema ? `${o.schema}.` : ""}${o.name}: ${w}`);
|
|
87
88
|
objects.push({ type: o.type, ...(o.schema ? { schema: o.schema } : {}), name: o.name, ddl: o.statement });
|
|
88
89
|
}
|
|
89
90
|
return objectsToIR([...objects, ...access], warnings) as ExportedTemplate;
|
|
@@ -23,6 +23,7 @@ import { notExtensionOwned, scopeSql, type LivePgObject, type SchemaScope } from
|
|
|
23
23
|
import { POSTGRES_ENTITY_TYPES } from "../entity-types";
|
|
24
24
|
import { quoteIdent } from "../keywords";
|
|
25
25
|
import { stripMarker } from "../../core/ownership";
|
|
26
|
+
import { isSecretOption } from "../secret-options";
|
|
26
27
|
|
|
27
28
|
type Row = Record<string, unknown>;
|
|
28
29
|
|
|
@@ -310,3 +311,496 @@ export async function readRoutineKinds(client: PostgresClient, scope: SchemaScop
|
|
|
310
311
|
}
|
|
311
312
|
return out;
|
|
312
313
|
}
|
|
314
|
+
|
|
315
|
+
/** An operator named by another's COMMUTATOR or NEGATOR, as `OPERATOR(schema.symbol)`. */
|
|
316
|
+
const operatorRef = (column: string): string =>
|
|
317
|
+
`(SELECT 'OPERATOR(' || pg_catalog.quote_ident(rn.nspname) || '.' || r.oprname || ')' FROM pg_catalog.pg_operator r JOIN pg_catalog.pg_namespace rn ON rn.oid = r.oprnamespace WHERE r.oid = ${column})`;
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Operators: the ones a schema defines, not the server's own (oids below the
|
|
321
|
+
* first normal object id) or an extension's. Identified by name and operand
|
|
322
|
+
* types, `NONE` for a missing side (`DROP OPERATOR`'s spelling).
|
|
323
|
+
*/
|
|
324
|
+
export async function readOperators(client: PostgresClient, scope: SchemaScope): Promise<LivePgObject[]> {
|
|
325
|
+
const s = scopeSql("n.nspname", scope);
|
|
326
|
+
const rows = await client.query<Row>(
|
|
327
|
+
`SELECT o.oid::text AS oid, n.nspname AS schema, o.oprname AS name,
|
|
328
|
+
CASE WHEN o.oprleft <> 0 THEN pg_catalog.format_type(o.oprleft, NULL) END AS leftarg,
|
|
329
|
+
CASE WHEN o.oprright <> 0 THEN pg_catalog.format_type(o.oprright, NULL) END AS rightarg,
|
|
330
|
+
${proc("o.oprcode::pg_catalog.regproc")} AS func, ${operatorRef("o.oprcom")} AS commutator, ${operatorRef("o.oprnegate")} AS negator,
|
|
331
|
+
${proc("o.oprrest::pg_catalog.regproc")} AS restrict, ${proc("o.oprjoin::pg_catalog.regproc")} AS join_func,
|
|
332
|
+
o.oprcanhash AS hashes, o.oprcanmerge AS merges,
|
|
333
|
+
pg_catalog.obj_description(o.oid, 'pg_operator') AS comment, ${dependentsOf("pg_operator", "o.oid")} AS dependents
|
|
334
|
+
FROM pg_catalog.pg_operator o JOIN pg_catalog.pg_namespace n ON n.oid = o.oprnamespace
|
|
335
|
+
WHERE ${s.sql} AND o.oid >= 16384 AND ${notExtensionOwned("pg_operator", "o.oid")}
|
|
336
|
+
ORDER BY n.nspname ${C}, o.oprname ${C}, pg_catalog.format_type(o.oprleft, NULL) ${C} NULLS FIRST, pg_catalog.format_type(o.oprright, NULL) ${C} NULLS FIRST`,
|
|
337
|
+
s.params,
|
|
338
|
+
);
|
|
339
|
+
return rows.map((r) => {
|
|
340
|
+
const left = str(r.leftarg);
|
|
341
|
+
const right = str(r.rightarg);
|
|
342
|
+
const signature = `(${left ?? "NONE"},${right ?? "NONE"})`;
|
|
343
|
+
const name = `${quoteIdent(String(r.schema))}.${r.name}`;
|
|
344
|
+
const opts = [`FUNCTION = ${r.func}`];
|
|
345
|
+
if (left) opts.push(`LEFTARG = ${left}`);
|
|
346
|
+
if (right) opts.push(`RIGHTARG = ${right}`);
|
|
347
|
+
if (str(r.commutator)) opts.push(`COMMUTATOR = ${r.commutator}`);
|
|
348
|
+
if (str(r.negator)) opts.push(`NEGATOR = ${r.negator}`);
|
|
349
|
+
if (str(r.restrict)) opts.push(`RESTRICT = ${r.restrict}`);
|
|
350
|
+
if (str(r.join_func)) opts.push(`JOIN = ${r.join_func}`);
|
|
351
|
+
if (r.hashes === true) opts.push("HASHES");
|
|
352
|
+
if (r.merges === true) opts.push("MERGES");
|
|
353
|
+
return {
|
|
354
|
+
type: POSTGRES_ENTITY_TYPES.operator,
|
|
355
|
+
schema: String(r.schema),
|
|
356
|
+
name: String(r.name),
|
|
357
|
+
oid: String(r.oid),
|
|
358
|
+
signature,
|
|
359
|
+
...withComment(r),
|
|
360
|
+
...dependents(r),
|
|
361
|
+
statement: [`CREATE OPERATOR ${name} (${opts.join(", ")})`, ...commentOn(`OPERATOR ${name}${signature}`, r.comment)].join(";\n"),
|
|
362
|
+
};
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/** An `OPTIONS (...)` clause from a catalog `text[]` of `name=value`, and the names left out because they hold a credential. */
|
|
367
|
+
function optionsOf(raw: unknown): { clause: string; omitted: string[] } {
|
|
368
|
+
const kept: string[] = [];
|
|
369
|
+
const omitted: string[] = [];
|
|
370
|
+
for (const entry of (raw as string[] | null) ?? []) {
|
|
371
|
+
const at = entry.indexOf("=");
|
|
372
|
+
const name = at < 0 ? entry : entry.slice(0, at);
|
|
373
|
+
if (isSecretOption(name)) omitted.push(name);
|
|
374
|
+
else kept.push(`${quoteIdent(name)} ${literal(at < 0 ? "" : entry.slice(at + 1))}`);
|
|
375
|
+
}
|
|
376
|
+
return { clause: kept.length > 0 ? ` OPTIONS (${kept.join(", ")})` : "", omitted };
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const omittedWarning = (omitted: readonly string[]): { warnings?: string[] } =>
|
|
380
|
+
omitted.length > 0 ? { warnings: [`the option${omitted.length === 1 ? "" : "s"} ${omitted.join(", ")} hold${omitted.length === 1 ? "s" : ""} a credential and ${omitted.length === 1 ? "is" : "are"} left out; the environment supplies it`] } : {};
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* The kinds of object #3911 adds that belong to the database or hang off a
|
|
384
|
+
* table: extended statistics (printed by the server), foreign data wrappers
|
|
385
|
+
* and servers, and publications, each assembled from its catalog rows. A
|
|
386
|
+
* wrapper, server or publication is the database's, so the schema scope does
|
|
387
|
+
* not narrow it. A credential in an option is left out and said so.
|
|
388
|
+
*/
|
|
389
|
+
export async function readDatabaseKinds(client: PostgresClient, scope: SchemaScope): Promise<LivePgObject[]> {
|
|
390
|
+
const out: LivePgObject[] = [...(await readOperators(client, scope))];
|
|
391
|
+
|
|
392
|
+
const s = scopeSql("n.nspname", scope);
|
|
393
|
+
const stats = await client.query<Row>(
|
|
394
|
+
`SELECT e.oid::text AS oid, n.nspname AS schema, e.stxname AS name, pg_catalog.pg_get_statisticsobjdef(e.oid) AS def,
|
|
395
|
+
pg_catalog.obj_description(e.oid, 'pg_statistic_ext') AS comment
|
|
396
|
+
FROM pg_catalog.pg_statistic_ext e JOIN pg_catalog.pg_namespace n ON n.oid = e.stxnamespace
|
|
397
|
+
WHERE ${s.sql} AND ${notExtensionOwned("pg_statistic_ext", "e.oid")}
|
|
398
|
+
ORDER BY n.nspname ${C}, e.stxname ${C}`,
|
|
399
|
+
s.params,
|
|
400
|
+
);
|
|
401
|
+
for (const r of stats) {
|
|
402
|
+
const q = qname(String(r.schema), String(r.name));
|
|
403
|
+
out.push({
|
|
404
|
+
type: POSTGRES_ENTITY_TYPES.statistics,
|
|
405
|
+
schema: String(r.schema),
|
|
406
|
+
name: String(r.name),
|
|
407
|
+
oid: String(r.oid),
|
|
408
|
+
...withComment(r),
|
|
409
|
+
statement: [String(r.def), ...commentOn(`STATISTICS ${q}`, r.comment)].join(";\n"),
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
const wrappers = await client.query<Row>(
|
|
414
|
+
`SELECT w.oid::text AS oid, w.fdwname AS name, ${proc("w.fdwhandler::pg_catalog.regproc")} AS handler, ${proc("w.fdwvalidator::pg_catalog.regproc")} AS validator,
|
|
415
|
+
w.fdwoptions AS options, pg_catalog.obj_description(w.oid, 'pg_foreign_data_wrapper') AS comment, ${dependentsOf("pg_foreign_data_wrapper", "w.oid")} AS dependents
|
|
416
|
+
FROM pg_catalog.pg_foreign_data_wrapper w
|
|
417
|
+
WHERE ${notExtensionOwned("pg_foreign_data_wrapper", "w.oid")}
|
|
418
|
+
ORDER BY w.fdwname ${C}`,
|
|
419
|
+
);
|
|
420
|
+
for (const r of wrappers) {
|
|
421
|
+
const name = quoteIdent(String(r.name));
|
|
422
|
+
const { clause, omitted } = optionsOf(r.options);
|
|
423
|
+
out.push({
|
|
424
|
+
type: POSTGRES_ENTITY_TYPES.foreignDataWrapper,
|
|
425
|
+
name: String(r.name),
|
|
426
|
+
oid: String(r.oid),
|
|
427
|
+
...withComment(r),
|
|
428
|
+
...dependents(r),
|
|
429
|
+
...omittedWarning(omitted),
|
|
430
|
+
statement: [
|
|
431
|
+
`CREATE FOREIGN DATA WRAPPER ${name}${str(r.handler) ? ` HANDLER ${r.handler}` : ""}${str(r.validator) ? ` VALIDATOR ${r.validator}` : ""}${clause}`,
|
|
432
|
+
...commentOn(`FOREIGN DATA WRAPPER ${name}`, r.comment),
|
|
433
|
+
].join(";\n"),
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
const servers = await client.query<Row>(
|
|
438
|
+
`SELECT v.oid::text AS oid, v.srvname AS name, v.srvtype AS type, v.srvversion AS version, w.fdwname AS wrapper, v.srvoptions AS options,
|
|
439
|
+
pg_catalog.obj_description(v.oid, 'pg_foreign_server') AS comment, ${dependentsOf("pg_foreign_server", "v.oid")} AS dependents
|
|
440
|
+
FROM pg_catalog.pg_foreign_server v JOIN pg_catalog.pg_foreign_data_wrapper w ON w.oid = v.srvfdw
|
|
441
|
+
WHERE ${notExtensionOwned("pg_foreign_server", "v.oid")}
|
|
442
|
+
ORDER BY v.srvname ${C}`,
|
|
443
|
+
);
|
|
444
|
+
for (const r of servers) {
|
|
445
|
+
const name = quoteIdent(String(r.name));
|
|
446
|
+
const { clause, omitted } = optionsOf(r.options);
|
|
447
|
+
out.push({
|
|
448
|
+
type: POSTGRES_ENTITY_TYPES.foreignServer,
|
|
449
|
+
name: String(r.name),
|
|
450
|
+
oid: String(r.oid),
|
|
451
|
+
...withComment(r),
|
|
452
|
+
...dependents(r),
|
|
453
|
+
...omittedWarning(omitted),
|
|
454
|
+
statement: [
|
|
455
|
+
`CREATE SERVER ${name}${str(r.type) ? ` TYPE ${literal(String(r.type))}` : ""}${str(r.version) ? ` VERSION ${literal(String(r.version))}` : ""} FOREIGN DATA WRAPPER ${quoteIdent(String(r.wrapper))}${clause}`,
|
|
456
|
+
...commentOn(`SERVER ${name}`, r.comment),
|
|
457
|
+
].join(";\n"),
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
out.push(...(await readPublications(client)));
|
|
462
|
+
out.push(...(await readOperatorClasses(client, scope)), ...(await readTextSearch(client, scope)), ...(await readForeignTables(client, scope)));
|
|
463
|
+
return out;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/** Publications. Column lists, row filters and schema members are 15's; they are read where the catalog has them. */
|
|
467
|
+
async function readPublications(client: PostgresClient): Promise<LivePgObject[]> {
|
|
468
|
+
const has = async (catalog: string, column?: string): Promise<boolean> => {
|
|
469
|
+
const rows = await client.query<Row>(
|
|
470
|
+
column
|
|
471
|
+
? `SELECT 1 AS x FROM pg_catalog.pg_attribute WHERE attrelid = to_regclass('pg_catalog.${catalog}') AND attname = '${column}' AND NOT attisdropped`
|
|
472
|
+
: `SELECT 1 AS x WHERE to_regclass('pg_catalog.${catalog}') IS NOT NULL`,
|
|
473
|
+
);
|
|
474
|
+
return rows.length > 0;
|
|
475
|
+
};
|
|
476
|
+
const columnLists = await has("pg_publication_rel", "prattrs");
|
|
477
|
+
const schemaMembers = await has("pg_publication_namespace");
|
|
478
|
+
|
|
479
|
+
const pubs = await client.query<Row>(
|
|
480
|
+
`SELECT p.oid::text AS oid, p.pubname AS name, p.puballtables AS alltables, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate,
|
|
481
|
+
pg_catalog.to_jsonb(p) AS def, pg_catalog.obj_description(p.oid, 'pg_publication') AS comment
|
|
482
|
+
FROM pg_catalog.pg_publication p
|
|
483
|
+
WHERE ${notExtensionOwned("pg_publication", "p.oid")}
|
|
484
|
+
ORDER BY p.pubname ${C}`,
|
|
485
|
+
);
|
|
486
|
+
const out: LivePgObject[] = [];
|
|
487
|
+
for (const r of pubs) {
|
|
488
|
+
const name = quoteIdent(String(r.name));
|
|
489
|
+
const d = (typeof r.def === "string" ? JSON.parse(r.def) : r.def) as Record<string, unknown>;
|
|
490
|
+
const tables = r.alltables
|
|
491
|
+
? []
|
|
492
|
+
: await client.query<Row>(
|
|
493
|
+
`SELECT n.nspname AS schema, c.relname AS name,
|
|
494
|
+
${columnLists ? "ARRAY(SELECT pg_catalog.quote_ident(a.attname) FROM pg_catalog.pg_attribute a WHERE a.attrelid = r.prrelid AND a.attnum = ANY(r.prattrs::pg_catalog.int2[]) ORDER BY a.attnum)" : "NULL::text[]"} AS columns,
|
|
495
|
+
${columnLists ? "pg_catalog.pg_get_expr(r.prqual, r.prrelid)" : "NULL::text"} AS filter
|
|
496
|
+
FROM pg_catalog.pg_publication_rel r JOIN pg_catalog.pg_class c ON c.oid = r.prrelid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
497
|
+
WHERE r.prpubid = $1::oid ORDER BY n.nspname ${C}, c.relname ${C}`,
|
|
498
|
+
[String(r.oid)],
|
|
499
|
+
);
|
|
500
|
+
const schemas =
|
|
501
|
+
schemaMembers && !r.alltables
|
|
502
|
+
? await client.query<Row>(
|
|
503
|
+
`SELECT n.nspname AS name FROM pg_catalog.pg_publication_namespace pn JOIN pg_catalog.pg_namespace n ON n.oid = pn.pnnspid WHERE pn.pnpubid = $1::oid ORDER BY n.nspname ${C}`,
|
|
504
|
+
[String(r.oid)],
|
|
505
|
+
)
|
|
506
|
+
: [];
|
|
507
|
+
const members = [
|
|
508
|
+
...tables.map((t) => {
|
|
509
|
+
const columns = (t.columns as string[] | null) ?? [];
|
|
510
|
+
return `${qname(String(t.schema), String(t.name))}${columns.length > 0 ? ` (${columns.join(", ")})` : ""}${str(t.filter) ? ` WHERE (${t.filter})` : ""}`;
|
|
511
|
+
}),
|
|
512
|
+
];
|
|
513
|
+
const clauses: string[] = [];
|
|
514
|
+
if (r.alltables) clauses.push("FOR ALL TABLES");
|
|
515
|
+
else if (members.length > 0 || schemas.length > 0) {
|
|
516
|
+
const parts: string[] = [];
|
|
517
|
+
if (members.length > 0) parts.push(`TABLE ${members.join(", ")}`);
|
|
518
|
+
if (schemas.length > 0) parts.push(`TABLES IN SCHEMA ${schemas.map((x) => quoteIdent(String(x.name))).join(", ")}`);
|
|
519
|
+
clauses.push(`FOR ${parts.join(", ")}`);
|
|
520
|
+
}
|
|
521
|
+
const publish = [r.pubinsert && "insert", r.pubupdate && "update", r.pubdelete && "delete", r.pubtruncate && "truncate"].filter(Boolean);
|
|
522
|
+
const options: string[] = [];
|
|
523
|
+
if (publish.length !== 4) options.push(`publish = ${literal(publish.join(", "))}`);
|
|
524
|
+
if (d.pubviaroot === true) options.push("publish_via_partition_root = true");
|
|
525
|
+
if (d.pubgencols !== undefined && d.pubgencols !== "n") options.push("publish_generated_columns = stored");
|
|
526
|
+
if (options.length > 0) clauses.push(`WITH (${options.join(", ")})`);
|
|
527
|
+
out.push({
|
|
528
|
+
type: POSTGRES_ENTITY_TYPES.publication,
|
|
529
|
+
name: String(r.name),
|
|
530
|
+
oid: String(r.oid),
|
|
531
|
+
...withComment(r),
|
|
532
|
+
statement: [`CREATE PUBLICATION ${name}${clauses.length > 0 ? ` ${clauses.join(" ")}` : ""}`, ...commentOn(`PUBLICATION ${name}`, r.comment)].join(";\n"),
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
return out;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// ── Operator classes and families, text search, foreign tables (#4406) ──
|
|
539
|
+
|
|
540
|
+
/** An object named by `schema`.`name` columns, as a clause: a `pg_catalog` one bare. */
|
|
541
|
+
const refName = (schema: string, name: string): string =>
|
|
542
|
+
`CASE WHEN ${schema} = 'pg_catalog' THEN pg_catalog.quote_ident(${name}) ELSE pg_catalog.quote_ident(${schema}) || '.' || pg_catalog.quote_ident(${name}) END`;
|
|
543
|
+
|
|
544
|
+
/** An operand type, `NONE` for the missing side of a prefix operator. */
|
|
545
|
+
const operand = (column: string): string => `CASE WHEN ${column} = 0 THEN 'NONE' ELSE pg_catalog.format_type(${column}, NULL) END`;
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Operator classes and families. A class carries its members: the operators
|
|
549
|
+
* and support functions that depend on it (`pg_depend`, a member made by
|
|
550
|
+
* CREATE OPERATOR CLASS depends on the class; one added by ALTER OPERATOR
|
|
551
|
+
* FAMILY ... ADD depends on the family and is "loose"). A family named as its
|
|
552
|
+
* class, in the same schema, is the one CREATE OPERATOR CLASS made for it and
|
|
553
|
+
* is the class's; every other family is read, with a warning when it holds
|
|
554
|
+
* loose members, which a declaration cannot carry.
|
|
555
|
+
*/
|
|
556
|
+
async function readOperatorClasses(client: PostgresClient, scope: SchemaScope): Promise<LivePgObject[]> {
|
|
557
|
+
const out: LivePgObject[] = [];
|
|
558
|
+
const s = scopeSql("n.nspname", scope);
|
|
559
|
+
const member = (catalog: string, alias: string) =>
|
|
560
|
+
`JOIN pg_catalog.pg_depend dep ON dep.classid = '${catalog}'::pg_catalog.regclass AND dep.objid = ${alias}.oid AND dep.refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass AND dep.refobjid = c.oid`;
|
|
561
|
+
const classes = await client.query<Row>(
|
|
562
|
+
`SELECT c.oid::text AS oid, n.nspname AS schema, c.opcname AS name, am.amname AS method, c.opcdefault AS isdefault,
|
|
563
|
+
pg_catalog.format_type(c.opcintype, NULL) AS type,
|
|
564
|
+
CASE WHEN c.opckeytype <> 0 THEN pg_catalog.format_type(c.opckeytype, NULL) END AS storage,
|
|
565
|
+
CASE WHEN f.opfname = c.opcname AND f.opfnamespace = c.opcnamespace THEN NULL ELSE ${refName("fn.nspname", "f.opfname")} END AS family,
|
|
566
|
+
ARRAY(SELECT 'OPERATOR ' || o.amopstrategy || ' ' || pg_catalog.quote_ident(opn.nspname) || '.' || op.oprname ||
|
|
567
|
+
' (' || ${operand("o.amoplefttype")} || ', ' || ${operand("o.amoprighttype")} || ')' ||
|
|
568
|
+
CASE WHEN o.amoppurpose = 'o' THEN ' FOR ORDER BY ' || (SELECT pg_catalog.quote_ident(sn.nspname) || '.' || pg_catalog.quote_ident(sf.opfname)
|
|
569
|
+
FROM pg_catalog.pg_opfamily sf JOIN pg_catalog.pg_namespace sn ON sn.oid = sf.opfnamespace WHERE sf.oid = o.amopsortfamily) ELSE '' END
|
|
570
|
+
FROM pg_catalog.pg_amop o ${member("pg_catalog.pg_amop", "o")}
|
|
571
|
+
JOIN pg_catalog.pg_operator op ON op.oid = o.amopopr JOIN pg_catalog.pg_namespace opn ON opn.oid = op.oprnamespace
|
|
572
|
+
ORDER BY o.amopstrategy, o.amoplefttype, o.amoprighttype) AS operators,
|
|
573
|
+
ARRAY(SELECT 'FUNCTION ' || p.amprocnum || ' (' || ${operand("p.amproclefttype")} || ', ' || ${operand("p.amprocrighttype")} || ') ' || p.amproc::pg_catalog.regprocedure::pg_catalog.text
|
|
574
|
+
FROM pg_catalog.pg_amproc p ${member("pg_catalog.pg_amproc", "p")}
|
|
575
|
+
ORDER BY p.amprocnum, p.amproclefttype, p.amprocrighttype) AS functions,
|
|
576
|
+
pg_catalog.obj_description(c.oid, 'pg_opclass') AS comment, ${dependentsOf("pg_opclass", "c.oid")} AS dependents
|
|
577
|
+
FROM pg_catalog.pg_opclass c JOIN pg_catalog.pg_namespace n ON n.oid = c.opcnamespace JOIN pg_catalog.pg_am am ON am.oid = c.opcmethod
|
|
578
|
+
JOIN pg_catalog.pg_opfamily f ON f.oid = c.opcfamily JOIN pg_catalog.pg_namespace fn ON fn.oid = f.opfnamespace
|
|
579
|
+
WHERE ${s.sql} AND c.oid >= 16384 AND ${notExtensionOwned("pg_opclass", "c.oid")}
|
|
580
|
+
ORDER BY n.nspname ${C}, c.opcname ${C}, am.amname ${C}`,
|
|
581
|
+
s.params,
|
|
582
|
+
);
|
|
583
|
+
for (const r of classes) {
|
|
584
|
+
const q = qname(String(r.schema), String(r.name));
|
|
585
|
+
const method = String(r.method);
|
|
586
|
+
const items = [...((r.operators as string[] | null) ?? []), ...((r.functions as string[] | null) ?? []), ...(str(r.storage) ? [`STORAGE ${r.storage}`] : [])];
|
|
587
|
+
out.push({
|
|
588
|
+
type: POSTGRES_ENTITY_TYPES.operatorClass,
|
|
589
|
+
schema: String(r.schema),
|
|
590
|
+
name: String(r.name),
|
|
591
|
+
oid: String(r.oid),
|
|
592
|
+
signature: ` USING ${method}`,
|
|
593
|
+
...withComment(r),
|
|
594
|
+
...dependents(r),
|
|
595
|
+
...(items.length === 0 ? { unsupported: "no operator, support function or storage type of it could be read; a CREATE OPERATOR CLASS needs at least one" } : {}),
|
|
596
|
+
statement: [
|
|
597
|
+
`CREATE OPERATOR CLASS ${q}${r.isdefault === true ? " DEFAULT" : ""} FOR TYPE ${r.type} USING ${method}${str(r.family) ? ` FAMILY ${r.family}` : ""} AS\n ${items.join(",\n ")}`,
|
|
598
|
+
...commentOn(`OPERATOR CLASS ${q} USING ${method}`, r.comment),
|
|
599
|
+
].join(";\n"),
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
const families = await client.query<Row>(
|
|
604
|
+
`SELECT f.oid::text AS oid, n.nspname AS schema, f.opfname AS name, am.amname AS method,
|
|
605
|
+
(SELECT count(*) FROM (SELECT o.oid FROM pg_catalog.pg_amop o WHERE o.amopfamily = f.oid UNION ALL SELECT p.oid FROM pg_catalog.pg_amproc p WHERE p.amprocfamily = f.oid) m
|
|
606
|
+
WHERE NOT EXISTS (SELECT 1 FROM pg_catalog.pg_depend dep WHERE dep.classid IN ('pg_catalog.pg_amop'::pg_catalog.regclass, 'pg_catalog.pg_amproc'::pg_catalog.regclass)
|
|
607
|
+
AND dep.objid = m.oid AND dep.refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass)) AS loose,
|
|
608
|
+
pg_catalog.obj_description(f.oid, 'pg_opfamily') AS comment, ${dependentsOf("pg_opfamily", "f.oid")} AS dependents
|
|
609
|
+
FROM pg_catalog.pg_opfamily f JOIN pg_catalog.pg_namespace n ON n.oid = f.opfnamespace JOIN pg_catalog.pg_am am ON am.oid = f.opfmethod
|
|
610
|
+
WHERE ${s.sql} AND f.oid >= 16384 AND ${notExtensionOwned("pg_opfamily", "f.oid")}
|
|
611
|
+
AND NOT EXISTS (SELECT 1 FROM pg_catalog.pg_opclass c WHERE c.opcfamily = f.oid AND c.opcname = f.opfname AND c.opcnamespace = f.opfnamespace)
|
|
612
|
+
ORDER BY n.nspname ${C}, f.opfname ${C}, am.amname ${C}`,
|
|
613
|
+
s.params,
|
|
614
|
+
);
|
|
615
|
+
for (const r of families) {
|
|
616
|
+
const q = qname(String(r.schema), String(r.name));
|
|
617
|
+
const method = String(r.method);
|
|
618
|
+
const loose = Number(r.loose ?? 0);
|
|
619
|
+
out.push({
|
|
620
|
+
type: POSTGRES_ENTITY_TYPES.operatorFamily,
|
|
621
|
+
schema: String(r.schema),
|
|
622
|
+
name: String(r.name),
|
|
623
|
+
oid: String(r.oid),
|
|
624
|
+
signature: ` USING ${method}`,
|
|
625
|
+
...withComment(r),
|
|
626
|
+
...dependents(r),
|
|
627
|
+
...(loose > 0 ? { warnings: [`${loose} loose member${loose === 1 ? "" : "s"} added with ALTER OPERATOR FAMILY ... ADD ${loose === 1 ? "is" : "are"} left out; a declaration holds the family and its classes' members`] } : {}),
|
|
628
|
+
statement: [`CREATE OPERATOR FAMILY ${q} USING ${method}`, ...commentOn(`OPERATOR FAMILY ${q} USING ${method}`, r.comment)].join(";\n"),
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
return out;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* A dictionary's `dictinitoption`, which the server prints as
|
|
636
|
+
* `name = 'value', ...` (a quote and a backslash doubled in the value), as a
|
|
637
|
+
* list of name and value; undefined when the text is not that form.
|
|
638
|
+
*/
|
|
639
|
+
export function parseDictionaryOptions(init: string | undefined): Array<[string, string]> | undefined {
|
|
640
|
+
if (init === undefined || init.trim() === "") return [];
|
|
641
|
+
const out: Array<[string, string]> = [];
|
|
642
|
+
const item = /\s*("(?:[^"]|"")+"|[^\s=,]+)\s*=\s*'((?:[^']|'')*)'\s*(?:,|$)/y;
|
|
643
|
+
let at = 0;
|
|
644
|
+
while (at < init.length) {
|
|
645
|
+
item.lastIndex = at;
|
|
646
|
+
const m = item.exec(init);
|
|
647
|
+
if (!m) return undefined;
|
|
648
|
+
const name = m[1]!.startsWith('"') ? m[1]!.slice(1, -1).replace(/""/g, '"') : m[1]!;
|
|
649
|
+
out.push([name, m[2]!.replace(/''/g, "'").replace(/\\\\/g, "\\")]);
|
|
650
|
+
at = item.lastIndex;
|
|
651
|
+
}
|
|
652
|
+
return out;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/** Text search dictionaries and configurations; a configuration's mappings are the ALTER ... ADD MAPPING statements after its CREATE. */
|
|
656
|
+
async function readTextSearch(client: PostgresClient, scope: SchemaScope): Promise<LivePgObject[]> {
|
|
657
|
+
const out: LivePgObject[] = [];
|
|
658
|
+
const s = scopeSql("n.nspname", scope);
|
|
659
|
+
const dictionaries = await client.query<Row>(
|
|
660
|
+
`SELECT d.oid::text AS oid, n.nspname AS schema, d.dictname AS name, d.dictinitoption AS init,
|
|
661
|
+
(SELECT ${refName("tn.nspname", "t.tmplname")} FROM pg_catalog.pg_ts_template t JOIN pg_catalog.pg_namespace tn ON tn.oid = t.tmplnamespace WHERE t.oid = d.dicttemplate) AS template,
|
|
662
|
+
pg_catalog.obj_description(d.oid, 'pg_ts_dict') AS comment, ${dependentsOf("pg_ts_dict", "d.oid")} AS dependents
|
|
663
|
+
FROM pg_catalog.pg_ts_dict d JOIN pg_catalog.pg_namespace n ON n.oid = d.dictnamespace
|
|
664
|
+
WHERE ${s.sql} AND d.oid >= 16384 AND ${notExtensionOwned("pg_ts_dict", "d.oid")}
|
|
665
|
+
ORDER BY n.nspname ${C}, d.dictname ${C}`,
|
|
666
|
+
s.params,
|
|
667
|
+
);
|
|
668
|
+
for (const r of dictionaries) {
|
|
669
|
+
const q = qname(String(r.schema), String(r.name));
|
|
670
|
+
const options = parseDictionaryOptions(str(r.init));
|
|
671
|
+
out.push({
|
|
672
|
+
type: POSTGRES_ENTITY_TYPES.textSearchDictionary,
|
|
673
|
+
schema: String(r.schema),
|
|
674
|
+
name: String(r.name),
|
|
675
|
+
oid: String(r.oid),
|
|
676
|
+
...withComment(r),
|
|
677
|
+
...dependents(r),
|
|
678
|
+
...(options === undefined ? { unsupported: `its options (${str(r.init)}) are not in the form the server writes them` } : {}),
|
|
679
|
+
statement: [
|
|
680
|
+
`CREATE TEXT SEARCH DICTIONARY ${q} (${["TEMPLATE = " + r.template, ...(options ?? []).map(([k, v]) => `${quoteIdent(k)} = ${literal(v)}`)].join(", ")})`,
|
|
681
|
+
...commentOn(`TEXT SEARCH DICTIONARY ${q}`, r.comment),
|
|
682
|
+
].join(";\n"),
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
const configurations = await client.query<Row>(
|
|
687
|
+
`SELECT c.oid::text AS oid, n.nspname AS schema, c.cfgname AS name,
|
|
688
|
+
(SELECT ${refName("pn.nspname", "p.prsname")} FROM pg_catalog.pg_ts_parser p JOIN pg_catalog.pg_namespace pn ON pn.oid = p.prsnamespace WHERE p.oid = c.cfgparser) AS parser,
|
|
689
|
+
ARRAY(SELECT tt.alias || E'\\t' || pg_catalog.string_agg((SELECT ${refName("dn.nspname", "d.dictname")} FROM pg_catalog.pg_ts_dict d JOIN pg_catalog.pg_namespace dn ON dn.oid = d.dictnamespace WHERE d.oid = m.mapdict), ', ' ORDER BY m.mapseqno)
|
|
690
|
+
FROM pg_catalog.pg_ts_config_map m, pg_catalog.ts_token_type(c.cfgparser) tt WHERE m.mapcfg = c.oid AND tt.tokid = m.maptokentype
|
|
691
|
+
GROUP BY tt.tokid, tt.alias ORDER BY tt.tokid) AS mappings,
|
|
692
|
+
pg_catalog.obj_description(c.oid, 'pg_ts_config') AS comment, ${dependentsOf("pg_ts_config", "c.oid")} AS dependents
|
|
693
|
+
FROM pg_catalog.pg_ts_config c JOIN pg_catalog.pg_namespace n ON n.oid = c.cfgnamespace
|
|
694
|
+
WHERE ${s.sql} AND c.oid >= 16384 AND ${notExtensionOwned("pg_ts_config", "c.oid")}
|
|
695
|
+
ORDER BY n.nspname ${C}, c.cfgname ${C}`,
|
|
696
|
+
s.params,
|
|
697
|
+
);
|
|
698
|
+
for (const r of configurations) {
|
|
699
|
+
const q = qname(String(r.schema), String(r.name));
|
|
700
|
+
// Token types that share their dictionaries share an ADD MAPPING, in the parser's token order.
|
|
701
|
+
const groups = new Map<string, string[]>();
|
|
702
|
+
for (const line of (r.mappings as string[] | null) ?? []) {
|
|
703
|
+
const [token, dictionaries] = line.split("\t") as [string, string];
|
|
704
|
+
groups.set(dictionaries, [...(groups.get(dictionaries) ?? []), quoteIdent(token)]);
|
|
705
|
+
}
|
|
706
|
+
out.push({
|
|
707
|
+
type: POSTGRES_ENTITY_TYPES.textSearchConfiguration,
|
|
708
|
+
schema: String(r.schema),
|
|
709
|
+
name: String(r.name),
|
|
710
|
+
oid: String(r.oid),
|
|
711
|
+
...withComment(r),
|
|
712
|
+
...dependents(r),
|
|
713
|
+
statement: [
|
|
714
|
+
`CREATE TEXT SEARCH CONFIGURATION ${q} (PARSER = ${r.parser})`,
|
|
715
|
+
...[...groups].map(([dicts, tokens]) => `ALTER TEXT SEARCH CONFIGURATION ${q} ADD MAPPING FOR ${tokens.join(", ")} WITH ${dicts}`),
|
|
716
|
+
...commentOn(`TEXT SEARCH CONFIGURATION ${q}`, r.comment),
|
|
717
|
+
].join(";\n"),
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
return out;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
/**
|
|
724
|
+
* Foreign tables: their columns (type, collation, default, NOT NULL and
|
|
725
|
+
* OPTIONS), CHECK constraints, server and OPTIONS. One that is a partition,
|
|
726
|
+
* inherits or has a generated column is read as unsupported, since a
|
|
727
|
+
* declaration does not hold those. An option that holds a credential is left
|
|
728
|
+
* out and said so.
|
|
729
|
+
*/
|
|
730
|
+
async function readForeignTables(client: PostgresClient, scope: SchemaScope): Promise<LivePgObject[]> {
|
|
731
|
+
const s = scopeSql("n.nspname", scope);
|
|
732
|
+
const tables = await client.query<Row>(
|
|
733
|
+
`SELECT c.oid::text AS oid, n.nspname AS schema, c.relname AS name, c.relispartition AS ispartition,
|
|
734
|
+
EXISTS (SELECT 1 FROM pg_catalog.pg_inherits i WHERE i.inhrelid = c.oid) AS inherits,
|
|
735
|
+
fs.srvname AS server, ft.ftoptions AS options,
|
|
736
|
+
pg_catalog.obj_description(c.oid, 'pg_class') AS comment, ${dependentsOf("pg_class", "c.oid")} AS dependents
|
|
737
|
+
FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
738
|
+
JOIN pg_catalog.pg_foreign_table ft ON ft.ftrelid = c.oid JOIN pg_catalog.pg_foreign_server fs ON fs.oid = ft.ftserver
|
|
739
|
+
WHERE c.relkind = 'f' AND ${s.sql} AND ${notExtensionOwned("pg_class", "c.oid")}
|
|
740
|
+
ORDER BY n.nspname ${C}, c.relname ${C}`,
|
|
741
|
+
s.params,
|
|
742
|
+
);
|
|
743
|
+
if (tables.length === 0) return [];
|
|
744
|
+
const oids = tables.map((t) => String(t.oid));
|
|
745
|
+
const columns = await client.query<Row>(
|
|
746
|
+
`SELECT a.attrelid::text AS rel, a.attname AS name, pg_catalog.format_type(a.atttypid, a.atttypmod) AS type, a.attnotnull AS notnull,
|
|
747
|
+
a.attgenerated::text AS generated, pg_catalog.pg_get_expr(d.adbin, d.adrelid, true) AS dflt,
|
|
748
|
+
CASE WHEN a.attcollation <> 0 AND a.attcollation <> t.typcollation THEN ${collationName("a.attcollation")} END AS collation,
|
|
749
|
+
a.attfdwoptions AS options, pg_catalog.col_description(a.attrelid, a.attnum) AS comment
|
|
750
|
+
FROM pg_catalog.pg_attribute AS a JOIN pg_catalog.pg_type t ON t.oid = a.atttypid
|
|
751
|
+
LEFT JOIN pg_catalog.pg_attrdef d ON d.adrelid = a.attrelid AND d.adnum = a.attnum
|
|
752
|
+
WHERE a.attrelid = ANY($1::oid[]) AND a.attnum > 0 AND NOT a.attisdropped
|
|
753
|
+
ORDER BY a.attrelid, a.attnum`,
|
|
754
|
+
[oids],
|
|
755
|
+
);
|
|
756
|
+
const checks = await client.query<Row>(
|
|
757
|
+
`SELECT k.conrelid::text AS rel, k.conname AS name, pg_catalog.pg_get_constraintdef(k.oid, true) AS def, pg_catalog.obj_description(k.oid, 'pg_constraint') AS comment
|
|
758
|
+
FROM pg_catalog.pg_constraint k
|
|
759
|
+
WHERE k.conrelid = ANY($1::oid[]) AND k.contype = 'c' AND k.conislocal
|
|
760
|
+
ORDER BY k.conrelid, k.conname ${C}`,
|
|
761
|
+
[oids],
|
|
762
|
+
);
|
|
763
|
+
return tables.map((t): LivePgObject => {
|
|
764
|
+
const rel = String(t.oid);
|
|
765
|
+
const q = qname(String(t.schema), String(t.name));
|
|
766
|
+
const omitted: string[] = [];
|
|
767
|
+
const lines: string[] = [];
|
|
768
|
+
const commentLines: string[] = [];
|
|
769
|
+
let generated = false;
|
|
770
|
+
for (const c of columns.filter((x) => String(x.rel) === rel)) {
|
|
771
|
+
const parts = [quoteIdent(String(c.name)), String(c.type)];
|
|
772
|
+
const opts = optionsOf(c.options);
|
|
773
|
+
omitted.push(...opts.omitted);
|
|
774
|
+
if (opts.clause) parts.push(opts.clause.trim());
|
|
775
|
+
if (str(c.collation)) parts.push(`COLLATE ${c.collation}`);
|
|
776
|
+
if (str(c.generated)) generated = true;
|
|
777
|
+
else if (str(c.dflt)) parts.push(`DEFAULT ${c.dflt}`);
|
|
778
|
+
if (c.notnull === true) parts.push("NOT NULL");
|
|
779
|
+
lines.push(parts.join(" "));
|
|
780
|
+
commentLines.push(...commentOn(`COLUMN ${q}.${quoteIdent(String(c.name))}`, c.comment));
|
|
781
|
+
}
|
|
782
|
+
for (const k of checks.filter((x) => String(x.rel) === rel)) {
|
|
783
|
+
lines.push(`CONSTRAINT ${quoteIdent(String(k.name))} ${k.def}`);
|
|
784
|
+
commentLines.push(...commentOn(`CONSTRAINT ${quoteIdent(String(k.name))} ON ${q}`, k.comment));
|
|
785
|
+
}
|
|
786
|
+
const { clause, omitted: tableOmitted } = optionsOf(t.options);
|
|
787
|
+
omitted.push(...tableOmitted);
|
|
788
|
+
const unsupported =
|
|
789
|
+
t.ispartition === true ? "it is a partition of another table (CREATE FOREIGN TABLE ... PARTITION OF is not declared)" : t.inherits === true ? "it inherits from another table (INHERITS is not declared on a foreign table)" : generated ? "it has a generated column (not declared on a foreign table)" : undefined;
|
|
790
|
+
return {
|
|
791
|
+
type: POSTGRES_ENTITY_TYPES.foreignTable,
|
|
792
|
+
schema: String(t.schema),
|
|
793
|
+
name: String(t.name),
|
|
794
|
+
oid: rel,
|
|
795
|
+
...withComment(t),
|
|
796
|
+
...dependents(t),
|
|
797
|
+
...omittedWarning(omitted),
|
|
798
|
+
...(unsupported ? { unsupported } : {}),
|
|
799
|
+
statement: [
|
|
800
|
+
`CREATE FOREIGN TABLE ${q} (${lines.length > 0 ? `\n ${lines.join(",\n ")}\n` : ""}) SERVER ${quoteIdent(String(t.server))}${clause}`,
|
|
801
|
+
...commentOn(`FOREIGN TABLE ${q}`, t.comment),
|
|
802
|
+
...commentLines,
|
|
803
|
+
].join(";\n"),
|
|
804
|
+
};
|
|
805
|
+
});
|
|
806
|
+
}
|
|
@@ -53,7 +53,7 @@ import { MIGRATION_TRAILER_KEY } from "../migrate/names";
|
|
|
53
53
|
import { isProviderOwned, providerData } from "../providers";
|
|
54
54
|
import { FOREIGN_TABLES } from "../../core/foreign-tables";
|
|
55
55
|
import type { PostgresProvider } from "../providers/types";
|
|
56
|
-
import { readCollations, readRoutineKinds, readTypeKinds } from "./catalog-kinds";
|
|
56
|
+
import { readCollations, readDatabaseKinds, readRoutineKinds, readTypeKinds } from "./catalog-kinds";
|
|
57
57
|
|
|
58
58
|
/** The server's own schemas, never part of a declared schema. */
|
|
59
59
|
export const SYSTEM_SCHEMAS = ["pg_catalog", "information_schema", "pg_toast"];
|
|
@@ -83,6 +83,8 @@ export interface LivePgObject {
|
|
|
83
83
|
dependents?: string[];
|
|
84
84
|
/** Why no declaration can hold this object; it is read so import can say it left it out. */
|
|
85
85
|
unsupported?: string;
|
|
86
|
+
/** What was left out of an object that is otherwise read (an option that holds a credential), for import to say. */
|
|
87
|
+
warnings?: string[];
|
|
86
88
|
}
|
|
87
89
|
|
|
88
90
|
/** What {@link readLiveSchema} reads. */
|
|
@@ -629,6 +631,9 @@ export async function readLiveSchema(client: PostgresClient, scope: SchemaScope
|
|
|
629
631
|
// Aggregates, casts and event triggers, after the functions they call (#3911).
|
|
630
632
|
out.push(...(await readRoutineKinds(client, scope)));
|
|
631
633
|
|
|
634
|
+
// Extended statistics, foreign data wrappers and servers, and publications (#3911).
|
|
635
|
+
out.push(...(await readDatabaseKinds(client, scope)));
|
|
636
|
+
|
|
632
637
|
// Triggers: the ones written by hand, not a foreign key's or a partition's clone of its parent's.
|
|
633
638
|
{
|
|
634
639
|
const rows = await client.query<Row>(
|