@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
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import { join } from "node:path";
|
|
16
|
-
import { describe, expect, test } from "vitest";
|
|
16
|
+
import { beforeAll, describe, expect, test } from "vitest";
|
|
17
17
|
import { generateOpsPipeline } from "@intentius/chant/op";
|
|
18
18
|
import { parseYAML } from "@intentius/chant/yaml";
|
|
19
19
|
import type { ScheduledOpSpec } from "@intentius/chant/lexicon";
|
|
@@ -58,6 +58,13 @@ async function render(forge: "github" | "gitlab" | "forgejo", spec: ScheduledOpS
|
|
|
58
58
|
const runLine = (steps: Array<Record<string, unknown>>) => steps.find((s) => s.run === "chant run schema-watch");
|
|
59
59
|
|
|
60
60
|
describe("generateOpsPipeline renders the sql drift watch (#3642)", () => {
|
|
61
|
+
// The first render loads the github, gitlab and forgejo plugins and discovers the example's Ops. On a loaded CI shard
|
|
62
|
+
// that cold load took 5.7s and put the first test over the 5s unit-test budget (chant #2817). A file's beforeAll is
|
|
63
|
+
// not counted against the budget, so the load is paid here and each test below times only its own render.
|
|
64
|
+
beforeAll(async () => {
|
|
65
|
+
await generateOpsPipeline([reader(actionsSecret)], "github", { beforeScript: INSTALL.actions }, EXAMPLE);
|
|
66
|
+
}, 60_000);
|
|
67
|
+
|
|
61
68
|
test("GitHub: the Op's own cron, a read-only token and the reader's credentials", async () => {
|
|
62
69
|
const { file, doc } = await render("github", reader(actionsSecret));
|
|
63
70
|
expect(file.name).toBe("schema-watch.yml");
|
package/src/plugin.ts
CHANGED
|
@@ -18,7 +18,7 @@ import { completions } from "./lsp/completions";
|
|
|
18
18
|
import { hover } from "./lsp/hover";
|
|
19
19
|
import { sqlMcpResources, sqlMcpTools } from "./mcp";
|
|
20
20
|
import { sqlConfigSchema } from "./config";
|
|
21
|
-
import { SqlFileParser, dialectOption } from "./import-parser";
|
|
21
|
+
import { SqlFileParser, dialectOption, sourceOption } from "./import-parser";
|
|
22
22
|
import { sqlTemplateGenerator } from "./import-generator";
|
|
23
23
|
import { sqlCommands } from "./clickhouse/plan/commands";
|
|
24
24
|
import { sqlDeepNormalizationHooks } from "./clickhouse/plan/deep";
|
|
@@ -29,6 +29,7 @@ import { CLICKHOUSE_EMULATOR } from "./op/activities/clickhouse-emulator";
|
|
|
29
29
|
import { POSTGRES_EMULATOR } from "./op/activities/postgres-emulator";
|
|
30
30
|
import { sqlSkills } from "./skill-defs";
|
|
31
31
|
import { detectTemplate } from "./detect";
|
|
32
|
+
import { looksLikePostgres } from "./migrate/from-postgres/detect";
|
|
32
33
|
import { sqlWorkspaceOutputs } from "./workspace-outputs";
|
|
33
34
|
import { CDC_TEMPLATE, DEFAULT_TEMPLATE, EVENTS_TEMPLATE, POSTGRES_EVENTS_TEMPLATE, POSTGRES_TEMPLATE, POSTGRES_TENANT_TEMPLATE } from "./init-templates";
|
|
34
35
|
import { compositeCatalog } from "./composites/catalog";
|
|
@@ -113,6 +114,16 @@ export const sqlPlugin: LexiconPlugin = {
|
|
|
113
114
|
{ name: "aggregate", isTag: true, description: "A Postgres CREATE AGGREGATE, parsed into an aggregate entity identified by its argument types" },
|
|
114
115
|
{ name: "trigger", isTag: true, description: "A Postgres CREATE TRIGGER, parsed into a trigger entity on its table" },
|
|
115
116
|
{ name: "eventTrigger", isTag: true, description: "A Postgres CREATE EVENT TRIGGER, parsed into the database's event trigger entity" },
|
|
117
|
+
{ name: "operator", isTag: true, description: "A Postgres CREATE OPERATOR, parsed into an operator entity identified by its name and operand types" },
|
|
118
|
+
{ name: "operatorClass", isTag: true, description: "A Postgres CREATE OPERATOR CLASS, parsed into an operator class entity identified by its name and index method" },
|
|
119
|
+
{ name: "operatorFamily", isTag: true, description: "A Postgres CREATE OPERATOR FAMILY, parsed into an operator family entity identified by its name and index method" },
|
|
120
|
+
{ name: "foreignTable", isTag: true, description: "A Postgres CREATE FOREIGN TABLE, parsed into a foreign table entity with its columns, server and options" },
|
|
121
|
+
{ name: "textSearchConfiguration", isTag: true, description: "A Postgres CREATE TEXT SEARCH CONFIGURATION with its ADD MAPPING statements, parsed into a configuration entity" },
|
|
122
|
+
{ name: "textSearchDictionary", isTag: true, description: "A Postgres CREATE TEXT SEARCH DICTIONARY, parsed into a dictionary entity with its template and options" },
|
|
123
|
+
{ name: "statistics", isTag: true, description: "A Postgres CREATE STATISTICS, parsed into an extended statistics entity on its table" },
|
|
124
|
+
{ name: "foreignDataWrapper", isTag: true, description: "A Postgres CREATE FOREIGN DATA WRAPPER, parsed into the database's wrapper entity" },
|
|
125
|
+
{ name: "foreignServer", isTag: true, description: "A Postgres CREATE SERVER, parsed into the database's foreign server entity (user mappings are not declared)" },
|
|
126
|
+
{ name: "publication", isTag: true, description: "A Postgres CREATE PUBLICATION, parsed into the database's publication entity" },
|
|
116
127
|
{ name: "collation", isTag: true, description: "A Postgres CREATE COLLATION, parsed into a collation entity" },
|
|
117
128
|
{ name: "cast", isTag: true, description: "A Postgres CREATE CAST, parsed into a cast entity named by its two types" },
|
|
118
129
|
{ name: "policy", isTag: true, description: "A Postgres CREATE POLICY, parsed into a row-level security policy on its table, or a ClickHouse CREATE ROW POLICY" },
|
|
@@ -166,7 +177,31 @@ export const sqlPlugin: LexiconPlugin = {
|
|
|
166
177
|
|
|
167
178
|
/** `chant import schema.sql`: a file of Postgres or ClickHouse DDL (`./import-parser.ts`). */
|
|
168
179
|
templateParser(options) {
|
|
169
|
-
|
|
180
|
+
const from = sourceOption(options);
|
|
181
|
+
return new SqlFileParser(dialectOption(options), { skipUndeclarable: options?.skipUndeclarable === true, ...(from ? { from } : {}) });
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
/** `chant import --validate` (#4398): create the imported DDL on the pinned ClickHouse emulator. */
|
|
185
|
+
async validateImport(ir) {
|
|
186
|
+
const { validateOnEmulator } = await import("./sqlserver/validate");
|
|
187
|
+
return validateOnEmulator(ir);
|
|
188
|
+
},
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* `chant migrate --from sql/postgres --to sql/clickhouse` (#4399): the
|
|
192
|
+
* Postgres tables a CDC pipeline mirrors, as ClickHouse tables or
|
|
193
|
+
* `CdcMirror` calls (`./migrate/from-postgres/index.ts`).
|
|
194
|
+
*/
|
|
195
|
+
migrationSource(from, to) {
|
|
196
|
+
if (from !== "sql/postgres" || (to !== undefined && to !== "sql/clickhouse")) return undefined;
|
|
197
|
+
return {
|
|
198
|
+
detect: looksLikePostgres,
|
|
199
|
+
directory: true,
|
|
200
|
+
async transform(content, opts) {
|
|
201
|
+
const { transform } = await import("./migrate/from-postgres/index");
|
|
202
|
+
return transform(content, opts);
|
|
203
|
+
},
|
|
204
|
+
};
|
|
170
205
|
},
|
|
171
206
|
|
|
172
207
|
parserOptions() {
|
|
@@ -176,6 +211,11 @@ export const sqlPlugin: LexiconPlugin = {
|
|
|
176
211
|
type: "string" as const,
|
|
177
212
|
description: "the DDL's dialect, clickhouse or postgres; read off its statements when omitted",
|
|
178
213
|
},
|
|
214
|
+
{
|
|
215
|
+
name: "from",
|
|
216
|
+
type: "string" as const,
|
|
217
|
+
description: "read the file as a SQL Server schema (sqlserver) and translate it to ClickHouse; each judgment call is a needs-review diagnostic for --report and --strict",
|
|
218
|
+
},
|
|
179
219
|
{
|
|
180
220
|
name: "skipUndeclarable",
|
|
181
221
|
type: "boolean" as const,
|
|
@@ -260,6 +300,15 @@ export const sqlPlugin: LexiconPlugin = {
|
|
|
260
300
|
return { ...classifyDisruption(options), ...classifyPgDisruption(options) };
|
|
261
301
|
},
|
|
262
302
|
|
|
303
|
+
/**
|
|
304
|
+
* The migration files a pull request adds (#4407): each up statement given
|
|
305
|
+
* the class the schema-diff classifier gives the same change. Offline.
|
|
306
|
+
*/
|
|
307
|
+
async classifyMigrations(options) {
|
|
308
|
+
const { classifyMigrationFiles } = await import("./migration-classify");
|
|
309
|
+
return classifyMigrationFiles(options);
|
|
310
|
+
},
|
|
311
|
+
|
|
263
312
|
/** `chant sql diff` and `chant sql plan`: schema changes, classified. */
|
|
264
313
|
commands() {
|
|
265
314
|
return sqlCommands;
|
|
@@ -188,7 +188,7 @@ export function declaredAccess(objects: ReadonlyArray<{ key: string; canonical:
|
|
|
188
188
|
for (const { canonical: o } of objects) {
|
|
189
189
|
const name = `${o.schema ? `${quoteIdent(o.schema)}.` : ""}${quoteIdent(o.name)}`;
|
|
190
190
|
if (o.kind === "schema") declaredTargets.push({ kind: "schema", name: quoteIdent(o.name) });
|
|
191
|
-
else if (o.kind === "table" || o.kind === "view" || o.kind === "materializedView") {
|
|
191
|
+
else if (o.kind === "table" || o.kind === "foreignTable" || o.kind === "view" || o.kind === "materializedView") {
|
|
192
192
|
relationKind.set(name, "table");
|
|
193
193
|
declaredTargets.push({ kind: "table", name });
|
|
194
194
|
} else if (o.kind === "sequence") {
|
|
@@ -21,7 +21,7 @@ function targetsOf(objects: readonly LivePgObject[]): AclTarget[] {
|
|
|
21
21
|
for (const o of objects) {
|
|
22
22
|
const name = `${o.schema ? `${quoteIdent(o.schema)}.` : ""}${quoteIdent(o.name)}`;
|
|
23
23
|
if (o.type === POSTGRES_ENTITY_TYPES.schema) out.push({ kind: "schema", name: quoteIdent(o.name) });
|
|
24
|
-
else if (o.type === POSTGRES_ENTITY_TYPES.table || o.type === POSTGRES_ENTITY_TYPES.view || o.type === POSTGRES_ENTITY_TYPES.materializedView) out.push({ kind: "table", name });
|
|
24
|
+
else if (o.type === POSTGRES_ENTITY_TYPES.table || o.type === POSTGRES_ENTITY_TYPES.foreignTable || o.type === POSTGRES_ENTITY_TYPES.view || o.type === POSTGRES_ENTITY_TYPES.materializedView) out.push({ kind: "table", name });
|
|
25
25
|
else if (o.type === POSTGRES_ENTITY_TYPES.sequence) out.push({ kind: "sequence", name });
|
|
26
26
|
else if (o.type === POSTGRES_ENTITY_TYPES.function || o.type === POSTGRES_ENTITY_TYPES.procedure) {
|
|
27
27
|
out.push({ kind: o.type === POSTGRES_ENTITY_TYPES.function ? "function" : "procedure", name: `${name}${o.signature ?? ""}` });
|
|
@@ -553,6 +553,9 @@ async function applyAccess(
|
|
|
553
553
|
}
|
|
554
554
|
}
|
|
555
555
|
|
|
556
|
+
/** The kinds that belong to the database, not a schema. */
|
|
557
|
+
const UNPLACED_KINDS: ReadonlySet<CanonicalKind> = new Set(["eventTrigger", "foreignDataWrapper", "foreignServer", "publication"]);
|
|
558
|
+
|
|
556
559
|
/** The address a live object is keyed by in the diff. */
|
|
557
560
|
function liveCanonicalAddress(o: LivePgObject): { kind: CanonicalKind; schema?: string; name: string; signature?: string } {
|
|
558
561
|
const kind = (
|
|
@@ -571,6 +574,16 @@ function liveCanonicalAddress(o: LivePgObject): { kind: CanonicalKind; schema?:
|
|
|
571
574
|
"Postgres::Collation": "collation",
|
|
572
575
|
"Postgres::Cast": "cast",
|
|
573
576
|
"Postgres::EventTrigger": "eventTrigger",
|
|
577
|
+
"Postgres::Operator": "operator",
|
|
578
|
+
"Postgres::OperatorClass": "operatorClass",
|
|
579
|
+
"Postgres::OperatorFamily": "operatorFamily",
|
|
580
|
+
"Postgres::ForeignTable": "foreignTable",
|
|
581
|
+
"Postgres::TextSearchConfiguration": "textSearchConfiguration",
|
|
582
|
+
"Postgres::TextSearchDictionary": "textSearchDictionary",
|
|
583
|
+
"Postgres::Statistics": "statistics",
|
|
584
|
+
"Postgres::ForeignDataWrapper": "foreignDataWrapper",
|
|
585
|
+
"Postgres::ForeignServer": "foreignServer",
|
|
586
|
+
"Postgres::Publication": "publication",
|
|
574
587
|
"Postgres::Extension": "extension",
|
|
575
588
|
"Postgres::Function": "function",
|
|
576
589
|
"Postgres::Procedure": "procedure",
|
|
@@ -583,7 +596,7 @@ function liveCanonicalAddress(o: LivePgObject): { kind: CanonicalKind; schema?:
|
|
|
583
596
|
)[o.type];
|
|
584
597
|
return {
|
|
585
598
|
kind,
|
|
586
|
-
...(o.schema !== undefined && kind !== "schema" && kind !== "extension" && kind !== "role" && kind !== "cast" && kind
|
|
599
|
+
...(o.schema !== undefined && kind !== "schema" && kind !== "extension" && kind !== "role" && kind !== "cast" && !UNPLACED_KINDS.has(kind) ? { schema: o.schema } : {}),
|
|
587
600
|
name: o.name,
|
|
588
601
|
...(o.signature !== undefined ? { signature: o.signature } : {}),
|
|
589
602
|
};
|
|
@@ -34,7 +34,7 @@ import { ACCESS_KINDS, canonicalOptions, sameConstraint, type CanonicalConstrain
|
|
|
34
34
|
import type { PgDiffObject, PgSchemaObject } from "../plan/schema";
|
|
35
35
|
import { migrationTarget } from "../migrate/handoff";
|
|
36
36
|
import { pgName } from "../migrate/names";
|
|
37
|
-
import type { CheckDef, ColumnDef, DomainProps, ExclusionDef, ForeignKeyDef, IndexProps, KeyDef, PolicyProps, SequenceProps, TableProps } from "../entities";
|
|
37
|
+
import type { CheckDef, ColumnDef, DomainProps, ForeignTableProps, TextSearchConfigurationProps, ExclusionDef, ForeignKeyDef, IndexProps, KeyDef, PolicyProps, SequenceProps, TableProps } from "../entities";
|
|
38
38
|
|
|
39
39
|
const same = (a: unknown, b: unknown) => JSON.stringify(a) === JSON.stringify(b);
|
|
40
40
|
|
|
@@ -108,10 +108,12 @@ export const pgQualified = (schema: string | undefined, name: string): string =>
|
|
|
108
108
|
export const objectIdent = (o: { kind: CanonicalKind; schema?: string; name: string; signature?: string }): string =>
|
|
109
109
|
o.kind === "cast"
|
|
110
110
|
? o.name
|
|
111
|
-
: o.kind === "schema" || o.kind === "extension" || o.kind === "role" || o.kind === "eventTrigger"
|
|
111
|
+
: o.kind === "schema" || o.kind === "extension" || o.kind === "role" || o.kind === "eventTrigger" || o.kind === "foreignDataWrapper" || o.kind === "foreignServer" || o.kind === "publication"
|
|
112
112
|
? quoteIdent(o.name)
|
|
113
113
|
: o.kind === "trigger" || o.kind === "policy"
|
|
114
114
|
? `${quoteIdent(o.name)}${o.signature ?? ""}`
|
|
115
|
+
: o.kind === "operator"
|
|
116
|
+
? `${o.schema ? `${quoteIdent(o.schema)}.` : ""}${o.name}${o.signature ?? ""}`
|
|
115
117
|
: `${pgQualified(o.schema, o.name)}${o.signature ?? ""}`;
|
|
116
118
|
|
|
117
119
|
/** The words a statement names the object's kind with. */
|
|
@@ -133,6 +135,16 @@ export const KIND_WORDS: Readonly<Record<CanonicalKind, string>> = {
|
|
|
133
135
|
collation: "COLLATION",
|
|
134
136
|
cast: "CAST",
|
|
135
137
|
eventTrigger: "EVENT TRIGGER",
|
|
138
|
+
operator: "OPERATOR",
|
|
139
|
+
operatorClass: "OPERATOR CLASS",
|
|
140
|
+
operatorFamily: "OPERATOR FAMILY",
|
|
141
|
+
foreignTable: "FOREIGN TABLE",
|
|
142
|
+
textSearchConfiguration: "TEXT SEARCH CONFIGURATION",
|
|
143
|
+
textSearchDictionary: "TEXT SEARCH DICTIONARY",
|
|
144
|
+
statistics: "STATISTICS",
|
|
145
|
+
foreignDataWrapper: "FOREIGN DATA WRAPPER",
|
|
146
|
+
foreignServer: "SERVER",
|
|
147
|
+
publication: "PUBLICATION",
|
|
136
148
|
trigger: "TRIGGER",
|
|
137
149
|
policy: "POLICY",
|
|
138
150
|
role: "ROLE",
|
|
@@ -315,7 +327,7 @@ function identitySetClauses(options: string | undefined): string[] {
|
|
|
315
327
|
return out;
|
|
316
328
|
}
|
|
317
329
|
|
|
318
|
-
const COLUMN_FIELD = /^columns\.(.+?)(?:\.(type|default|notNull|generated|identity|collate|storage|comment))?$/;
|
|
330
|
+
const COLUMN_FIELD = /^columns\.(.+?)(?:\.(type|default|notNull|generated|identity|collate|storage|options|comment))?$/;
|
|
319
331
|
|
|
320
332
|
// ── Constraints ────────────────────────────────────────────────────────
|
|
321
333
|
|
|
@@ -569,6 +581,181 @@ export function setNotNullSteps(table: string, column: string, live?: Pick<PgDif
|
|
|
569
581
|
return steps;
|
|
570
582
|
}
|
|
571
583
|
|
|
584
|
+
/** An `OPTIONS (...)` clause that takes the options `have` to `want`: ADD, SET and DROP per option; empty when they agree. */
|
|
585
|
+
function optionsClause(want: unknown, have: unknown): string {
|
|
586
|
+
const w = (want ?? {}) as Record<string, string>;
|
|
587
|
+
const h = (have ?? {}) as Record<string, string>;
|
|
588
|
+
const parts = [
|
|
589
|
+
...Object.keys(w).filter((k) => !(k in h)).map((k) => `ADD ${quoteIdent(k)} ${pgString(w[k]!)}`),
|
|
590
|
+
...Object.keys(w).filter((k) => k in h && h[k] !== w[k]).map((k) => `SET ${quoteIdent(k)} ${pgString(w[k]!)}`),
|
|
591
|
+
...Object.keys(h).filter((k) => !(k in w)).map((k) => `DROP ${quoteIdent(k)}`),
|
|
592
|
+
];
|
|
593
|
+
return parts.length > 0 ? `OPTIONS (${parts.join(", ")})` : "";
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/** A publication's table as `ADD TABLE` writes it: its name, column list and row filter. */
|
|
597
|
+
const publicationTable = (t: { table: string; columns?: string[]; where?: string }): string =>
|
|
598
|
+
`${t.table}${t.columns && t.columns.length > 0 ? ` (${t.columns.map(quoteIdent).join(", ")})` : ""}${t.where ? ` WHERE (${t.where})` : ""}`;
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* The ALTER statements that take a foreign data wrapper, a foreign server or
|
|
602
|
+
* a publication to the declaration, in place (SQLPG303): one ALTER for a
|
|
603
|
+
* wrapper or server; for a publication one SET (...) for its options, and a
|
|
604
|
+
* DROP and ADD per table or schema that changed.
|
|
605
|
+
*/
|
|
606
|
+
function alteredSteps(d: PgDiffObject, live: PgDiffObject, target: string): PgStep[] {
|
|
607
|
+
const out: string[] = [];
|
|
608
|
+
const f = d.fields;
|
|
609
|
+
const l = live.fields;
|
|
610
|
+
if (d.kind === "foreignDataWrapper") {
|
|
611
|
+
const clauses = [
|
|
612
|
+
!same(f.handler, l.handler) ? (f.handler ? `HANDLER ${String(f.handler)}` : "NO HANDLER") : "",
|
|
613
|
+
!same(f.validator, l.validator) ? (f.validator ? `VALIDATOR ${String(f.validator)}` : "NO VALIDATOR") : "",
|
|
614
|
+
optionsClause(f.options, l.options),
|
|
615
|
+
].filter(Boolean);
|
|
616
|
+
if (clauses.length > 0) out.push(`ALTER FOREIGN DATA WRAPPER ${target} ${clauses.join(" ")}`);
|
|
617
|
+
} else if (d.kind === "foreignServer") {
|
|
618
|
+
const clauses = [!same(f.version, l.version) ? `VERSION ${f.version === undefined ? "NULL" : pgString(String(f.version))}` : "", optionsClause(f.options, l.options)].filter(Boolean);
|
|
619
|
+
if (clauses.length > 0) out.push(`ALTER SERVER ${target} ${clauses.join(" ")}`);
|
|
620
|
+
} else if (d.kind === "publication") {
|
|
621
|
+
const sets: string[] = [];
|
|
622
|
+
if (!same(f.publish, l.publish)) sets.push(`publish = ${pgString(((f.publish as string[] | undefined) ?? ["insert", "update", "delete", "truncate"]).join(", "))}`);
|
|
623
|
+
if (!same(f.viaRoot, l.viaRoot)) sets.push(`publish_via_partition_root = ${f.viaRoot === true}`);
|
|
624
|
+
if (!same(f.generatedColumns, l.generatedColumns)) sets.push(`publish_generated_columns = ${String(f.generatedColumns ?? "none")}`);
|
|
625
|
+
if (sets.length > 0) out.push(`ALTER PUBLICATION ${target} SET (${sets.join(", ")})`);
|
|
626
|
+
type Member = { table: string; columns?: string[]; where?: string };
|
|
627
|
+
const want = (f.tables as Member[] | undefined) ?? [];
|
|
628
|
+
const have = (l.tables as Member[] | undefined) ?? [];
|
|
629
|
+
const kept = (m: Member) => want.some((w) => w.table === m.table && same(w, m));
|
|
630
|
+
for (const m of have) if (!kept(m)) out.push(`ALTER PUBLICATION ${target} DROP TABLE ${m.table}`);
|
|
631
|
+
for (const m of want) if (!have.some((h) => h.table === m.table && same(h, m))) out.push(`ALTER PUBLICATION ${target} ADD TABLE ${publicationTable(m)}`);
|
|
632
|
+
const wantSchemas = (f.schemas as string[] | undefined) ?? [];
|
|
633
|
+
const haveSchemas = (l.schemas as string[] | undefined) ?? [];
|
|
634
|
+
for (const s of haveSchemas) if (!wantSchemas.includes(s)) out.push(`ALTER PUBLICATION ${target} DROP TABLES IN SCHEMA ${s}`);
|
|
635
|
+
for (const s of wantSchemas) if (!haveSchemas.includes(s)) out.push(`ALTER PUBLICATION ${target} ADD TABLES IN SCHEMA ${s}`);
|
|
636
|
+
}
|
|
637
|
+
return out.map((sql) => step(sql, "metadata", "SQLPG303"));
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
|
|
641
|
+
// ── Operators, text search and foreign tables (#4406) ──────────────────
|
|
642
|
+
|
|
643
|
+
/**
|
|
644
|
+
* The clauses of `ALTER OPERATOR ... SET (...)` for the attributes the diff
|
|
645
|
+
* found changeable in place (SQLPG304): RESTRICT and JOIN to the declared
|
|
646
|
+
* estimator or NONE, COMMUTATOR and NEGATOR to the declared operator, and
|
|
647
|
+
* HASHES and MERGES as bare words, since they can only be turned on.
|
|
648
|
+
*/
|
|
649
|
+
function operatorSetClauses(d: PgDiffObject, changes: readonly PgChange[]): string[] {
|
|
650
|
+
const definition = (d.fields.definition ?? {}) as Record<string, string>;
|
|
651
|
+
return changes.map((c) => {
|
|
652
|
+
const key = c.field.replace(/^definition\./, "");
|
|
653
|
+
const value = definition[key];
|
|
654
|
+
if (key === "hashes" || key === "merges") return key.toUpperCase();
|
|
655
|
+
if (key === "commutator" || key === "negator") return `${key.toUpperCase()} = OPERATOR(${value})`;
|
|
656
|
+
return `${key.toUpperCase()} = ${value ?? "NONE"}`;
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* A text search configuration's mappings, or a dictionary's options, taken to
|
|
662
|
+
* the declaration in place (SQLPG305). For a configuration: DROP MAPPING for
|
|
663
|
+
* the token types no longer mapped, ALTER MAPPING for those whose dictionaries
|
|
664
|
+
* changed, ADD MAPPING for the new ones, one statement for the token types
|
|
665
|
+
* that share their dictionaries; the dictionaries are named as the
|
|
666
|
+
* declaration wrote them. For a dictionary: one ALTER with each option that
|
|
667
|
+
* differs set, and each option the declaration dropped named with no value,
|
|
668
|
+
* which removes it.
|
|
669
|
+
*/
|
|
670
|
+
function textSearchSteps(obj: DeclaredPgObject, live: PgDiffObject, target: string): PgStep[] {
|
|
671
|
+
const d = obj.canonical;
|
|
672
|
+
if (d.kind === "textSearchDictionary") {
|
|
673
|
+
const want = (d.fields.options ?? {}) as Record<string, string>;
|
|
674
|
+
const have = (live.fields.options ?? {}) as Record<string, string>;
|
|
675
|
+
const parts = [
|
|
676
|
+
...Object.keys(want).filter((k) => have[k] !== want[k]).map((k) => `${quoteIdent(k)} = ${pgString(want[k]!)}`),
|
|
677
|
+
...Object.keys(have).filter((k) => !(k in want)).map((k) => quoteIdent(k)),
|
|
678
|
+
];
|
|
679
|
+
return parts.length > 0 ? [step(`ALTER TEXT SEARCH DICTIONARY ${target} (${parts.join(", ")})`, "metadata", "SQLPG305")] : [];
|
|
680
|
+
}
|
|
681
|
+
const want = (d.fields.mappings ?? {}) as Record<string, string[]>;
|
|
682
|
+
const have = (live.fields.mappings ?? {}) as Record<string, string[]>;
|
|
683
|
+
const written = new Map<string, string[]>();
|
|
684
|
+
for (const m of (obj.props as unknown as TextSearchConfigurationProps).mappings) for (const t of m.tokens) written.set(t.toLowerCase(), m.dictionaries);
|
|
685
|
+
const group = (tokens: string[]): Map<string, string[]> => {
|
|
686
|
+
const out = new Map<string, string[]>();
|
|
687
|
+
for (const t of tokens) {
|
|
688
|
+
const key = JSON.stringify(want[t]);
|
|
689
|
+
out.set(key, [...(out.get(key) ?? []), t]);
|
|
690
|
+
}
|
|
691
|
+
return out;
|
|
692
|
+
};
|
|
693
|
+
const dropped = Object.keys(have).filter((t) => !(t in want));
|
|
694
|
+
const added = Object.keys(want).filter((t) => !(t in have));
|
|
695
|
+
const changed = Object.keys(want).filter((t) => t in have && JSON.stringify(have[t]) !== JSON.stringify(want[t]));
|
|
696
|
+
const names = (tokens: string[]) => tokens.map(quoteIdent).join(", ");
|
|
697
|
+
const out: PgStep[] = [];
|
|
698
|
+
if (dropped.length > 0) out.push(step(`ALTER TEXT SEARCH CONFIGURATION ${target} DROP MAPPING FOR ${names(dropped)}`, "metadata", "SQLPG305"));
|
|
699
|
+
for (const [verb, tokens] of [["ALTER", changed], ["ADD", added]] as const) {
|
|
700
|
+
for (const members of group(tokens).values()) out.push(step(`ALTER TEXT SEARCH CONFIGURATION ${target} ${verb} MAPPING FOR ${names(members)} WITH ${written.get(members[0]!)!.join(", ")}`, "metadata", "SQLPG305"));
|
|
701
|
+
}
|
|
702
|
+
return out;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
/**
|
|
706
|
+
* A foreign table's changes in place (SQLPG306), as ALTER FOREIGN TABLE
|
|
707
|
+
* makes them: ADD and DROP COLUMN, a column's type, default, NOT NULL and
|
|
708
|
+
* OPTIONS, CHECK constraints added and dropped, and the table's OPTIONS.
|
|
709
|
+
* A column added with options gets them in an ALTER of its own, since ADD
|
|
710
|
+
* COLUMN has no OPTIONS clause.
|
|
711
|
+
*/
|
|
712
|
+
function foreignTableSteps(c: PgChange, obj: DeclaredPgObject, live: PgDiffObject, alterKind: string): PgStep[] {
|
|
713
|
+
const d = obj.canonical;
|
|
714
|
+
const props = obj.props as unknown as ForeignTableProps;
|
|
715
|
+
const make = (sql: string) => step(sql, "metadata", "SQLPG306");
|
|
716
|
+
if (c.field === "options") {
|
|
717
|
+
const clause = optionsClause(d.fields.options, live.fields.options);
|
|
718
|
+
return clause ? [make(`${alterKind} ${clause}`)] : [];
|
|
719
|
+
}
|
|
720
|
+
if (c.field.startsWith("constraints.")) {
|
|
721
|
+
const key = c.field.slice("constraints.".length);
|
|
722
|
+
// A declared CHECK the server lacks is added; one it has and the declaration dropped is dropped by the server's name.
|
|
723
|
+
if (c.before === undefined) {
|
|
724
|
+
const i = d.constraints.findIndex((a) => (a.name ?? a.kind.toLowerCase()) === key && !live.constraints.some((b) => sameConstraint(a, b)));
|
|
725
|
+
return i >= 0 && props.checks[i] ? [make(`${alterKind} ADD ${constraintSql("CHECK", props.checks[i]!)}`)] : [];
|
|
726
|
+
}
|
|
727
|
+
return [make(`${alterKind} DROP CONSTRAINT ${quoteIdent(key)}`)];
|
|
728
|
+
}
|
|
729
|
+
const m = COLUMN_FIELD.exec(c.field);
|
|
730
|
+
if (!m) return [];
|
|
731
|
+
const name = m[1]!;
|
|
732
|
+
const prop = m[2];
|
|
733
|
+
const def = props.columns.find((x) => x.name === name);
|
|
734
|
+
const column = `${alterKind} ALTER COLUMN ${quoteIdent(name)}`;
|
|
735
|
+
const liveColumn = live.columns.find((x) => x.name === name);
|
|
736
|
+
if (prop === undefined) {
|
|
737
|
+
if (!def) return [make(`${alterKind} DROP COLUMN ${quoteIdent(name)}`)];
|
|
738
|
+
const options = optionsClause(def.options, undefined);
|
|
739
|
+
return [make(`${alterKind} ADD COLUMN ${columnDefinition(def)}`), ...(options ? [make(`${column} ${options}`)] : [])];
|
|
740
|
+
}
|
|
741
|
+
if (!def) return [];
|
|
742
|
+
switch (prop) {
|
|
743
|
+
case "type":
|
|
744
|
+
case "collate":
|
|
745
|
+
return def.type ? [make(`${column} TYPE ${def.type}${def.collate ? ` COLLATE ${def.collate}` : prop === "collate" ? ' COLLATE "default"' : ""}`)] : [];
|
|
746
|
+
case "default":
|
|
747
|
+
return [make(def.default !== undefined ? `${column} SET DEFAULT ${def.default}` : `${column} DROP DEFAULT`)];
|
|
748
|
+
case "notNull":
|
|
749
|
+
return [make(`${column} ${def.notNull ? "SET" : "DROP"} NOT NULL`)];
|
|
750
|
+
case "options": {
|
|
751
|
+
const clause = optionsClause(def.options, liveColumn?.options);
|
|
752
|
+
return clause ? [make(`${column} ${clause}`)] : [];
|
|
753
|
+
}
|
|
754
|
+
default:
|
|
755
|
+
return [];
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
|
|
572
759
|
/**
|
|
573
760
|
* The statements for an object that exists on the server, change by change.
|
|
574
761
|
* `live` is the server's definition, matched to the declaration (a renamed
|
|
@@ -604,13 +791,23 @@ export function alterSteps(
|
|
|
604
791
|
steps.push(...createSteps(obj, opts.marker, { cls: c.class, rule: c.rule }));
|
|
605
792
|
continue;
|
|
606
793
|
}
|
|
794
|
+
if (c.rule === "SQLPG306") {
|
|
795
|
+
steps.push(...foreignTableSteps(c, obj, live, alterKind));
|
|
796
|
+
continue;
|
|
797
|
+
}
|
|
798
|
+
if (d.kind === "foreignTable" && c.rule === "SQLPG216" && /^constraints\.[^.]+\.comment$/.test(c.field)) {
|
|
799
|
+
const name = c.field.split(".")[1]!;
|
|
800
|
+
const declared = d.constraints.find((a) => (a.name ?? a.kind.toLowerCase()) === name);
|
|
801
|
+
steps.push(step(`COMMENT ON CONSTRAINT ${quoteIdent(name)} ON ${target} IS ${declared?.comment === undefined ? "NULL" : pgString(declared.comment)}`, c.class, c.rule));
|
|
802
|
+
continue;
|
|
803
|
+
}
|
|
607
804
|
if (c.field.startsWith("constraints.")) {
|
|
608
805
|
if (!constraintsDone) steps.push(...constraintSteps(obj, live, notNullChecks));
|
|
609
806
|
constraintsDone = true;
|
|
610
807
|
continue;
|
|
611
808
|
}
|
|
612
809
|
const col = COLUMN_FIELD.exec(c.field);
|
|
613
|
-
if (col && (d.kind === "table")) {
|
|
810
|
+
if (col && (d.kind === "table" || d.kind === "foreignTable")) {
|
|
614
811
|
const name = col[1]!;
|
|
615
812
|
const prop = col[2];
|
|
616
813
|
const def = columns.get(name);
|
|
@@ -704,6 +901,15 @@ export function alterSteps(
|
|
|
704
901
|
case "SQLPG229":
|
|
705
902
|
steps.push(step(`ALTER INDEX ${objectIdent(live)} RENAME TO ${quoteIdent(d.name)}`, c.class, c.rule));
|
|
706
903
|
break;
|
|
904
|
+
case "SQLPG303":
|
|
905
|
+
if (!steps.some((s) => s.rule === "SQLPG303")) steps.push(...alteredSteps(d, live, target));
|
|
906
|
+
break;
|
|
907
|
+
case "SQLPG304":
|
|
908
|
+
if (!steps.some((s) => s.rule === "SQLPG304")) steps.push(step(`ALTER OPERATOR ${target} SET (${operatorSetClauses(d, changes.filter((x) => x.rule === "SQLPG304")).join(", ")})`, c.class, c.rule));
|
|
909
|
+
break;
|
|
910
|
+
case "SQLPG305":
|
|
911
|
+
if (!steps.some((s) => s.rule === "SQLPG305")) steps.push(...textSearchSteps(obj, live, target));
|
|
912
|
+
break;
|
|
707
913
|
case "SQLPG250":
|
|
708
914
|
case "SQLPG280":
|
|
709
915
|
case "SQLPG302":
|
|
@@ -882,15 +1088,30 @@ const DROP_ORDER: Record<CanonicalKind, number> = {
|
|
|
882
1088
|
defaultPrivileges: 0,
|
|
883
1089
|
policy: 0,
|
|
884
1090
|
trigger: 0,
|
|
1091
|
+
// A publication reads tables, extended statistics are on one: both go before it.
|
|
1092
|
+
publication: 0,
|
|
1093
|
+
statistics: 0,
|
|
885
1094
|
materializedView: 1,
|
|
886
1095
|
view: 2,
|
|
887
1096
|
index: 3,
|
|
888
1097
|
table: 4,
|
|
1098
|
+
// A foreign table reads a foreign server (below) and a type; a view on it goes before.
|
|
1099
|
+
foreignTable: 4,
|
|
889
1100
|
sequence: 5,
|
|
890
1101
|
// An event trigger and a cast before the function they call; an aggregate before its functions.
|
|
891
1102
|
eventTrigger: 5.5,
|
|
892
1103
|
cast: 5.5,
|
|
893
1104
|
aggregate: 5.5,
|
|
1105
|
+
operator: 5.5,
|
|
1106
|
+
// A class names operators and functions; a family goes after its classes.
|
|
1107
|
+
operatorClass: 5.2,
|
|
1108
|
+
operatorFamily: 5.3,
|
|
1109
|
+
// A configuration maps token types to dictionaries, so it goes first.
|
|
1110
|
+
textSearchConfiguration: 5.7,
|
|
1111
|
+
textSearchDictionary: 5.8,
|
|
1112
|
+
// A server before its wrapper, a wrapper before the handler function it calls.
|
|
1113
|
+
foreignServer: 5.5,
|
|
1114
|
+
foreignDataWrapper: 5.6,
|
|
894
1115
|
// A routine after what calls it (a view, a trigger, a default), before the types its parameters use.
|
|
895
1116
|
function: 6,
|
|
896
1117
|
procedure: 6,
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The names Postgres gives a constraint or an index that the DDL leaves
|
|
3
|
+
* unnamed, so a reader of the declarations can spell them without a server.
|
|
4
|
+
*
|
|
5
|
+
* `makeObjectName` and the `Choose*Name` functions are from the Postgres
|
|
6
|
+
* source (`src/backend/commands/indexcmds.c`, `src/backend/catalog/heap.c`,
|
|
7
|
+
* `src/backend/commands/tablecmds.c`): the table name, then the column
|
|
8
|
+
* names joined with `_`, then a label (`pkey`, `key`, `fkey`, `check`),
|
|
9
|
+
* cut to 63 bytes by shortening the longer of the first two parts one
|
|
10
|
+
* character at a time. A name already taken gets a number after the label
|
|
11
|
+
* (`orders_pkey1`).
|
|
12
|
+
*
|
|
13
|
+
* A name the author writes always wins; these are for the rest. Drizzle's
|
|
14
|
+
* own derived names (`orders_user_id_users_id_fk`) differ from them, which is
|
|
15
|
+
* why the Drizzle export names every constraint it writes.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const NAMEDATALEN = 64;
|
|
19
|
+
|
|
20
|
+
/** `makeObjectName(name1, name2, label)`: ASCII names only. */
|
|
21
|
+
export function makeObjectName(name1: string, name2: string | undefined, label: string | undefined): string {
|
|
22
|
+
let overhead = 0;
|
|
23
|
+
let n2 = 0;
|
|
24
|
+
if (name2 !== undefined) {
|
|
25
|
+
n2 = name2.length;
|
|
26
|
+
overhead++;
|
|
27
|
+
}
|
|
28
|
+
if (label !== undefined) overhead += label.length + 1;
|
|
29
|
+
const avail = NAMEDATALEN - 1 - overhead;
|
|
30
|
+
let n1 = name1.length;
|
|
31
|
+
while (n1 + n2 > avail) {
|
|
32
|
+
if (n1 > n2) n1--;
|
|
33
|
+
else n2--;
|
|
34
|
+
}
|
|
35
|
+
let name = name1.slice(0, n1);
|
|
36
|
+
if (name2 !== undefined) name += `_${name2.slice(0, n2)}`;
|
|
37
|
+
if (label !== undefined) name += `_${label}`;
|
|
38
|
+
return name;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** The first of `label`, `label1`, `label2`, ... that gives a name not in `taken`. */
|
|
42
|
+
export function chooseName(name1: string, name2: string | undefined, label: string, taken: ReadonlySet<string>): string {
|
|
43
|
+
let pass = 0;
|
|
44
|
+
let candidate = makeObjectName(name1, name2, label);
|
|
45
|
+
while (taken.has(candidate)) candidate = makeObjectName(name1, name2, `${label}${++pass}`);
|
|
46
|
+
return candidate;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** The column names of a key or foreign key, joined as Postgres joins them. */
|
|
50
|
+
export const columnsAddition = (columns: readonly string[]): string => columns.join("_");
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The identifiers `expr` reads as column names: words outside quotes and
|
|
54
|
+
* string literals that are not followed by `(`, and are in `columns`.
|
|
55
|
+
*/
|
|
56
|
+
export function referencedColumns(expr: string, columns: readonly string[]): string[] {
|
|
57
|
+
const known = new Set(columns);
|
|
58
|
+
const found = new Set<string>();
|
|
59
|
+
const re = /'(?:[^']|'')*'|"((?:[^"]|"")+)"|([A-Za-z_][\w$]*)(?!\s*\()(?!\s*\.)/g;
|
|
60
|
+
for (let m = re.exec(expr); m; m = re.exec(expr)) {
|
|
61
|
+
const name = m[1] !== undefined ? m[1].replace(/""/g, '"') : m[2];
|
|
62
|
+
if (name !== undefined && known.has(m[1] !== undefined ? name : name.toLowerCase())) found.add(m[1] !== undefined ? name : name.toLowerCase());
|
|
63
|
+
}
|
|
64
|
+
return [...found];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** A check constraint's default name: the column when the expression reads one, else just the table. */
|
|
68
|
+
export function checkName(table: string, expr: string, columns: readonly string[], taken: ReadonlySet<string>): string {
|
|
69
|
+
const refs = referencedColumns(expr, columns);
|
|
70
|
+
return chooseName(table, refs.length === 1 ? refs[0] : undefined, "check", taken);
|
|
71
|
+
}
|