@supabase/pg-delta 1.0.0-alpha.4 → 1.0.0-alpha.6
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/README.md +40 -23
- package/dist/cli/app.js +26 -3
- package/dist/cli/bin/cli.js +5 -0
- package/dist/cli/commands/catalog-export.d.ts +5 -0
- package/dist/cli/commands/catalog-export.js +64 -0
- package/dist/cli/commands/declarative-apply.d.ts +6 -0
- package/dist/cli/commands/declarative-apply.js +288 -0
- package/dist/cli/commands/declarative-export.d.ts +5 -0
- package/dist/cli/commands/declarative-export.js +245 -0
- package/dist/cli/commands/plan.js +19 -6
- package/dist/cli/exit-code.d.ts +2 -0
- package/dist/cli/exit-code.js +7 -0
- package/dist/cli/formatters/tree/tree.js +3 -2
- package/dist/cli/utils/apply-display.d.ts +52 -0
- package/dist/cli/utils/apply-display.js +183 -0
- package/dist/cli/utils/export-display.d.ts +43 -0
- package/dist/cli/utils/export-display.js +202 -0
- package/dist/cli/utils/resolve-input.d.ts +7 -0
- package/dist/cli/utils/resolve-input.js +13 -0
- package/dist/core/catalog-export/index.d.ts +11 -0
- package/dist/core/catalog-export/index.js +10 -0
- package/dist/core/catalog.diff.d.ts +1 -0
- package/dist/core/catalog.diff.js +64 -48
- package/dist/core/catalog.model.d.ts +14 -1
- package/dist/core/catalog.model.js +103 -1
- package/dist/core/catalog.snapshot.d.ts +66 -0
- package/dist/core/catalog.snapshot.js +206 -0
- package/dist/core/declarative-apply/discover-sql.d.ts +18 -0
- package/dist/core/declarative-apply/discover-sql.js +86 -0
- package/dist/core/declarative-apply/extract-catalog-providers.d.ts +23 -0
- package/dist/core/declarative-apply/extract-catalog-providers.js +159 -0
- package/dist/core/declarative-apply/index.d.ts +49 -0
- package/dist/core/declarative-apply/index.js +134 -0
- package/dist/core/declarative-apply/round-apply.d.ts +100 -0
- package/dist/core/declarative-apply/round-apply.js +378 -0
- package/dist/core/export/file-mapper.d.ts +71 -0
- package/dist/core/export/file-mapper.js +474 -0
- package/dist/core/export/grouper.d.ts +13 -0
- package/dist/core/export/grouper.js +76 -0
- package/dist/core/export/index.d.ts +45 -0
- package/dist/core/export/index.js +63 -0
- package/dist/core/export/types.d.ts +84 -0
- package/dist/core/export/types.js +25 -0
- package/dist/core/fixtures/empty-catalogs/postgres-15-16-baseline.json +287 -0
- package/dist/core/integrations/filter/dsl.d.ts +38 -1
- package/dist/core/integrations/filter/dsl.js +20 -2
- package/dist/core/integrations/filter/extractors.js +42 -0
- package/dist/core/integrations/integration-dsl.d.ts +10 -0
- package/dist/core/integrations/supabase.d.ts +8 -0
- package/dist/core/integrations/supabase.js +9 -0
- package/dist/core/objects/aggregate/aggregate.diff.d.ts +2 -8
- package/dist/core/objects/aggregate/aggregate.diff.js +16 -70
- package/dist/core/objects/aggregate/aggregate.model.d.ts +8 -8
- package/dist/core/objects/aggregate/aggregate.model.js +1 -1
- package/dist/core/objects/aggregate/changes/aggregate.create.js +1 -1
- package/dist/core/objects/aggregate/changes/aggregate.drop.js +1 -1
- package/dist/core/objects/base.privilege-diff.d.ts +38 -13
- package/dist/core/objects/base.privilege-diff.js +104 -22
- package/dist/core/objects/base.privilege.d.ts +1 -0
- package/dist/core/objects/base.privilege.js +9 -2
- package/dist/core/objects/collation/collation.diff.d.ts +2 -3
- package/dist/core/objects/diff-context.d.ts +15 -0
- package/dist/core/objects/diff-context.js +1 -0
- package/dist/core/objects/domain/changes/domain.create.js +4 -2
- package/dist/core/objects/domain/domain.diff.d.ts +2 -8
- package/dist/core/objects/domain/domain.diff.js +16 -77
- package/dist/core/objects/domain/domain.model.js +1 -1
- package/dist/core/objects/event-trigger/event-trigger.diff.d.ts +2 -3
- package/dist/core/objects/foreign-data-wrapper/foreign-data-wrapper/foreign-data-wrapper.diff.d.ts +2 -8
- package/dist/core/objects/foreign-data-wrapper/foreign-data-wrapper/foreign-data-wrapper.diff.js +13 -77
- package/dist/core/objects/foreign-data-wrapper/foreign-data-wrapper/foreign-data-wrapper.model.js +2 -2
- package/dist/core/objects/foreign-data-wrapper/foreign-table/foreign-table.diff.d.ts +2 -8
- package/dist/core/objects/foreign-data-wrapper/foreign-table/foreign-table.diff.js +16 -77
- package/dist/core/objects/foreign-data-wrapper/foreign-table/foreign-table.model.js +1 -1
- package/dist/core/objects/foreign-data-wrapper/server/server.diff.d.ts +2 -8
- package/dist/core/objects/foreign-data-wrapper/server/server.diff.js +13 -77
- package/dist/core/objects/language/language.diff.d.ts +2 -5
- package/dist/core/objects/language/language.diff.js +7 -39
- package/dist/core/objects/materialized-view/materialized-view.diff.d.ts +2 -8
- package/dist/core/objects/materialized-view/materialized-view.diff.js +16 -158
- package/dist/core/objects/materialized-view/materialized-view.model.d.ts +3 -3
- package/dist/core/objects/materialized-view/materialized-view.model.js +1 -1
- package/dist/core/objects/procedure/changes/procedure.alter.js +12 -12
- package/dist/core/objects/procedure/procedure.diff.d.ts +2 -8
- package/dist/core/objects/procedure/procedure.diff.js +16 -77
- package/dist/core/objects/procedure/procedure.model.d.ts +9 -9
- package/dist/core/objects/procedure/procedure.model.js +1 -1
- package/dist/core/objects/publication/changes/publication.alter.d.ts +0 -9
- package/dist/core/objects/publication/changes/publication.alter.js +0 -14
- package/dist/core/objects/publication/changes/publication.types.d.ts +2 -2
- package/dist/core/objects/publication/publication.diff.d.ts +2 -3
- package/dist/core/objects/publication/publication.diff.js +8 -13
- package/dist/core/objects/rls-policy/changes/rls-policy.alter.js +3 -3
- package/dist/core/objects/rls-policy/rls-policy.model.d.ts +2 -2
- package/dist/core/objects/role/role.diff.js +22 -1
- package/dist/core/objects/role/role.model.d.ts +4 -3
- package/dist/core/objects/role/role.model.js +118 -12
- package/dist/core/objects/rule/rule.model.d.ts +1 -1
- package/dist/core/objects/schema/schema.diff.d.ts +2 -8
- package/dist/core/objects/schema/schema.diff.js +16 -77
- package/dist/core/objects/schema/schema.model.js +1 -1
- package/dist/core/objects/sequence/sequence.diff.d.ts +2 -8
- package/dist/core/objects/sequence/sequence.diff.js +16 -79
- package/dist/core/objects/sequence/sequence.model.js +1 -1
- package/dist/core/objects/subscription/subscription.diff.d.ts +2 -3
- package/dist/core/objects/table/changes/table.create.js +3 -0
- package/dist/core/objects/table/table.diff.d.ts +2 -8
- package/dist/core/objects/table/table.diff.js +26 -157
- package/dist/core/objects/table/table.model.d.ts +23 -22
- package/dist/core/objects/table/table.model.js +1 -1
- package/dist/core/objects/trigger/changes/trigger.create.js +2 -4
- package/dist/core/objects/trigger/trigger.model.d.ts +8 -0
- package/dist/core/objects/trigger/trigger.model.js +11 -0
- package/dist/core/objects/type/composite-type/composite-type.diff.d.ts +2 -8
- package/dist/core/objects/type/composite-type/composite-type.diff.js +16 -77
- package/dist/core/objects/type/composite-type/composite-type.model.d.ts +3 -3
- package/dist/core/objects/type/composite-type/composite-type.model.js +2 -1
- package/dist/core/objects/type/enum/enum.diff.d.ts +2 -8
- package/dist/core/objects/type/enum/enum.diff.js +25 -112
- package/dist/core/objects/type/enum/enum.model.js +1 -1
- package/dist/core/objects/type/range/changes/range.create.js +6 -3
- package/dist/core/objects/type/range/range.diff.d.ts +2 -8
- package/dist/core/objects/type/range/range.diff.js +16 -77
- package/dist/core/objects/type/range/range.model.js +1 -1
- package/dist/core/objects/view/view.diff.d.ts +2 -8
- package/dist/core/objects/view/view.diff.js +16 -158
- package/dist/core/objects/view/view.model.d.ts +18 -4
- package/dist/core/objects/view/view.model.js +3 -13
- package/dist/core/plan/apply.js +9 -26
- package/dist/core/plan/create.d.ts +19 -6
- package/dist/core/plan/create.js +134 -174
- package/dist/core/plan/serialize.js +16 -4
- package/dist/core/plan/sql-format/fixtures.js +3 -5
- package/dist/core/plan/sql-format/keyword-case.js +26 -1
- package/dist/core/plan/ssl-config.d.ts +32 -0
- package/dist/core/plan/ssl-config.js +115 -0
- package/dist/core/plan/types.d.ts +6 -0
- package/dist/core/postgres-config.d.ts +14 -0
- package/dist/core/postgres-config.js +53 -2
- package/dist/core/sort/graph-builder.js +10 -0
- package/dist/core/sort/logical-sort.js +31 -23
- package/dist/core/test-utils/assert-valid-sql.d.ts +10 -0
- package/dist/core/test-utils/assert-valid-sql.js +19 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -1
- package/package.json +21 -4
- package/src/cli/app.ts +27 -3
- package/src/cli/bin/cli.ts +6 -0
- package/src/cli/commands/catalog-export.ts +78 -0
- package/src/cli/commands/declarative-apply.diagnostics.test.ts +77 -0
- package/src/cli/commands/declarative-apply.ts +380 -0
- package/src/cli/commands/declarative-export.ts +330 -0
- package/src/cli/commands/plan.ts +28 -7
- package/src/cli/exit-code.test.ts +19 -0
- package/src/cli/exit-code.ts +7 -0
- package/src/cli/formatters/tree/tree.ts +3 -2
- package/src/cli/utils/apply-display.test.ts +348 -0
- package/src/cli/utils/apply-display.ts +238 -0
- package/src/cli/utils/export-display.test.ts +103 -0
- package/src/cli/utils/export-display.ts +275 -0
- package/src/cli/utils/integrations.test.ts +44 -0
- package/src/cli/utils/resolve-input.test.ts +38 -0
- package/src/cli/utils/resolve-input.ts +17 -0
- package/src/core/catalog-export/index.ts +20 -0
- package/src/core/catalog.diff.ts +79 -78
- package/src/core/catalog.model.test.ts +122 -0
- package/src/core/catalog.model.ts +127 -1
- package/src/core/catalog.snapshot.test.ts +464 -0
- package/src/core/catalog.snapshot.ts +289 -0
- package/src/core/declarative-apply/discover-sql.test.ts +103 -0
- package/src/core/declarative-apply/discover-sql.ts +107 -0
- package/src/core/declarative-apply/extract-catalog-providers.ts +220 -0
- package/src/core/declarative-apply/index.test.ts +67 -0
- package/src/core/declarative-apply/index.ts +205 -0
- package/src/core/declarative-apply/round-apply.test.ts +504 -0
- package/src/core/declarative-apply/round-apply.ts +562 -0
- package/src/core/expand-replace-dependencies.test.ts +70 -0
- package/src/core/export/file-mapper.test.ts +816 -0
- package/src/core/export/file-mapper.ts +574 -0
- package/src/core/export/grouper.ts +108 -0
- package/src/core/export/index.ts +129 -0
- package/src/core/export/types.ts +104 -0
- package/src/core/fixtures/empty-catalogs/postgres-15-16-baseline.json +287 -0
- package/src/core/integrations/filter/dsl.test.ts +211 -0
- package/src/core/integrations/filter/dsl.ts +65 -3
- package/src/core/integrations/filter/extractors.test.ts +244 -0
- package/src/core/integrations/filter/extractors.ts +42 -0
- package/src/core/integrations/integration-dsl.ts +10 -0
- package/src/core/integrations/serialize/dsl.test.ts +91 -0
- package/src/core/integrations/supabase.ts +9 -0
- package/src/core/objects/aggregate/aggregate.diff.ts +39 -95
- package/src/core/objects/aggregate/aggregate.model.ts +1 -1
- package/src/core/objects/aggregate/changes/aggregate.alter.test.ts +3 -1
- package/src/core/objects/aggregate/changes/aggregate.comment.test.ts +5 -2
- package/src/core/objects/aggregate/changes/aggregate.create.test.ts +6 -3
- package/src/core/objects/aggregate/changes/aggregate.create.ts +1 -1
- package/src/core/objects/aggregate/changes/aggregate.drop.test.ts +7 -3
- package/src/core/objects/aggregate/changes/aggregate.drop.ts +1 -1
- package/src/core/objects/aggregate/changes/aggregate.privilege.test.ts +9 -3
- package/src/core/objects/base.privilege-diff.ts +178 -30
- package/src/core/objects/base.privilege.ts +9 -2
- package/src/core/objects/collation/changes/collation.alter.test.ts +7 -2
- package/src/core/objects/collation/changes/collation.create.test.ts +7 -2
- package/src/core/objects/collation/changes/collation.drop.test.ts +4 -1
- package/src/core/objects/collation/collation.diff.test.ts +9 -12
- package/src/core/objects/collation/collation.diff.ts +2 -1
- package/src/core/objects/diff-context.ts +16 -0
- package/src/core/objects/domain/changes/domain.alter.test.ts +28 -9
- package/src/core/objects/domain/changes/domain.create.test.ts +32 -2
- package/src/core/objects/domain/changes/domain.create.ts +7 -1
- package/src/core/objects/domain/changes/domain.drop.test.ts +4 -1
- package/src/core/objects/domain/domain.diff.ts +39 -102
- package/src/core/objects/domain/domain.model.ts +1 -1
- package/src/core/objects/event-trigger/changes/event-trigger.alter.test.ts +10 -3
- package/src/core/objects/event-trigger/changes/event-trigger.create.test.ts +4 -1
- package/src/core/objects/event-trigger/changes/event-trigger.drop.test.ts +4 -1
- package/src/core/objects/event-trigger/event-trigger.diff.test.ts +12 -7
- package/src/core/objects/event-trigger/event-trigger.diff.ts +2 -1
- package/src/core/objects/extension/changes/extension.alter.test.ts +7 -2
- package/src/core/objects/extension/changes/extension.create.test.ts +4 -1
- package/src/core/objects/extension/changes/extension.drop.test.ts +4 -1
- package/src/core/objects/extension/extension.model.test.ts +98 -0
- package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.alter.test.ts +16 -5
- package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.create.test.ts +51 -16
- package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.drop.test.ts +4 -1
- package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/foreign-data-wrapper.diff.test.ts +111 -4
- package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/foreign-data-wrapper.diff.ts +31 -101
- package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/foreign-data-wrapper.model.ts +2 -2
- package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.alter.test.ts +46 -15
- package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.create.test.ts +13 -4
- package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.drop.test.ts +4 -1
- package/src/core/objects/foreign-data-wrapper/foreign-table/foreign-table.diff.ts +39 -102
- package/src/core/objects/foreign-data-wrapper/foreign-table/foreign-table.model.ts +1 -1
- package/src/core/objects/foreign-data-wrapper/server/changes/server.alter.test.ts +22 -7
- package/src/core/objects/foreign-data-wrapper/server/changes/server.create.test.ts +19 -6
- package/src/core/objects/foreign-data-wrapper/server/changes/server.drop.test.ts +4 -1
- package/src/core/objects/foreign-data-wrapper/server/server.diff.test.ts +95 -0
- package/src/core/objects/foreign-data-wrapper/server/server.diff.ts +31 -101
- package/src/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.alter.test.ts +13 -4
- package/src/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.create.test.ts +16 -5
- package/src/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.drop.test.ts +10 -3
- package/src/core/objects/index/changes/index.alter.test.ts +13 -4
- package/src/core/objects/index/changes/index.create.test.ts +4 -1
- package/src/core/objects/index/changes/index.drop.test.ts +4 -1
- package/src/core/objects/language/changes/language.alter.test.ts +4 -1
- package/src/core/objects/language/changes/language.create.test.ts +4 -1
- package/src/core/objects/language/changes/language.drop.test.ts +4 -1
- package/src/core/objects/language/language.diff.test.ts +86 -4
- package/src/core/objects/language/language.diff.ts +17 -49
- package/src/core/objects/materialized-view/changes/materialized-view.alter.test.ts +10 -3
- package/src/core/objects/materialized-view/changes/materialized-view.create.test.ts +7 -2
- package/src/core/objects/materialized-view/changes/materialized-view.drop.test.ts +4 -1
- package/src/core/objects/materialized-view/materialized-view.diff.test.ts +162 -0
- package/src/core/objects/materialized-view/materialized-view.diff.ts +41 -191
- package/src/core/objects/materialized-view/materialized-view.model.ts +1 -1
- package/src/core/objects/procedure/changes/procedure.alter.test.ts +121 -49
- package/src/core/objects/procedure/changes/procedure.alter.ts +15 -12
- package/src/core/objects/procedure/changes/procedure.create.test.ts +4 -1
- package/src/core/objects/procedure/changes/procedure.drop.test.ts +7 -2
- package/src/core/objects/procedure/procedure.diff.ts +39 -102
- package/src/core/objects/procedure/procedure.model.ts +1 -1
- package/src/core/objects/publication/changes/publication.alter.test.ts +15 -21
- package/src/core/objects/publication/changes/publication.alter.ts +0 -18
- package/src/core/objects/publication/changes/publication.comment.test.ts +5 -2
- package/src/core/objects/publication/changes/publication.create.test.ts +5 -2
- package/src/core/objects/publication/changes/publication.drop.test.ts +3 -1
- package/src/core/objects/publication/changes/publication.types.ts +0 -2
- package/src/core/objects/publication/publication.diff.test.ts +24 -19
- package/src/core/objects/publication/publication.diff.ts +9 -15
- package/src/core/objects/rls-policy/changes/rls-policy.alter.test.ts +31 -14
- package/src/core/objects/rls-policy/changes/rls-policy.alter.ts +3 -3
- package/src/core/objects/rls-policy/changes/rls-policy.create.test.ts +10 -3
- package/src/core/objects/rls-policy/changes/rls-policy.drop.test.ts +4 -1
- package/src/core/objects/role/changes/role.alter.test.ts +31 -15
- package/src/core/objects/role/changes/role.create.test.ts +6 -2
- package/src/core/objects/role/changes/role.drop.test.ts +4 -1
- package/src/core/objects/role/role.diff.test.ts +235 -0
- package/src/core/objects/role/role.diff.ts +21 -1
- package/src/core/objects/role/role.model.ts +122 -14
- package/src/core/objects/rule/changes/rule.alter.test.ts +7 -3
- package/src/core/objects/rule/changes/rule.comment.test.ts +5 -2
- package/src/core/objects/rule/changes/rule.create.test.ts +6 -2
- package/src/core/objects/rule/changes/rule.drop.test.ts +3 -1
- package/src/core/objects/schema/changes/schema.alter.test.ts +4 -1
- package/src/core/objects/schema/changes/schema.create.test.ts +4 -1
- package/src/core/objects/schema/changes/schema.drop.test.ts +4 -1
- package/src/core/objects/schema/schema.diff.ts +39 -102
- package/src/core/objects/schema/schema.model.ts +1 -1
- package/src/core/objects/sequence/changes/sequence.alter.test.ts +11 -5
- package/src/core/objects/sequence/changes/sequence.create.test.ts +8 -3
- package/src/core/objects/sequence/changes/sequence.drop.test.ts +4 -1
- package/src/core/objects/sequence/sequence.diff.test.ts +114 -0
- package/src/core/objects/sequence/sequence.diff.ts +39 -104
- package/src/core/objects/sequence/sequence.model.ts +1 -1
- package/src/core/objects/subscription/changes/subscription.alter.test.ts +15 -5
- package/src/core/objects/subscription/changes/subscription.comment.test.ts +5 -2
- package/src/core/objects/subscription/changes/subscription.create.test.ts +5 -2
- package/src/core/objects/subscription/changes/subscription.drop.test.ts +3 -1
- package/src/core/objects/subscription/subscription.diff.test.ts +16 -11
- package/src/core/objects/subscription/subscription.diff.ts +2 -1
- package/src/core/objects/table/changes/table.alter.test.ts +38 -15
- package/src/core/objects/table/changes/table.create.test.ts +41 -3
- package/src/core/objects/table/changes/table.create.ts +4 -0
- package/src/core/objects/table/changes/table.drop.test.ts +3 -1
- package/src/core/objects/table/table.diff.test.ts +157 -0
- package/src/core/objects/table/table.diff.ts +54 -190
- package/src/core/objects/table/table.model.ts +1 -1
- package/src/core/objects/trigger/changes/trigger.alter.test.ts +8 -4
- package/src/core/objects/trigger/changes/trigger.create.test.ts +5 -1
- package/src/core/objects/trigger/changes/trigger.create.ts +7 -4
- package/src/core/objects/trigger/changes/trigger.drop.test.ts +5 -1
- package/src/core/objects/trigger/trigger.diff.test.ts +1 -0
- package/src/core/objects/trigger/trigger.model.ts +12 -0
- package/src/core/objects/type/composite-type/changes/composite-type.alter.test.ts +10 -4
- package/src/core/objects/type/composite-type/changes/composite-type.create.test.ts +7 -2
- package/src/core/objects/type/composite-type/changes/composite-type.drop.test.ts +4 -1
- package/src/core/objects/type/composite-type/composite-type.diff.test.ts +78 -0
- package/src/core/objects/type/composite-type/composite-type.diff.ts +39 -101
- package/src/core/objects/type/composite-type/composite-type.model.ts +2 -1
- package/src/core/objects/type/enum/changes/enum.alter.test.ts +14 -5
- package/src/core/objects/type/enum/changes/enum.create.test.ts +4 -1
- package/src/core/objects/type/enum/changes/enum.drop.test.ts +4 -1
- package/src/core/objects/type/enum/enum.diff.test.ts +181 -0
- package/src/core/objects/type/enum/enum.diff.ts +58 -146
- package/src/core/objects/type/enum/enum.model.ts +1 -1
- package/src/core/objects/type/range/changes/range.alter.test.ts +3 -1
- package/src/core/objects/type/range/changes/range.create.test.ts +5 -2
- package/src/core/objects/type/range/changes/range.create.ts +6 -2
- package/src/core/objects/type/range/changes/range.drop.test.ts +3 -1
- package/src/core/objects/type/range/range.diff.test.ts +77 -0
- package/src/core/objects/type/range/range.diff.ts +39 -101
- package/src/core/objects/type/range/range.model.ts +1 -1
- package/src/core/objects/view/changes/view.alter.test.ts +8 -3
- package/src/core/objects/view/changes/view.create.test.ts +7 -2
- package/src/core/objects/view/changes/view.drop.test.ts +4 -1
- package/src/core/objects/view/view.diff.test.ts +82 -0
- package/src/core/objects/view/view.diff.ts +41 -191
- package/src/core/objects/view/view.model.ts +3 -17
- package/src/core/plan/apply.ts +9 -27
- package/src/core/plan/create.ts +173 -237
- package/src/core/plan/serialize.test.ts +317 -0
- package/src/core/plan/serialize.ts +18 -4
- package/src/core/plan/sql-format/fixtures.ts +2 -5
- package/src/core/plan/sql-format/format-lowercase-coverage.test.ts +52 -0
- package/src/core/plan/sql-format/format-off.test.ts +14 -17
- package/src/core/plan/sql-format/format-pretty-lower-leading.test.ts +27 -22
- package/src/core/plan/sql-format/format-pretty-narrow.test.ts +17 -21
- package/src/core/plan/sql-format/format-pretty-preserve.test.ts +25 -20
- package/src/core/plan/sql-format/format-pretty-upper.test.ts +23 -20
- package/src/core/plan/sql-format/keyword-case.ts +36 -1
- package/src/core/plan/ssl-config.ts +172 -0
- package/src/core/plan/types.ts +6 -0
- package/src/core/postgres-config.ts +71 -2
- package/src/core/sort/graph-builder.ts +12 -0
- package/src/core/sort/logical-sort.test.ts +371 -0
- package/src/core/sort/logical-sort.ts +32 -25
- package/src/core/sort/topological-sort.test.ts +275 -0
- package/src/core/test-utils/assert-valid-sql.ts +20 -0
- package/src/index.ts +26 -2
|
@@ -494,10 +494,6 @@ describe("sql formatting snapshots", () => {
|
|
|
494
494
|
'insert, update, delete, truncate',
|
|
495
495
|
publish_via_partition_root = false);
|
|
496
496
|
|
|
497
|
-
-- publication.alter.set_all_tables
|
|
498
|
-
ALTER PUBLICATION pub_custom
|
|
499
|
-
SET FOR ALL TABLES;
|
|
500
|
-
|
|
501
497
|
-- publication.alter.set_list
|
|
502
498
|
ALTER PUBLICATION pub_custom
|
|
503
499
|
SET TABLE
|
|
@@ -636,31 +632,31 @@ describe("sql formatting snapshots", () => {
|
|
|
636
632
|
|
|
637
633
|
-- function.alter.change_owner
|
|
638
634
|
ALTER FUNCTION
|
|
639
|
-
public.calculate_metrics_for_analytics_dashboard_with_extended_name OWNER TO new_admin;
|
|
635
|
+
public.calculate_metrics_for_analytics_dashboard_with_extended_name(text, text, integer) OWNER TO new_admin;
|
|
640
636
|
|
|
641
637
|
-- function.alter.set_security
|
|
642
638
|
ALTER FUNCTION
|
|
643
|
-
public.calculate_metrics_for_analytics_dashboard_with_extended_name SECURITY INVOKER;
|
|
639
|
+
public.calculate_metrics_for_analytics_dashboard_with_extended_name(text, text, integer) SECURITY INVOKER;
|
|
644
640
|
|
|
645
641
|
-- function.alter.set_config
|
|
646
642
|
ALTER FUNCTION
|
|
647
|
-
public.calculate_metrics_for_analytics_dashboard_with_extended_name SET work_mem TO '256MB';
|
|
643
|
+
public.calculate_metrics_for_analytics_dashboard_with_extended_name(text, text, integer) SET work_mem TO '256MB';
|
|
648
644
|
|
|
649
645
|
-- function.alter.set_volatility
|
|
650
646
|
ALTER FUNCTION
|
|
651
|
-
public.calculate_metrics_for_analytics_dashboard_with_extended_name IMMUTABLE;
|
|
647
|
+
public.calculate_metrics_for_analytics_dashboard_with_extended_name(text, text, integer) IMMUTABLE;
|
|
652
648
|
|
|
653
649
|
-- function.alter.set_strictness
|
|
654
650
|
ALTER FUNCTION
|
|
655
|
-
public.calculate_metrics_for_analytics_dashboard_with_extended_name CALLED ON NULL INPUT;
|
|
651
|
+
public.calculate_metrics_for_analytics_dashboard_with_extended_name(text, text, integer) CALLED ON NULL INPUT;
|
|
656
652
|
|
|
657
653
|
-- function.alter.set_leakproof
|
|
658
654
|
ALTER FUNCTION
|
|
659
|
-
public.calculate_metrics_for_analytics_dashboard_with_extended_name LEAKPROOF;
|
|
655
|
+
public.calculate_metrics_for_analytics_dashboard_with_extended_name(text, text, integer) LEAKPROOF;
|
|
660
656
|
|
|
661
657
|
-- function.alter.set_parallel
|
|
662
658
|
ALTER FUNCTION
|
|
663
|
-
public.calculate_metrics_for_analytics_dashboard_with_extended_name PARALLEL RESTRICTED;
|
|
659
|
+
public.calculate_metrics_for_analytics_dashboard_with_extended_name(text, text, integer) PARALLEL RESTRICTED;
|
|
664
660
|
|
|
665
661
|
-- function.comment
|
|
666
662
|
COMMENT ON FUNCTION
|
|
@@ -746,17 +742,17 @@ describe("sql formatting snapshots", () => {
|
|
|
746
742
|
public.table_with_very_long_name_for_formatting_and_wrapping_test;
|
|
747
743
|
|
|
748
744
|
-- policy.alter.set_roles
|
|
749
|
-
ALTER POLICY
|
|
745
|
+
ALTER POLICY allow_select_own
|
|
750
746
|
ON
|
|
751
747
|
public.table_with_very_long_name_for_formatting_and_wrapping_test TO authenticated, anon;
|
|
752
748
|
|
|
753
749
|
-- policy.alter.set_using
|
|
754
|
-
ALTER POLICY
|
|
750
|
+
ALTER POLICY allow_select_own
|
|
755
751
|
ON
|
|
756
752
|
public.table_with_very_long_name_for_formatting_and_wrapping_test USING (auth.uid() = user_id AND status = 'active');
|
|
757
753
|
|
|
758
754
|
-- policy.alter.set_with_check
|
|
759
|
-
ALTER POLICY
|
|
755
|
+
ALTER POLICY allow_select_own
|
|
760
756
|
ON
|
|
761
757
|
public.table_with_very_long_name_for_formatting_and_wrapping_test WITH CHECK (auth.uid() = user_id);
|
|
762
758
|
|
|
@@ -906,7 +902,7 @@ describe("sql formatting snapshots", () => {
|
|
|
906
902
|
STYPE = anycompatiblearray,
|
|
907
903
|
COMBINEFUNC = array_cat,
|
|
908
904
|
INITCOND = '{}',
|
|
909
|
-
PARALLEL SAFE,
|
|
905
|
+
PARALLEL = SAFE,
|
|
910
906
|
STRICT
|
|
911
907
|
);
|
|
912
908
|
|
|
@@ -1201,18 +1197,18 @@ describe("sql formatting snapshots", () => {
|
|
|
1201
1197
|
NULL;
|
|
1202
1198
|
|
|
1203
1199
|
-- foreign_table.grant
|
|
1204
|
-
GRANT SELECT
|
|
1205
|
-
|
|
1200
|
+
GRANT SELECT
|
|
1201
|
+
ON TABLE public.remote_users TO
|
|
1206
1202
|
app_reader;
|
|
1207
1203
|
|
|
1208
1204
|
-- foreign_table.revoke
|
|
1209
|
-
REVOKE SELECT
|
|
1210
|
-
|
|
1205
|
+
REVOKE SELECT
|
|
1206
|
+
ON TABLE public.remote_users FROM
|
|
1211
1207
|
app_reader;
|
|
1212
1208
|
|
|
1213
1209
|
-- foreign_table.revoke_grant_option
|
|
1214
|
-
REVOKE GRANT OPTION FOR SELECT
|
|
1215
|
-
|
|
1210
|
+
REVOKE GRANT OPTION FOR SELECT
|
|
1211
|
+
ON TABLE public.remote_users FROM
|
|
1216
1212
|
app_reader;
|
|
1217
1213
|
|
|
1218
1214
|
-- server.create
|
|
@@ -401,9 +401,6 @@ describe("sql formatting snapshots", () => {
|
|
|
401
401
|
ALTER PUBLICATION pub_custom
|
|
402
402
|
SET (publish = 'insert, update, delete, truncate', publish_via_partition_root = false);
|
|
403
403
|
|
|
404
|
-
-- publication.alter.set_all_tables
|
|
405
|
-
ALTER PUBLICATION pub_custom SET FOR ALL TABLES;
|
|
406
|
-
|
|
407
404
|
-- publication.alter.set_list
|
|
408
405
|
ALTER PUBLICATION pub_custom
|
|
409
406
|
SET TABLE
|
|
@@ -518,28 +515,38 @@ describe("sql formatting snapshots", () => {
|
|
|
518
515
|
IN "p_table_name_for_metrics" text, IN "p_limit_count_default" integer);
|
|
519
516
|
|
|
520
517
|
-- function.alter.change_owner
|
|
521
|
-
ALTER FUNCTION
|
|
518
|
+
ALTER FUNCTION
|
|
519
|
+
public.calculate_metrics_for_analytics_dashboard_with_extended_name(text, text, integer) OWNER TO
|
|
522
520
|
new_admin;
|
|
523
521
|
|
|
524
522
|
-- function.alter.set_security
|
|
525
|
-
ALTER FUNCTION
|
|
523
|
+
ALTER FUNCTION
|
|
524
|
+
public.calculate_metrics_for_analytics_dashboard_with_extended_name(text, text, integer) SECURITY
|
|
525
|
+
INVOKER;
|
|
526
526
|
|
|
527
527
|
-- function.alter.set_config
|
|
528
|
-
ALTER FUNCTION
|
|
528
|
+
ALTER FUNCTION
|
|
529
|
+
public.calculate_metrics_for_analytics_dashboard_with_extended_name(text, text, integer)
|
|
529
530
|
SET work_mem TO '256MB';
|
|
530
531
|
|
|
531
532
|
-- function.alter.set_volatility
|
|
532
|
-
ALTER FUNCTION
|
|
533
|
+
ALTER FUNCTION
|
|
534
|
+
public.calculate_metrics_for_analytics_dashboard_with_extended_name(text, text, integer)
|
|
535
|
+
IMMUTABLE;
|
|
533
536
|
|
|
534
537
|
-- function.alter.set_strictness
|
|
535
|
-
ALTER FUNCTION
|
|
538
|
+
ALTER FUNCTION
|
|
539
|
+
public.calculate_metrics_for_analytics_dashboard_with_extended_name(text, text, integer) CALLED
|
|
536
540
|
ON NULL INPUT;
|
|
537
541
|
|
|
538
542
|
-- function.alter.set_leakproof
|
|
539
|
-
ALTER FUNCTION
|
|
543
|
+
ALTER FUNCTION
|
|
544
|
+
public.calculate_metrics_for_analytics_dashboard_with_extended_name(text, text, integer)
|
|
545
|
+
LEAKPROOF;
|
|
540
546
|
|
|
541
547
|
-- function.alter.set_parallel
|
|
542
|
-
ALTER FUNCTION
|
|
548
|
+
ALTER FUNCTION
|
|
549
|
+
public.calculate_metrics_for_analytics_dashboard_with_extended_name(text, text, integer) PARALLEL
|
|
543
550
|
RESTRICTED;
|
|
544
551
|
|
|
545
552
|
-- function.comment
|
|
@@ -620,18 +627,16 @@ describe("sql formatting snapshots", () => {
|
|
|
620
627
|
DROP POLICY allow_select_own ON public.table_with_very_long_name_for_formatting_and_wrapping_test;
|
|
621
628
|
|
|
622
629
|
-- policy.alter.set_roles
|
|
623
|
-
ALTER POLICY
|
|
630
|
+
ALTER POLICY allow_select_own
|
|
624
631
|
ON public.table_with_very_long_name_for_formatting_and_wrapping_test TO authenticated, anon;
|
|
625
632
|
|
|
626
633
|
-- policy.alter.set_using
|
|
627
|
-
ALTER POLICY public.
|
|
628
|
-
ON public.table_with_very_long_name_for_formatting_and_wrapping_test
|
|
634
|
+
ALTER POLICY allow_select_own ON public.table_with_very_long_name_for_formatting_and_wrapping_test
|
|
629
635
|
USING (auth.uid() = user_id AND status = 'active');
|
|
630
636
|
|
|
631
637
|
-- policy.alter.set_with_check
|
|
632
|
-
ALTER POLICY public.
|
|
633
|
-
|
|
634
|
-
CHECK (auth.uid() = user_id);
|
|
638
|
+
ALTER POLICY allow_select_own ON public.table_with_very_long_name_for_formatting_and_wrapping_test
|
|
639
|
+
WITH CHECK (auth.uid() = user_id);
|
|
635
640
|
|
|
636
641
|
-- policy.comment
|
|
637
642
|
COMMENT ON POLICY allow_select_own
|
|
@@ -741,7 +746,7 @@ describe("sql formatting snapshots", () => {
|
|
|
741
746
|
STYPE = anycompatiblearray,
|
|
742
747
|
COMBINEFUNC = array_cat,
|
|
743
748
|
INITCOND = '{}',
|
|
744
|
-
PARALLEL SAFE,
|
|
749
|
+
PARALLEL = SAFE,
|
|
745
750
|
STRICT
|
|
746
751
|
);
|
|
747
752
|
|
|
@@ -986,13 +991,13 @@ describe("sql formatting snapshots", () => {
|
|
|
986
991
|
COMMENT ON FOREIGN TABLE public.remote_users IS NULL;
|
|
987
992
|
|
|
988
993
|
-- foreign_table.grant
|
|
989
|
-
GRANT SELECT ON
|
|
994
|
+
GRANT SELECT ON TABLE public.remote_users TO app_reader;
|
|
990
995
|
|
|
991
996
|
-- foreign_table.revoke
|
|
992
|
-
REVOKE SELECT ON
|
|
997
|
+
REVOKE SELECT ON TABLE public.remote_users FROM app_reader;
|
|
993
998
|
|
|
994
999
|
-- foreign_table.revoke_grant_option
|
|
995
|
-
REVOKE GRANT OPTION FOR SELECT ON
|
|
1000
|
+
REVOKE GRANT OPTION FOR SELECT ON TABLE public.remote_users FROM app_reader;
|
|
996
1001
|
|
|
997
1002
|
-- server.create
|
|
998
1003
|
CREATE SERVER remote_server
|
|
@@ -396,9 +396,6 @@ describe("sql formatting snapshots", () => {
|
|
|
396
396
|
ALTER PUBLICATION pub_custom
|
|
397
397
|
SET (publish = 'insert, update, delete, truncate', publish_via_partition_root = false);
|
|
398
398
|
|
|
399
|
-
-- publication.alter.set_all_tables
|
|
400
|
-
ALTER PUBLICATION pub_custom SET FOR ALL TABLES;
|
|
401
|
-
|
|
402
399
|
-- publication.alter.set_list
|
|
403
400
|
ALTER PUBLICATION pub_custom
|
|
404
401
|
SET TABLE
|
|
@@ -513,28 +510,36 @@ describe("sql formatting snapshots", () => {
|
|
|
513
510
|
IN "p_table_name_for_metrics" text, IN "p_limit_count_default" integer);
|
|
514
511
|
|
|
515
512
|
-- function.alter.change_owner
|
|
516
|
-
ALTER FUNCTION
|
|
513
|
+
ALTER FUNCTION
|
|
514
|
+
public.calculate_metrics_for_analytics_dashboard_with_extended_name(text, text, integer) OWNER TO
|
|
517
515
|
new_admin;
|
|
518
516
|
|
|
519
517
|
-- function.alter.set_security
|
|
520
|
-
ALTER FUNCTION
|
|
518
|
+
ALTER FUNCTION
|
|
519
|
+
public.calculate_metrics_for_analytics_dashboard_with_extended_name(text, text, integer) SECURITY
|
|
520
|
+
INVOKER;
|
|
521
521
|
|
|
522
522
|
-- function.alter.set_config
|
|
523
|
-
ALTER FUNCTION
|
|
523
|
+
ALTER FUNCTION
|
|
524
|
+
public.calculate_metrics_for_analytics_dashboard_with_extended_name(text, text, integer)
|
|
524
525
|
SET work_mem TO '256MB';
|
|
525
526
|
|
|
526
527
|
-- function.alter.set_volatility
|
|
527
|
-
ALTER FUNCTION
|
|
528
|
+
ALTER FUNCTION
|
|
529
|
+
public.calculate_metrics_for_analytics_dashboard_with_extended_name(text, text, integer) IMMUTABLE;
|
|
528
530
|
|
|
529
531
|
-- function.alter.set_strictness
|
|
530
|
-
ALTER FUNCTION
|
|
532
|
+
ALTER FUNCTION
|
|
533
|
+
public.calculate_metrics_for_analytics_dashboard_with_extended_name(text, text, integer) CALLED
|
|
531
534
|
ON NULL INPUT;
|
|
532
535
|
|
|
533
536
|
-- function.alter.set_leakproof
|
|
534
|
-
ALTER FUNCTION
|
|
537
|
+
ALTER FUNCTION
|
|
538
|
+
public.calculate_metrics_for_analytics_dashboard_with_extended_name(text, text, integer) LEAKPROOF;
|
|
535
539
|
|
|
536
540
|
-- function.alter.set_parallel
|
|
537
|
-
ALTER FUNCTION
|
|
541
|
+
ALTER FUNCTION
|
|
542
|
+
public.calculate_metrics_for_analytics_dashboard_with_extended_name(text, text, integer) PARALLEL
|
|
538
543
|
RESTRICTED;
|
|
539
544
|
|
|
540
545
|
-- function.comment
|
|
@@ -613,18 +618,16 @@ describe("sql formatting snapshots", () => {
|
|
|
613
618
|
DROP POLICY allow_select_own ON public.table_with_very_long_name_for_formatting_and_wrapping_test;
|
|
614
619
|
|
|
615
620
|
-- policy.alter.set_roles
|
|
616
|
-
ALTER POLICY
|
|
621
|
+
ALTER POLICY allow_select_own
|
|
617
622
|
ON public.table_with_very_long_name_for_formatting_and_wrapping_test TO authenticated, anon;
|
|
618
623
|
|
|
619
624
|
-- policy.alter.set_using
|
|
620
|
-
ALTER POLICY public.
|
|
621
|
-
ON public.table_with_very_long_name_for_formatting_and_wrapping_test
|
|
625
|
+
ALTER POLICY allow_select_own ON public.table_with_very_long_name_for_formatting_and_wrapping_test
|
|
622
626
|
USING (auth.uid() = user_id AND status = 'active');
|
|
623
627
|
|
|
624
628
|
-- policy.alter.set_with_check
|
|
625
|
-
ALTER POLICY public.
|
|
626
|
-
|
|
627
|
-
CHECK (auth.uid() = user_id);
|
|
629
|
+
ALTER POLICY allow_select_own ON public.table_with_very_long_name_for_formatting_and_wrapping_test
|
|
630
|
+
WITH CHECK (auth.uid() = user_id);
|
|
628
631
|
|
|
629
632
|
-- policy.comment
|
|
630
633
|
COMMENT ON POLICY allow_select_own
|
|
@@ -734,7 +737,7 @@ describe("sql formatting snapshots", () => {
|
|
|
734
737
|
STYPE = anycompatiblearray,
|
|
735
738
|
COMBINEFUNC = array_cat,
|
|
736
739
|
INITCOND = '{}',
|
|
737
|
-
PARALLEL SAFE,
|
|
740
|
+
PARALLEL = SAFE,
|
|
738
741
|
STRICT
|
|
739
742
|
);
|
|
740
743
|
|
|
@@ -979,13 +982,13 @@ describe("sql formatting snapshots", () => {
|
|
|
979
982
|
COMMENT ON FOREIGN TABLE public.remote_users IS NULL;
|
|
980
983
|
|
|
981
984
|
-- foreign_table.grant
|
|
982
|
-
GRANT SELECT ON
|
|
985
|
+
GRANT SELECT ON TABLE public.remote_users TO app_reader;
|
|
983
986
|
|
|
984
987
|
-- foreign_table.revoke
|
|
985
|
-
REVOKE SELECT ON
|
|
988
|
+
REVOKE SELECT ON TABLE public.remote_users FROM app_reader;
|
|
986
989
|
|
|
987
990
|
-- foreign_table.revoke_grant_option
|
|
988
|
-
REVOKE GRANT OPTION FOR SELECT ON
|
|
991
|
+
REVOKE GRANT OPTION FOR SELECT ON TABLE public.remote_users FROM app_reader;
|
|
989
992
|
|
|
990
993
|
-- server.create
|
|
991
994
|
CREATE SERVER remote_server
|
|
@@ -207,6 +207,36 @@ const STRUCTURAL_TOP_LEVEL_KEYWORDS = new Set([
|
|
|
207
207
|
"WRAPPER",
|
|
208
208
|
"MAPPING",
|
|
209
209
|
]);
|
|
210
|
+
const EMPTY_SCOPED_SET: ReadonlySet<string> = new Set();
|
|
211
|
+
|
|
212
|
+
const ALTER_DEFAULT_PRIVILEGES_KEYWORDS: ReadonlySet<string> = new Set([
|
|
213
|
+
"PUBLIC",
|
|
214
|
+
"SEQUENCES",
|
|
215
|
+
"ROUTINES",
|
|
216
|
+
"TYPES",
|
|
217
|
+
"SCHEMAS",
|
|
218
|
+
]);
|
|
219
|
+
|
|
220
|
+
const GRANT_REVOKE_KEYWORDS: ReadonlySet<string> = new Set(["PUBLIC"]);
|
|
221
|
+
|
|
222
|
+
function getStatementScopedKeywords(
|
|
223
|
+
topLevelTokens: Array<{ token: Token; index: number }>,
|
|
224
|
+
): ReadonlySet<string> {
|
|
225
|
+
const first = topLevelTokens[0]?.token.upper;
|
|
226
|
+
const second = topLevelTokens[1]?.token.upper;
|
|
227
|
+
const third = topLevelTokens[2]?.token.upper;
|
|
228
|
+
|
|
229
|
+
if (first === "ALTER" && second === "DEFAULT" && third === "PRIVILEGES") {
|
|
230
|
+
return ALTER_DEFAULT_PRIVILEGES_KEYWORDS;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (first === "GRANT" || first === "REVOKE") {
|
|
234
|
+
return GRANT_REVOKE_KEYWORDS;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return EMPTY_SCOPED_SET;
|
|
238
|
+
}
|
|
239
|
+
|
|
210
240
|
const ALTER_TYPE_BOUNDARY_KEYWORDS = new Set([
|
|
211
241
|
"COLLATE",
|
|
212
242
|
"USING",
|
|
@@ -287,6 +317,7 @@ function collectCaseableTokenStarts(
|
|
|
287
317
|
if (topLevelTokens.length === 0) return caseable;
|
|
288
318
|
|
|
289
319
|
const command = topLevelTokens[0].token.upper;
|
|
320
|
+
const scopedKeywords = getStatementScopedKeywords(topLevelTokens);
|
|
290
321
|
const objectNameTokenIndexes = new Set<number>();
|
|
291
322
|
for (let topIndex = 0; topIndex < topLevelTokens.length; topIndex += 1) {
|
|
292
323
|
if (isLikelyObjectNameToken(command, topLevelTokens, topIndex)) {
|
|
@@ -297,7 +328,8 @@ function collectCaseableTokenStarts(
|
|
|
297
328
|
for (let index = 0; index < tokens.length; index += 1) {
|
|
298
329
|
const token = tokens[index];
|
|
299
330
|
const upper = token.upper;
|
|
300
|
-
if (!STRUCTURAL_TOP_LEVEL_KEYWORDS.has(upper))
|
|
331
|
+
if (!STRUCTURAL_TOP_LEVEL_KEYWORDS.has(upper) && !scopedKeywords.has(upper))
|
|
332
|
+
continue;
|
|
301
333
|
if (objectNameTokenIndexes.has(index)) continue;
|
|
302
334
|
if (isQualifiedIdentifierToken(statement, token)) continue;
|
|
303
335
|
|
|
@@ -436,6 +468,9 @@ function isCaseableInContext(
|
|
|
436
468
|
if (upper === "IDENTITY") {
|
|
437
469
|
return prev === "REPLICA" || prev === "AS";
|
|
438
470
|
}
|
|
471
|
+
if (upper === "PUBLIC") {
|
|
472
|
+
return prev === "TO" || prev === "FROM";
|
|
473
|
+
}
|
|
439
474
|
if (upper === "OR") {
|
|
440
475
|
return true;
|
|
441
476
|
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSL configuration parsing for PostgreSQL connection URLs.
|
|
3
|
+
*
|
|
4
|
+
* Supports sslmode and certificate paths (URL params or env). Used by plan,
|
|
5
|
+
* apply, and catalog-export when connecting to source/target databases.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { readFile } from "node:fs/promises";
|
|
9
|
+
|
|
10
|
+
/** Parsed SSL options for the pg client plus URL with SSL params stripped (internal). */
|
|
11
|
+
type SslConfig = {
|
|
12
|
+
ssl?:
|
|
13
|
+
| boolean
|
|
14
|
+
| {
|
|
15
|
+
rejectUnauthorized: boolean;
|
|
16
|
+
ca?: string;
|
|
17
|
+
cert?: string;
|
|
18
|
+
key?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Custom server identity check function.
|
|
21
|
+
* Used to skip hostname verification for verify-ca mode.
|
|
22
|
+
* Returns undefined to indicate success (no error).
|
|
23
|
+
*/
|
|
24
|
+
checkServerIdentity?: () => undefined;
|
|
25
|
+
};
|
|
26
|
+
cleanedUrl: string;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Parse SSL configuration from a PostgreSQL connection URL.
|
|
31
|
+
* Supports sslmode (require, verify-ca, verify-full, prefer, disable).
|
|
32
|
+
* Certificates can be provided via:
|
|
33
|
+
* - Query string parameters (file paths): sslrootcert, sslcert, sslkey (preferred)
|
|
34
|
+
* - Environment variables (content): PGDELTA_SOURCE_SSLROOTCERT/SSLCERT/SSLKEY or PGDELTA_TARGET_SSLROOTCERT/SSLCERT/SSLKEY
|
|
35
|
+
* Returns SSL options for the postgres.js library and a cleaned URL without SSL-related query parameters.
|
|
36
|
+
*/
|
|
37
|
+
export async function parseSslConfig(
|
|
38
|
+
url: string,
|
|
39
|
+
connectionType: "source" | "target",
|
|
40
|
+
): Promise<SslConfig> {
|
|
41
|
+
const urlObj = new URL(url);
|
|
42
|
+
const sslmode = urlObj.searchParams.get("sslmode");
|
|
43
|
+
const sslrootcert = urlObj.searchParams.get("sslrootcert");
|
|
44
|
+
const sslcert = urlObj.searchParams.get("sslcert");
|
|
45
|
+
const sslkey = urlObj.searchParams.get("sslkey");
|
|
46
|
+
|
|
47
|
+
// Remove SSL-related query parameters since we parse them ourselves
|
|
48
|
+
urlObj.searchParams.delete("sslmode");
|
|
49
|
+
urlObj.searchParams.delete("sslrootcert");
|
|
50
|
+
urlObj.searchParams.delete("sslcert");
|
|
51
|
+
urlObj.searchParams.delete("sslkey");
|
|
52
|
+
const cleanedUrl = urlObj.toString();
|
|
53
|
+
|
|
54
|
+
// Handle different SSL modes
|
|
55
|
+
if (sslmode === "disable") {
|
|
56
|
+
return { cleanedUrl, ssl: false };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (
|
|
60
|
+
sslmode === "require" ||
|
|
61
|
+
sslmode === "prefer" ||
|
|
62
|
+
sslmode === "verify-ca" ||
|
|
63
|
+
sslmode === "verify-full"
|
|
64
|
+
) {
|
|
65
|
+
// Helper function to get certificate value: query param (file path) takes precedence over env var (content)
|
|
66
|
+
const getCertValue = async (
|
|
67
|
+
queryParam: string | null,
|
|
68
|
+
envVarName: string,
|
|
69
|
+
): Promise<string | undefined> => {
|
|
70
|
+
// Prefer query parameter (file path)
|
|
71
|
+
if (queryParam) {
|
|
72
|
+
try {
|
|
73
|
+
return await readFile(queryParam, "utf-8");
|
|
74
|
+
} catch (error) {
|
|
75
|
+
throw new Error(
|
|
76
|
+
`Failed to read certificate file '${queryParam}': ${error instanceof Error ? error.message : String(error)}`,
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// Fallback to environment variable (content)
|
|
81
|
+
const envValue = process.env[envVarName];
|
|
82
|
+
return envValue || undefined;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const hasExplicitVerification =
|
|
86
|
+
sslmode === "verify-ca" || sslmode === "verify-full";
|
|
87
|
+
|
|
88
|
+
// Get CA certificate value.
|
|
89
|
+
// - verify-ca/verify-full: check query param first, then env var
|
|
90
|
+
// - require/prefer: only check query param (libpq backward compatibility
|
|
91
|
+
// requires an explicit root CA *file*, not a global env var)
|
|
92
|
+
const caEnvVar =
|
|
93
|
+
connectionType === "source"
|
|
94
|
+
? "PGDELTA_SOURCE_SSLROOTCERT"
|
|
95
|
+
: "PGDELTA_TARGET_SSLROOTCERT";
|
|
96
|
+
let caValue: string | undefined;
|
|
97
|
+
if (sslrootcert) {
|
|
98
|
+
// Explicit file path in query param — always honour it
|
|
99
|
+
caValue = await getCertValue(sslrootcert, caEnvVar);
|
|
100
|
+
} else if (hasExplicitVerification) {
|
|
101
|
+
// verify-ca / verify-full without file path — fall back to env var
|
|
102
|
+
caValue = await getCertValue(null, caEnvVar);
|
|
103
|
+
}
|
|
104
|
+
// require/prefer without sslrootcert: no CA cert, no verification
|
|
105
|
+
|
|
106
|
+
// Determine if we should verify the CA chain
|
|
107
|
+
// From PostgreSQL docs: "if a root CA file exists, the behavior of sslmode=require
|
|
108
|
+
// will be the same as that of verify-ca"
|
|
109
|
+
const hasLibpqCompatibility =
|
|
110
|
+
(sslmode === "require" || sslmode === "prefer") && caValue !== undefined;
|
|
111
|
+
const shouldVerifyCa = hasExplicitVerification || hasLibpqCompatibility;
|
|
112
|
+
|
|
113
|
+
// Determine if we should verify hostname
|
|
114
|
+
// - verify-full: verify both CA and hostname
|
|
115
|
+
// - verify-ca: verify CA only (skip hostname)
|
|
116
|
+
// - require/prefer with CA (libpq compat): behaves like verify-ca (skip hostname)
|
|
117
|
+
const shouldVerifyHostname = sslmode === "verify-full";
|
|
118
|
+
|
|
119
|
+
const ssl: {
|
|
120
|
+
rejectUnauthorized: boolean;
|
|
121
|
+
ca?: string;
|
|
122
|
+
cert?: string;
|
|
123
|
+
key?: string;
|
|
124
|
+
checkServerIdentity?: () => undefined;
|
|
125
|
+
} = {
|
|
126
|
+
rejectUnauthorized: shouldVerifyCa,
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// Add CA certificate if verifying
|
|
130
|
+
if (shouldVerifyCa && caValue) {
|
|
131
|
+
ssl.ca = caValue;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// For verify-ca and libpq compatibility mode: skip hostname verification
|
|
135
|
+
// This matches PostgreSQL semantics where verify-ca only checks the CA chain
|
|
136
|
+
if (shouldVerifyCa && !shouldVerifyHostname) {
|
|
137
|
+
ssl.checkServerIdentity = () => undefined;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Get client certificate (optional, for mutual TLS)
|
|
141
|
+
const certEnvVar =
|
|
142
|
+
connectionType === "source"
|
|
143
|
+
? "PGDELTA_SOURCE_SSLCERT"
|
|
144
|
+
: "PGDELTA_TARGET_SSLCERT";
|
|
145
|
+
const certValue = await getCertValue(sslcert, certEnvVar);
|
|
146
|
+
if (certValue) {
|
|
147
|
+
ssl.cert = certValue;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Get client key (optional, for mutual TLS, required if cert is provided)
|
|
151
|
+
const keyEnvVar =
|
|
152
|
+
connectionType === "source"
|
|
153
|
+
? "PGDELTA_SOURCE_SSLKEY"
|
|
154
|
+
: "PGDELTA_TARGET_SSLKEY";
|
|
155
|
+
const keyValue = await getCertValue(sslkey, keyEnvVar);
|
|
156
|
+
if (keyValue) {
|
|
157
|
+
ssl.key = keyValue;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Warn if cert is provided without key (or vice versa)
|
|
161
|
+
if ((ssl.cert && !ssl.key) || (!ssl.cert && ssl.key)) {
|
|
162
|
+
throw new Error(
|
|
163
|
+
"Both client certificate and key must be provided together for mutual TLS",
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return { ssl, cleanedUrl };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// No sslmode specified or invalid value - explicitly disable SSL
|
|
171
|
+
return { cleanedUrl, ssl: false };
|
|
172
|
+
}
|
package/src/core/plan/types.ts
CHANGED
|
@@ -162,4 +162,10 @@ export interface CreatePlanOptions {
|
|
|
162
162
|
serialize?: SerializeDSL | ChangeSerializer;
|
|
163
163
|
/** Role to use when executing the migration (SET ROLE will be added to statements) */
|
|
164
164
|
role?: string;
|
|
165
|
+
/**
|
|
166
|
+
* When true, don't subtract privileges covered by ALTER DEFAULT PRIVILEGES
|
|
167
|
+
* from explicit GRANTs during diffing. Use this for declarative export where
|
|
168
|
+
* the output must be self-contained and not rely on statement execution order.
|
|
169
|
+
*/
|
|
170
|
+
skipDefaultPrivilegeSubtraction?: boolean;
|
|
165
171
|
}
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import type { PoolClient, PoolConfig } from "pg";
|
|
6
|
-
import { Pool, types } from "pg";
|
|
6
|
+
import { escapeIdentifier, Pool, types } from "pg";
|
|
7
|
+
import { parseSslConfig } from "./plan/ssl-config.ts";
|
|
7
8
|
|
|
8
9
|
// ============================================================================
|
|
9
10
|
// Array Parser
|
|
@@ -103,6 +104,12 @@ types.setTypeParser(1007, (val: string) => parseArray(val, parseIntElement)); //
|
|
|
103
104
|
// @ts-expect-error - pg types expects TypeId but raw OID numbers work fine
|
|
104
105
|
types.setTypeParser(1016, (val: string) => parseArray(val, parseIntElement)); // int8[]
|
|
105
106
|
|
|
107
|
+
const DEFAULT_POOL_MAX = Number(process.env.PGDELTA_POOL_MAX) || 5;
|
|
108
|
+
const DEFAULT_CONNECTION_TIMEOUT_MS =
|
|
109
|
+
Number(process.env.PGDELTA_CONNECTION_TIMEOUT_MS) || 3_000;
|
|
110
|
+
const DEFAULT_CONNECT_TIMEOUT_MS =
|
|
111
|
+
Number(process.env.PGDELTA_CONNECT_TIMEOUT_MS) || 2_500;
|
|
112
|
+
|
|
106
113
|
/**
|
|
107
114
|
* Options for creating a Pool with event listeners.
|
|
108
115
|
*/
|
|
@@ -125,7 +132,12 @@ export function createPool(
|
|
|
125
132
|
options?: CreatePoolOptions,
|
|
126
133
|
): Pool {
|
|
127
134
|
const { onConnect, onError, onAcquire, onRemove, ...config } = options ?? {};
|
|
128
|
-
const pool = new Pool({
|
|
135
|
+
const pool = new Pool({
|
|
136
|
+
connectionString,
|
|
137
|
+
max: DEFAULT_POOL_MAX,
|
|
138
|
+
connectionTimeoutMillis: DEFAULT_CONNECTION_TIMEOUT_MS,
|
|
139
|
+
...config,
|
|
140
|
+
});
|
|
129
141
|
|
|
130
142
|
if (onConnect) pool.on("connect", onConnect);
|
|
131
143
|
if (onError) pool.on("error", onError);
|
|
@@ -149,6 +161,63 @@ export function createPool(
|
|
|
149
161
|
* inside each `client.end()` callback — ensuring all sockets are
|
|
150
162
|
* truly closed before it resolves.
|
|
151
163
|
*/
|
|
164
|
+
/**
|
|
165
|
+
* Create a pool from a connection URL with standard session setup:
|
|
166
|
+
* SSL parsing, search_path isolation, optional SET ROLE, and 57P01 suppression.
|
|
167
|
+
*
|
|
168
|
+
* Returns the pool and a `close` function that properly waits for all sockets
|
|
169
|
+
* to close (via {@link endPool}).
|
|
170
|
+
*/
|
|
171
|
+
export async function createManagedPool(
|
|
172
|
+
url: string,
|
|
173
|
+
options?: { role?: string; label?: "source" | "target" },
|
|
174
|
+
): Promise<{ pool: Pool; close: () => Promise<void> }> {
|
|
175
|
+
const sslConfig = await parseSslConfig(url, options?.label ?? "target");
|
|
176
|
+
const pool = createPool(sslConfig.cleanedUrl, {
|
|
177
|
+
...(sslConfig.ssl !== undefined ? { ssl: sslConfig.ssl } : {}),
|
|
178
|
+
onError: (err: Error & { code?: string }) => {
|
|
179
|
+
if (err.code !== "57P01") {
|
|
180
|
+
console.error("Pool error:", err);
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
onConnect: async (client) => {
|
|
184
|
+
await client.query("SET search_path = ''");
|
|
185
|
+
if (options?.role) {
|
|
186
|
+
await client.query(`SET ROLE ${escapeIdentifier(options.role)}`);
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// Eagerly validate connectivity so SSL/auth failures surface immediately
|
|
192
|
+
// instead of hanging on the first real query. node-pg's connectionTimeoutMillis
|
|
193
|
+
// is not reliably enforced under Bun when SSL negotiation hangs.
|
|
194
|
+
const label = options?.label ?? "target";
|
|
195
|
+
const timeoutMs = DEFAULT_CONNECT_TIMEOUT_MS;
|
|
196
|
+
try {
|
|
197
|
+
const client = await Promise.race([
|
|
198
|
+
pool.connect(),
|
|
199
|
+
new Promise<never>((_, reject) =>
|
|
200
|
+
setTimeout(
|
|
201
|
+
() =>
|
|
202
|
+
reject(
|
|
203
|
+
new Error(
|
|
204
|
+
`Connection to ${label} database timed out after ${timeoutMs}ms. ` +
|
|
205
|
+
`The server may require SSL, use an invalid certificate, or be unreachable.`,
|
|
206
|
+
),
|
|
207
|
+
),
|
|
208
|
+
timeoutMs,
|
|
209
|
+
),
|
|
210
|
+
),
|
|
211
|
+
]);
|
|
212
|
+
client.release();
|
|
213
|
+
} catch (err) {
|
|
214
|
+
await pool.end().catch(() => {});
|
|
215
|
+
throw err;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return { pool, close: () => endPool(pool) };
|
|
219
|
+
}
|
|
220
|
+
|
|
152
221
|
export function endPool(pool: Pool): Promise<void> {
|
|
153
222
|
const clientCount = pool.totalCount;
|
|
154
223
|
|
|
@@ -87,7 +87,19 @@ export function convertExplicitRequirementsToConstraints(
|
|
|
87
87
|
|
|
88
88
|
if (requiredIds.size === 0) continue;
|
|
89
89
|
|
|
90
|
+
// Collect dropped IDs for this change so we can skip requirements
|
|
91
|
+
// for stableIds that this change also drops. A change that drops a
|
|
92
|
+
// stableId should not depend on another change that creates the same
|
|
93
|
+
// stableId, because the entity already exists in the source database.
|
|
94
|
+
// This prevents false ordering constraints such as Grant → Revoke
|
|
95
|
+
// when both operate on the same ACL stableId.
|
|
96
|
+
const droppedIds = new Set<string>(phaseChanges[consumerIndex].drops);
|
|
97
|
+
|
|
90
98
|
for (const requiredId of requiredIds) {
|
|
99
|
+
if (droppedIds.has(requiredId)) {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
|
|
91
103
|
const producerIndexes =
|
|
92
104
|
graphData.changeIndexesByCreatedId.get(requiredId);
|
|
93
105
|
if (!producerIndexes || producerIndexes.size === 0) continue;
|