@supabase/pg-delta 1.0.0-alpha.3 → 1.0.0-alpha.4
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 +22 -0
- package/dist/cli/bin/cli.js +0 -0
- package/dist/cli/commands/plan.js +21 -0
- package/dist/cli/utils.d.ts +2 -0
- package/dist/cli/utils.js +1 -1
- package/dist/core/objects/table/table.model.d.ts +4 -2
- package/dist/core/objects/table/table.model.js +3 -0
- package/dist/core/objects/trigger/changes/trigger.alter.js +23 -0
- package/dist/core/objects/trigger/changes/trigger.create.js +2 -1
- package/dist/core/objects/trigger/trigger.model.d.ts +1 -0
- package/dist/core/objects/trigger/trigger.model.js +3 -0
- package/dist/core/plan/apply.js +3 -3
- package/dist/core/plan/create.js +34 -15
- package/dist/core/plan/sql-format/constants.d.ts +2 -0
- package/dist/core/plan/sql-format/constants.js +11 -0
- package/dist/core/plan/sql-format/fixtures.d.ts +2 -0
- package/dist/core/plan/sql-format/fixtures.js +2449 -0
- package/dist/core/plan/sql-format/format-utils.d.ts +37 -0
- package/dist/core/plan/sql-format/format-utils.js +274 -0
- package/dist/core/plan/sql-format/formatters.d.ts +20 -0
- package/dist/core/plan/sql-format/formatters.js +737 -0
- package/dist/core/plan/sql-format/index.d.ts +2 -0
- package/dist/core/plan/sql-format/index.js +98 -0
- package/dist/core/plan/sql-format/keyword-case.d.ts +2 -0
- package/dist/core/plan/sql-format/keyword-case.js +868 -0
- package/dist/core/plan/sql-format/protect.d.ts +3 -0
- package/dist/core/plan/sql-format/protect.js +269 -0
- package/dist/core/plan/sql-format/sql-scanner.d.ts +59 -0
- package/dist/core/plan/sql-format/sql-scanner.js +202 -0
- package/dist/core/plan/sql-format/tokenizer.d.ts +22 -0
- package/dist/core/plan/sql-format/tokenizer.js +118 -0
- package/dist/core/plan/sql-format/types.d.ts +28 -0
- package/dist/core/plan/sql-format/types.js +1 -0
- package/dist/core/plan/sql-format/wrap.d.ts +2 -0
- package/dist/core/plan/sql-format/wrap.js +165 -0
- package/dist/core/plan/sql-format.d.ts +2 -0
- package/dist/core/plan/sql-format.js +1 -0
- package/dist/core/plan/statements.d.ts +2 -1
- package/dist/core/plan/statements.js +6 -2
- package/dist/core/postgres-config.d.ts +15 -0
- package/dist/core/postgres-config.js +30 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/package.json +37 -22
- package/src/cli/app.ts +28 -0
- package/src/cli/bin/cli.ts +9 -0
- package/src/cli/commands/apply.ts +101 -0
- package/src/cli/commands/plan.ts +195 -0
- package/src/cli/commands/sync.ts +185 -0
- package/src/cli/formatters/index.ts +5 -0
- package/src/cli/formatters/tree/tree-builder.ts +380 -0
- package/src/cli/formatters/tree/tree-renderer.ts +372 -0
- package/src/cli/formatters/tree/tree.ts +237 -0
- package/src/cli/utils/integrations.ts +42 -0
- package/src/cli/utils.ts +231 -0
- package/src/core/catalog.diff.ts +246 -0
- package/src/core/catalog.model.ts +384 -0
- package/src/core/change.types.ts +44 -0
- package/src/core/context.ts +26 -0
- package/src/core/depend.ts +1870 -0
- package/src/core/expand-replace-dependencies.ts +380 -0
- package/src/core/fingerprint.ts +204 -0
- package/src/core/integrations/filter/dsl.ts +204 -0
- package/src/core/integrations/filter/extractors.ts +145 -0
- package/src/core/integrations/filter/filter.types.ts +3 -0
- package/src/core/integrations/integration-dsl.ts +24 -0
- package/src/core/integrations/integration.types.ts +7 -0
- package/src/core/integrations/serialize/dsl.ts +77 -0
- package/src/core/integrations/serialize/serialize.types.ts +3 -0
- package/src/core/integrations/supabase.ts +121 -0
- package/src/core/objects/aggregate/aggregate.diff.test.ts +215 -0
- package/src/core/objects/aggregate/aggregate.diff.ts +278 -0
- package/src/core/objects/aggregate/aggregate.model.ts +317 -0
- package/src/core/objects/aggregate/changes/aggregate.alter.test.ts +64 -0
- package/src/core/objects/aggregate/changes/aggregate.alter.ts +32 -0
- package/src/core/objects/aggregate/changes/aggregate.base.ts +20 -0
- package/src/core/objects/aggregate/changes/aggregate.comment.test.ts +86 -0
- package/src/core/objects/aggregate/changes/aggregate.comment.ts +62 -0
- package/src/core/objects/aggregate/changes/aggregate.create.test.ts +101 -0
- package/src/core/objects/aggregate/changes/aggregate.create.ts +329 -0
- package/src/core/objects/aggregate/changes/aggregate.drop.test.ts +78 -0
- package/src/core/objects/aggregate/changes/aggregate.drop.ts +32 -0
- package/src/core/objects/aggregate/changes/aggregate.privilege.test.ts +130 -0
- package/src/core/objects/aggregate/changes/aggregate.privilege.ts +146 -0
- package/src/core/objects/aggregate/changes/aggregate.types.ts +12 -0
- package/src/core/objects/base.change.ts +62 -0
- package/src/core/objects/base.default-privileges.ts +204 -0
- package/src/core/objects/base.diff.ts +20 -0
- package/src/core/objects/base.model.ts +82 -0
- package/src/core/objects/base.privilege-diff.ts +299 -0
- package/src/core/objects/base.privilege.ts +184 -0
- package/src/core/objects/collation/changes/collation.alter.test.ts +63 -0
- package/src/core/objects/collation/changes/collation.alter.ts +79 -0
- package/src/core/objects/collation/changes/collation.base.ts +20 -0
- package/src/core/objects/collation/changes/collation.comment.ts +68 -0
- package/src/core/objects/collation/changes/collation.create.test.ts +51 -0
- package/src/core/objects/collation/changes/collation.create.ts +106 -0
- package/src/core/objects/collation/changes/collation.drop.test.ts +28 -0
- package/src/core/objects/collation/changes/collation.drop.ts +37 -0
- package/src/core/objects/collation/changes/collation.types.ts +10 -0
- package/src/core/objects/collation/collation.diff.test.ts +100 -0
- package/src/core/objects/collation/collation.diff.ts +126 -0
- package/src/core/objects/collation/collation.model.ts +224 -0
- package/src/core/objects/domain/changes/domain.alter.test.ts +316 -0
- package/src/core/objects/domain/changes/domain.alter.ts +286 -0
- package/src/core/objects/domain/changes/domain.base.ts +20 -0
- package/src/core/objects/domain/changes/domain.comment.ts +59 -0
- package/src/core/objects/domain/changes/domain.create.test.ts +65 -0
- package/src/core/objects/domain/changes/domain.create.ts +118 -0
- package/src/core/objects/domain/changes/domain.drop.test.ts +30 -0
- package/src/core/objects/domain/changes/domain.drop.ts +34 -0
- package/src/core/objects/domain/changes/domain.privilege.ts +171 -0
- package/src/core/objects/domain/changes/domain.types.ts +12 -0
- package/src/core/objects/domain/domain.diff.test.ts +284 -0
- package/src/core/objects/domain/domain.diff.ts +358 -0
- package/src/core/objects/domain/domain.model.ts +190 -0
- package/src/core/objects/event-trigger/changes/event-trigger.alter.test.ts +50 -0
- package/src/core/objects/event-trigger/changes/event-trigger.alter.ts +82 -0
- package/src/core/objects/event-trigger/changes/event-trigger.base.ts +20 -0
- package/src/core/objects/event-trigger/changes/event-trigger.comment.ts +66 -0
- package/src/core/objects/event-trigger/changes/event-trigger.create.test.ts +24 -0
- package/src/core/objects/event-trigger/changes/event-trigger.create.ts +72 -0
- package/src/core/objects/event-trigger/changes/event-trigger.drop.test.ts +22 -0
- package/src/core/objects/event-trigger/changes/event-trigger.drop.ts +34 -0
- package/src/core/objects/event-trigger/changes/event-trigger.types.ts +10 -0
- package/src/core/objects/event-trigger/event-trigger.diff.test.ts +126 -0
- package/src/core/objects/event-trigger/event-trigger.diff.ts +126 -0
- package/src/core/objects/event-trigger/event-trigger.model.ts +106 -0
- package/src/core/objects/extension/changes/extension.alter.test.ts +58 -0
- package/src/core/objects/extension/changes/extension.alter.ts +78 -0
- package/src/core/objects/extension/changes/extension.base.ts +20 -0
- package/src/core/objects/extension/changes/extension.comment.ts +64 -0
- package/src/core/objects/extension/changes/extension.create.test.ts +25 -0
- package/src/core/objects/extension/changes/extension.create.ts +63 -0
- package/src/core/objects/extension/changes/extension.drop.test.ts +23 -0
- package/src/core/objects/extension/changes/extension.drop.ts +34 -0
- package/src/core/objects/extension/changes/extension.types.ts +10 -0
- package/src/core/objects/extension/extension.diff.test.ts +42 -0
- package/src/core/objects/extension/extension.diff.ts +90 -0
- package/src/core/objects/extension/extension.model.ts +280 -0
- package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.alter.test.ts +125 -0
- package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.alter.ts +101 -0
- package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.base.ts +20 -0
- package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.comment.ts +72 -0
- package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.create.test.ts +125 -0
- package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.create.ts +95 -0
- package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.drop.test.ts +23 -0
- package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.drop.ts +36 -0
- package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.privilege.ts +172 -0
- package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.types.ts +12 -0
- package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/foreign-data-wrapper.diff.test.ts +179 -0
- package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/foreign-data-wrapper.diff.ts +341 -0
- package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/foreign-data-wrapper.model.ts +149 -0
- package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper.types.ts +10 -0
- package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.alter.test.ts +309 -0
- package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.alter.ts +341 -0
- package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.base.ts +20 -0
- package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.comment.ts +72 -0
- package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.create.test.ts +201 -0
- package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.create.ts +81 -0
- package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.drop.test.ts +43 -0
- package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.drop.ts +37 -0
- package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.privilege.ts +181 -0
- package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.types.ts +12 -0
- package/src/core/objects/foreign-data-wrapper/foreign-table/foreign-table.diff.test.ts +813 -0
- package/src/core/objects/foreign-data-wrapper/foreign-table/foreign-table.diff.ts +406 -0
- package/src/core/objects/foreign-data-wrapper/foreign-table/foreign-table.model.ts +242 -0
- package/src/core/objects/foreign-data-wrapper/server/changes/server.alter.test.ts +168 -0
- package/src/core/objects/foreign-data-wrapper/server/changes/server.alter.ts +126 -0
- package/src/core/objects/foreign-data-wrapper/server/changes/server.base.ts +20 -0
- package/src/core/objects/foreign-data-wrapper/server/changes/server.comment.ts +60 -0
- package/src/core/objects/foreign-data-wrapper/server/changes/server.create.test.ts +131 -0
- package/src/core/objects/foreign-data-wrapper/server/changes/server.create.ts +81 -0
- package/src/core/objects/foreign-data-wrapper/server/changes/server.drop.test.ts +24 -0
- package/src/core/objects/foreign-data-wrapper/server/changes/server.drop.ts +34 -0
- package/src/core/objects/foreign-data-wrapper/server/changes/server.privilege.ts +164 -0
- package/src/core/objects/foreign-data-wrapper/server/changes/server.types.ts +12 -0
- package/src/core/objects/foreign-data-wrapper/server/server.diff.test.ts +167 -0
- package/src/core/objects/foreign-data-wrapper/server/server.diff.ts +317 -0
- package/src/core/objects/foreign-data-wrapper/server/server.model.ts +133 -0
- package/src/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.alter.test.ts +82 -0
- package/src/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.alter.ts +69 -0
- package/src/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.base.ts +20 -0
- package/src/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.create.test.ts +85 -0
- package/src/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.create.ts +66 -0
- package/src/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.drop.test.ts +53 -0
- package/src/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.drop.ts +40 -0
- package/src/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.types.ts +8 -0
- package/src/core/objects/foreign-data-wrapper/user-mapping/user-mapping.diff.test.ts +77 -0
- package/src/core/objects/foreign-data-wrapper/user-mapping/user-mapping.diff.ts +107 -0
- package/src/core/objects/foreign-data-wrapper/user-mapping/user-mapping.model.ts +96 -0
- package/src/core/objects/index/changes/index.alter.test.ts +200 -0
- package/src/core/objects/index/changes/index.alter.ts +144 -0
- package/src/core/objects/index/changes/index.base.ts +20 -0
- package/src/core/objects/index/changes/index.comment.ts +63 -0
- package/src/core/objects/index/changes/index.create.test.ts +66 -0
- package/src/core/objects/index/changes/index.create.ts +68 -0
- package/src/core/objects/index/changes/index.drop.test.ts +44 -0
- package/src/core/objects/index/changes/index.drop.ts +34 -0
- package/src/core/objects/index/changes/index.types.ts +6 -0
- package/src/core/objects/index/changes/utils.ts +16 -0
- package/src/core/objects/index/index.diff.test.ts +153 -0
- package/src/core/objects/index/index.diff.ts +243 -0
- package/src/core/objects/index/index.model.ts +370 -0
- package/src/core/objects/language/changes/language.alter.test.ts +33 -0
- package/src/core/objects/language/changes/language.alter.ts +53 -0
- package/src/core/objects/language/changes/language.base.ts +20 -0
- package/src/core/objects/language/changes/language.comment.ts +58 -0
- package/src/core/objects/language/changes/language.create.test.ts +27 -0
- package/src/core/objects/language/changes/language.create.ts +104 -0
- package/src/core/objects/language/changes/language.drop.test.ts +25 -0
- package/src/core/objects/language/changes/language.drop.ts +39 -0
- package/src/core/objects/language/changes/language.privilege.ts +172 -0
- package/src/core/objects/language/changes/language.types.ts +12 -0
- package/src/core/objects/language/language.diff.test.ts +53 -0
- package/src/core/objects/language/language.diff.ts +176 -0
- package/src/core/objects/language/language.model.ts +150 -0
- package/src/core/objects/materialized-view/changes/materialized-view.alter.test.ts +123 -0
- package/src/core/objects/materialized-view/changes/materialized-view.alter.ts +113 -0
- package/src/core/objects/materialized-view/changes/materialized-view.base.ts +20 -0
- package/src/core/objects/materialized-view/changes/materialized-view.comment.ts +176 -0
- package/src/core/objects/materialized-view/changes/materialized-view.create.test.ts +64 -0
- package/src/core/objects/materialized-view/changes/materialized-view.create.ts +93 -0
- package/src/core/objects/materialized-view/changes/materialized-view.drop.test.ts +34 -0
- package/src/core/objects/materialized-view/changes/materialized-view.drop.ts +60 -0
- package/src/core/objects/materialized-view/changes/materialized-view.privilege.ts +212 -0
- package/src/core/objects/materialized-view/changes/materialized-view.types.ts +12 -0
- package/src/core/objects/materialized-view/materialized-view.diff.test.ts +102 -0
- package/src/core/objects/materialized-view/materialized-view.diff.ts +451 -0
- package/src/core/objects/materialized-view/materialized-view.model.ts +258 -0
- package/src/core/objects/procedure/changes/procedure.alter.test.ts +1005 -0
- package/src/core/objects/procedure/changes/procedure.alter.ts +287 -0
- package/src/core/objects/procedure/changes/procedure.base.ts +20 -0
- package/src/core/objects/procedure/changes/procedure.comment.ts +70 -0
- package/src/core/objects/procedure/changes/procedure.create.test.ts +48 -0
- package/src/core/objects/procedure/changes/procedure.create.ts +92 -0
- package/src/core/objects/procedure/changes/procedure.drop.test.ts +85 -0
- package/src/core/objects/procedure/changes/procedure.drop.ts +49 -0
- package/src/core/objects/procedure/changes/procedure.privilege.ts +188 -0
- package/src/core/objects/procedure/changes/procedure.types.ts +12 -0
- package/src/core/objects/procedure/procedure.diff.test.ts +161 -0
- package/src/core/objects/procedure/procedure.diff.ts +404 -0
- package/src/core/objects/procedure/procedure.model.ts +264 -0
- package/src/core/objects/procedure/utils.ts +58 -0
- package/src/core/objects/publication/changes/publication.alter.test.ts +223 -0
- package/src/core/objects/publication/changes/publication.alter.ts +243 -0
- package/src/core/objects/publication/changes/publication.base.ts +20 -0
- package/src/core/objects/publication/changes/publication.comment.test.ts +70 -0
- package/src/core/objects/publication/changes/publication.comment.ts +64 -0
- package/src/core/objects/publication/changes/publication.create.test.ts +87 -0
- package/src/core/objects/publication/changes/publication.create.ts +82 -0
- package/src/core/objects/publication/changes/publication.drop.test.ts +46 -0
- package/src/core/objects/publication/changes/publication.drop.ts +29 -0
- package/src/core/objects/publication/changes/publication.types.ts +26 -0
- package/src/core/objects/publication/publication.diff.test.ts +292 -0
- package/src/core/objects/publication/publication.diff.ts +253 -0
- package/src/core/objects/publication/publication.model.ts +206 -0
- package/src/core/objects/publication/utils.ts +55 -0
- package/src/core/objects/rls-policy/changes/rls-policy.alter.test.ts +250 -0
- package/src/core/objects/rls-policy/changes/rls-policy.alter.ts +128 -0
- package/src/core/objects/rls-policy/changes/rls-policy.base.ts +20 -0
- package/src/core/objects/rls-policy/changes/rls-policy.comment.ts +69 -0
- package/src/core/objects/rls-policy/changes/rls-policy.create.test.ts +74 -0
- package/src/core/objects/rls-policy/changes/rls-policy.create.ts +100 -0
- package/src/core/objects/rls-policy/changes/rls-policy.drop.test.ts +28 -0
- package/src/core/objects/rls-policy/changes/rls-policy.drop.ts +39 -0
- package/src/core/objects/rls-policy/changes/rls-policy.types.ts +10 -0
- package/src/core/objects/rls-policy/rls-policy.diff.test.ts +79 -0
- package/src/core/objects/rls-policy/rls-policy.diff.ts +121 -0
- package/src/core/objects/rls-policy/rls-policy.model.ts +140 -0
- package/src/core/objects/role/changes/role.alter.test.ts +346 -0
- package/src/core/objects/role/changes/role.alter.ts +110 -0
- package/src/core/objects/role/changes/role.base.ts +24 -0
- package/src/core/objects/role/changes/role.comment.ts +55 -0
- package/src/core/objects/role/changes/role.create.test.ts +52 -0
- package/src/core/objects/role/changes/role.create.ts +102 -0
- package/src/core/objects/role/changes/role.drop.test.ts +29 -0
- package/src/core/objects/role/changes/role.drop.ts +34 -0
- package/src/core/objects/role/changes/role.privilege.ts +376 -0
- package/src/core/objects/role/changes/role.types.ts +12 -0
- package/src/core/objects/role/role.diff.test.ts +44 -0
- package/src/core/objects/role/role.diff.ts +479 -0
- package/src/core/objects/role/role.model.ts +344 -0
- package/src/core/objects/rule/changes/rule.alter.test.ts +78 -0
- package/src/core/objects/rule/changes/rule.alter.ts +72 -0
- package/src/core/objects/rule/changes/rule.base.ts +20 -0
- package/src/core/objects/rule/changes/rule.comment.test.ts +55 -0
- package/src/core/objects/rule/changes/rule.comment.ts +62 -0
- package/src/core/objects/rule/changes/rule.create.test.ts +59 -0
- package/src/core/objects/rule/changes/rule.create.ts +42 -0
- package/src/core/objects/rule/changes/rule.drop.test.ts +38 -0
- package/src/core/objects/rule/changes/rule.drop.ts +29 -0
- package/src/core/objects/rule/changes/rule.types.ts +12 -0
- package/src/core/objects/rule/rule.diff.test.ts +132 -0
- package/src/core/objects/rule/rule.diff.ts +79 -0
- package/src/core/objects/rule/rule.model.ts +173 -0
- package/src/core/objects/schema/changes/schema.alter.test.ts +28 -0
- package/src/core/objects/schema/changes/schema.alter.ts +45 -0
- package/src/core/objects/schema/changes/schema.base.ts +20 -0
- package/src/core/objects/schema/changes/schema.comment.ts +56 -0
- package/src/core/objects/schema/changes/schema.create.test.ts +22 -0
- package/src/core/objects/schema/changes/schema.create.ts +47 -0
- package/src/core/objects/schema/changes/schema.drop.test.ts +20 -0
- package/src/core/objects/schema/changes/schema.drop.ts +34 -0
- package/src/core/objects/schema/changes/schema.privilege.ts +175 -0
- package/src/core/objects/schema/changes/schema.types.ts +12 -0
- package/src/core/objects/schema/schema.diff.test.ts +42 -0
- package/src/core/objects/schema/schema.diff.ts +209 -0
- package/src/core/objects/schema/schema.model.ts +107 -0
- package/src/core/objects/sequence/changes/sequence.alter.test.ts +151 -0
- package/src/core/objects/sequence/changes/sequence.alter.ts +115 -0
- package/src/core/objects/sequence/changes/sequence.base.ts +20 -0
- package/src/core/objects/sequence/changes/sequence.comment.ts +60 -0
- package/src/core/objects/sequence/changes/sequence.create.test.ts +84 -0
- package/src/core/objects/sequence/changes/sequence.create.ts +111 -0
- package/src/core/objects/sequence/changes/sequence.drop.test.ts +32 -0
- package/src/core/objects/sequence/changes/sequence.drop.ts +37 -0
- package/src/core/objects/sequence/changes/sequence.privilege.ts +179 -0
- package/src/core/objects/sequence/changes/sequence.types.ts +12 -0
- package/src/core/objects/sequence/sequence.diff.test.ts +141 -0
- package/src/core/objects/sequence/sequence.diff.ts +359 -0
- package/src/core/objects/sequence/sequence.model.ts +185 -0
- package/src/core/objects/subscription/changes/subscription.alter.test.ts +124 -0
- package/src/core/objects/subscription/changes/subscription.alter.ts +110 -0
- package/src/core/objects/subscription/changes/subscription.base.ts +20 -0
- package/src/core/objects/subscription/changes/subscription.comment.test.ts +67 -0
- package/src/core/objects/subscription/changes/subscription.comment.ts +64 -0
- package/src/core/objects/subscription/changes/subscription.create.test.ts +77 -0
- package/src/core/objects/subscription/changes/subscription.create.ts +69 -0
- package/src/core/objects/subscription/changes/subscription.drop.test.ts +46 -0
- package/src/core/objects/subscription/changes/subscription.drop.ts +20 -0
- package/src/core/objects/subscription/changes/subscription.types.ts +22 -0
- package/src/core/objects/subscription/subscription.diff.test.ts +232 -0
- package/src/core/objects/subscription/subscription.diff.ts +241 -0
- package/src/core/objects/subscription/subscription.model.ts +190 -0
- package/src/core/objects/subscription/utils.ts +156 -0
- package/src/core/objects/table/changes/table.alter.test.ts +823 -0
- package/src/core/objects/table/changes/table.alter.ts +806 -0
- package/src/core/objects/table/changes/table.base.ts +20 -0
- package/src/core/objects/table/changes/table.comment.ts +266 -0
- package/src/core/objects/table/changes/table.create.test.ts +150 -0
- package/src/core/objects/table/changes/table.create.ts +188 -0
- package/src/core/objects/table/changes/table.drop.test.ts +34 -0
- package/src/core/objects/table/changes/table.drop.ts +45 -0
- package/src/core/objects/table/changes/table.privilege.ts +200 -0
- package/src/core/objects/table/changes/table.types.ts +12 -0
- package/src/core/objects/table/table.diff.test.ts +711 -0
- package/src/core/objects/table/table.diff.ts +953 -0
- package/src/core/objects/table/table.model.ts +460 -0
- package/src/core/objects/trigger/changes/trigger.alter.test.ts +46 -0
- package/src/core/objects/trigger/changes/trigger.alter.ts +76 -0
- package/src/core/objects/trigger/changes/trigger.base.ts +20 -0
- package/src/core/objects/trigger/changes/trigger.comment.ts +64 -0
- package/src/core/objects/trigger/changes/trigger.create.test.ts +43 -0
- package/src/core/objects/trigger/changes/trigger.create.ts +85 -0
- package/src/core/objects/trigger/changes/trigger.drop.test.ts +43 -0
- package/src/core/objects/trigger/changes/trigger.drop.ts +39 -0
- package/src/core/objects/trigger/changes/trigger.types.ts +10 -0
- package/src/core/objects/trigger/trigger.diff.test.ts +83 -0
- package/src/core/objects/trigger/trigger.diff.ts +116 -0
- package/src/core/objects/trigger/trigger.model.ts +252 -0
- package/src/core/objects/type/composite-type/changes/composite-type.alter.test.ts +202 -0
- package/src/core/objects/type/composite-type/changes/composite-type.alter.ts +174 -0
- package/src/core/objects/type/composite-type/changes/composite-type.base.ts +20 -0
- package/src/core/objects/type/composite-type/changes/composite-type.comment.ts +145 -0
- package/src/core/objects/type/composite-type/changes/composite-type.create.test.ts +101 -0
- package/src/core/objects/type/composite-type/changes/composite-type.create.ts +95 -0
- package/src/core/objects/type/composite-type/changes/composite-type.drop.test.ts +33 -0
- package/src/core/objects/type/composite-type/changes/composite-type.drop.ts +37 -0
- package/src/core/objects/type/composite-type/changes/composite-type.privilege.ts +175 -0
- package/src/core/objects/type/composite-type/changes/composite-type.types.ts +12 -0
- package/src/core/objects/type/composite-type/composite-type.diff.test.ts +191 -0
- package/src/core/objects/type/composite-type/composite-type.diff.ts +372 -0
- package/src/core/objects/type/composite-type/composite-type.model.ts +252 -0
- package/src/core/objects/type/enum/changes/enum.alter.test.ts +104 -0
- package/src/core/objects/type/enum/changes/enum.alter.ts +91 -0
- package/src/core/objects/type/enum/changes/enum.base.ts +20 -0
- package/src/core/objects/type/enum/changes/enum.comment.ts +64 -0
- package/src/core/objects/type/enum/changes/enum.create.test.ts +28 -0
- package/src/core/objects/type/enum/changes/enum.create.ts +56 -0
- package/src/core/objects/type/enum/changes/enum.drop.test.ts +25 -0
- package/src/core/objects/type/enum/changes/enum.drop.ts +34 -0
- package/src/core/objects/type/enum/changes/enum.privilege.ts +175 -0
- package/src/core/objects/type/enum/changes/enum.types.ts +12 -0
- package/src/core/objects/type/enum/enum.diff.test.ts +191 -0
- package/src/core/objects/type/enum/enum.diff.ts +396 -0
- package/src/core/objects/type/enum/enum.model.ts +194 -0
- package/src/core/objects/type/range/changes/range.alter.test.ts +27 -0
- package/src/core/objects/type/range/changes/range.alter.ts +51 -0
- package/src/core/objects/type/range/changes/range.base.ts +20 -0
- package/src/core/objects/type/range/changes/range.comment.ts +64 -0
- package/src/core/objects/type/range/changes/range.create.test.ts +51 -0
- package/src/core/objects/type/range/changes/range.create.ts +151 -0
- package/src/core/objects/type/range/changes/range.drop.test.ts +26 -0
- package/src/core/objects/type/range/changes/range.drop.ts +34 -0
- package/src/core/objects/type/range/changes/range.privilege.ts +175 -0
- package/src/core/objects/type/range/changes/range.types.ts +12 -0
- package/src/core/objects/type/range/range.diff.test.ts +70 -0
- package/src/core/objects/type/range/range.diff.ts +259 -0
- package/src/core/objects/type/range/range.model.ts +187 -0
- package/src/core/objects/type/type.types.ts +5 -0
- package/src/core/objects/utils.ts +171 -0
- package/src/core/objects/view/changes/view.alter.test.ts +110 -0
- package/src/core/objects/view/changes/view.alter.ts +112 -0
- package/src/core/objects/view/changes/view.base.ts +20 -0
- package/src/core/objects/view/changes/view.comment.ts +59 -0
- package/src/core/objects/view/changes/view.create.test.ts +65 -0
- package/src/core/objects/view/changes/view.create.ts +73 -0
- package/src/core/objects/view/changes/view.drop.test.ts +34 -0
- package/src/core/objects/view/changes/view.drop.ts +40 -0
- package/src/core/objects/view/changes/view.privilege.ts +200 -0
- package/src/core/objects/view/changes/view.types.ts +12 -0
- package/src/core/objects/view/view.diff.test.ts +91 -0
- package/src/core/objects/view/view.diff.ts +365 -0
- package/src/core/objects/view/view.model.ts +276 -0
- package/src/core/plan/apply.ts +190 -0
- package/src/core/plan/create.ts +432 -0
- package/src/core/plan/hierarchy.ts +574 -0
- package/src/core/plan/index.ts +29 -0
- package/src/core/plan/io.ts +20 -0
- package/src/core/plan/risk.ts +48 -0
- package/src/core/plan/serialize.ts +195 -0
- package/src/core/plan/sql-format/constants.ts +13 -0
- package/src/core/plan/sql-format/fixtures.ts +2806 -0
- package/src/core/plan/sql-format/format-comment-literals.test.ts +96 -0
- package/src/core/plan/sql-format/format-functions.test.ts +127 -0
- package/src/core/plan/sql-format/format-lowercase-coverage.test.ts +67 -0
- package/src/core/plan/sql-format/format-off.test.ts +809 -0
- package/src/core/plan/sql-format/format-pretty-lower-leading.test.ts +1056 -0
- package/src/core/plan/sql-format/format-pretty-narrow.test.ts +1283 -0
- package/src/core/plan/sql-format/format-pretty-preserve.test.ts +1052 -0
- package/src/core/plan/sql-format/format-pretty-upper.test.ts +1045 -0
- package/src/core/plan/sql-format/format-stress.test.ts +616 -0
- package/src/core/plan/sql-format/format-utils.test.ts +91 -0
- package/src/core/plan/sql-format/format-utils.ts +391 -0
- package/src/core/plan/sql-format/formatters.ts +921 -0
- package/src/core/plan/sql-format/index.ts +149 -0
- package/src/core/plan/sql-format/keyword-case.test.ts +118 -0
- package/src/core/plan/sql-format/keyword-case.ts +1085 -0
- package/src/core/plan/sql-format/protect.test.ts +127 -0
- package/src/core/plan/sql-format/protect.ts +337 -0
- package/src/core/plan/sql-format/sql-scanner.test.ts +240 -0
- package/src/core/plan/sql-format/sql-scanner.ts +252 -0
- package/src/core/plan/sql-format/tokenizer.test.ts +68 -0
- package/src/core/plan/sql-format/tokenizer.ts +152 -0
- package/src/core/plan/sql-format/types.ts +31 -0
- package/src/core/plan/sql-format/wrap.test.ts +119 -0
- package/src/core/plan/sql-format/wrap.ts +196 -0
- package/src/core/plan/sql-format.ts +2 -0
- package/src/core/plan/statements.ts +22 -0
- package/src/core/plan/types.ts +165 -0
- package/src/core/postgres-config.ts +169 -0
- package/src/core/sort/custom-constraints.ts +161 -0
- package/src/core/sort/debug-visualization.ts +239 -0
- package/src/core/sort/dependency-filter.ts +224 -0
- package/src/core/sort/graph-builder.ts +223 -0
- package/src/core/sort/graph-utils.ts +51 -0
- package/src/core/sort/logical-sort.ts +590 -0
- package/src/core/sort/sort-changes.ts +234 -0
- package/src/core/sort/topological-sort.ts +184 -0
- package/src/core/sort/types.ts +112 -0
- package/src/core/sort/utils.ts +69 -0
- package/src/index.ts +14 -0
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified SQL scanner that handles quote/comment/dollar-tag state machines.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Callback invoked for each character that is NOT inside a quoted string,
|
|
7
|
+
* comment, or dollar-quoted block.
|
|
8
|
+
*
|
|
9
|
+
* @param index - position in the text
|
|
10
|
+
* @param char - the character at that position
|
|
11
|
+
* @param depth - current parenthesis depth (only tracked when `trackDepth` is true, else always 0)
|
|
12
|
+
* @returns `false` to stop walking early; `true` to continue
|
|
13
|
+
*/
|
|
14
|
+
type WalkCallback = (index: number, char: string, depth: number) => boolean;
|
|
15
|
+
|
|
16
|
+
type WalkSqlOptions = {
|
|
17
|
+
/** Track parenthesis depth and pass it to the callback. Default: false */
|
|
18
|
+
trackDepth?: boolean;
|
|
19
|
+
/** Start scanning from this index. Default: 0 */
|
|
20
|
+
startIndex?: number;
|
|
21
|
+
/**
|
|
22
|
+
* Called for characters/sequences inside quoted strings, comments, and
|
|
23
|
+
* dollar-quoted blocks (i.e., characters the walker "skips" over).
|
|
24
|
+
* Also called for quote/comment opener sequences (e.g., `--`, `/*`, `'`, `"`).
|
|
25
|
+
*
|
|
26
|
+
* NOT called for top-level characters — those go only to `onTopLevel`.
|
|
27
|
+
*
|
|
28
|
+
* For multi-char sequences (block comment open/close, dollar tags, escaped quotes),
|
|
29
|
+
* called once with the full sequence.
|
|
30
|
+
*/
|
|
31
|
+
onSkipped?: (chunk: string) => void;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Fast character-code check: A-Z, a-z, 0-9, _
|
|
36
|
+
*/
|
|
37
|
+
export function isWordChar(char: string): boolean {
|
|
38
|
+
const c = char.charCodeAt(0);
|
|
39
|
+
return (
|
|
40
|
+
(c >= 65 && c <= 90) ||
|
|
41
|
+
(c >= 97 && c <= 122) ||
|
|
42
|
+
(c >= 48 && c <= 57) ||
|
|
43
|
+
c === 95
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Return true when the single quote at `quoteIndex` starts a PostgreSQL
|
|
49
|
+
* escape string literal (`E'...'`). Only E-strings use backslash escaping;
|
|
50
|
+
* U&-strings use standard '' quoting (backslash is for Unicode escapes only).
|
|
51
|
+
*/
|
|
52
|
+
export function isEscapeStringQuoteStart(
|
|
53
|
+
text: string,
|
|
54
|
+
quoteIndex: number,
|
|
55
|
+
): boolean {
|
|
56
|
+
if (text[quoteIndex] !== "'") return false;
|
|
57
|
+
|
|
58
|
+
const prev = text[quoteIndex - 1];
|
|
59
|
+
const prev2 = text[quoteIndex - 2];
|
|
60
|
+
|
|
61
|
+
if (
|
|
62
|
+
(prev === "E" || prev === "e") &&
|
|
63
|
+
(prev2 === undefined || !isWordChar(prev2))
|
|
64
|
+
) {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Read a dollar-quote tag starting at `start` (which must be `$`).
|
|
73
|
+
* Returns the full tag including both `$` delimiters, e.g. `$$` or `$fn$`.
|
|
74
|
+
*/
|
|
75
|
+
export function readDollarTag(text: string, start: number): string | null {
|
|
76
|
+
if (text[start] !== "$") return null;
|
|
77
|
+
let i = start + 1;
|
|
78
|
+
while (i < text.length && isWordChar(text[i])) {
|
|
79
|
+
i += 1;
|
|
80
|
+
}
|
|
81
|
+
if (text[i] === "$") {
|
|
82
|
+
return text.slice(start, i + 1);
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Walk through SQL text, calling `onTopLevel` for each character that is
|
|
89
|
+
* outside of quotes, comments, and dollar-quoted blocks.
|
|
90
|
+
*
|
|
91
|
+
* The walker handles:
|
|
92
|
+
* - Single-quoted strings (with '' escaping)
|
|
93
|
+
* - Double-quoted identifiers (with "" escaping)
|
|
94
|
+
* - Line comments (--)
|
|
95
|
+
* - Block comments (/* ... * /)
|
|
96
|
+
* - Dollar-quoted strings ($tag$...$tag$)
|
|
97
|
+
* - Parenthesis depth tracking (optional)
|
|
98
|
+
*/
|
|
99
|
+
export function walkSql(
|
|
100
|
+
text: string,
|
|
101
|
+
onTopLevel: WalkCallback,
|
|
102
|
+
options?: WalkSqlOptions,
|
|
103
|
+
): void {
|
|
104
|
+
const trackDepth = options?.trackDepth ?? false;
|
|
105
|
+
const startIndex = options?.startIndex ?? 0;
|
|
106
|
+
const onSkipped = options?.onSkipped;
|
|
107
|
+
|
|
108
|
+
let inSingleQuote = false;
|
|
109
|
+
let singleQuoteEscapeMode = false;
|
|
110
|
+
let inDoubleQuote = false;
|
|
111
|
+
let inLineComment = false;
|
|
112
|
+
let inBlockComment = false;
|
|
113
|
+
let dollarTag: string | null = null;
|
|
114
|
+
let depth = 0;
|
|
115
|
+
|
|
116
|
+
let i = startIndex;
|
|
117
|
+
while (i < text.length) {
|
|
118
|
+
const char = text[i];
|
|
119
|
+
const next = text[i + 1];
|
|
120
|
+
|
|
121
|
+
// --- Inside line comment ---
|
|
122
|
+
if (inLineComment) {
|
|
123
|
+
onSkipped?.(char);
|
|
124
|
+
if (char === "\n") inLineComment = false;
|
|
125
|
+
i += 1;
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// --- Inside block comment ---
|
|
130
|
+
if (inBlockComment) {
|
|
131
|
+
if (char === "*" && next === "/") {
|
|
132
|
+
onSkipped?.("*/");
|
|
133
|
+
inBlockComment = false;
|
|
134
|
+
i += 2;
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
onSkipped?.(char);
|
|
138
|
+
i += 1;
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// --- Inside dollar-quoted string ---
|
|
143
|
+
if (dollarTag) {
|
|
144
|
+
if (text.startsWith(dollarTag, i)) {
|
|
145
|
+
onSkipped?.(dollarTag);
|
|
146
|
+
i += dollarTag.length;
|
|
147
|
+
dollarTag = null;
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
onSkipped?.(char);
|
|
151
|
+
i += 1;
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// --- Inside single-quoted string ---
|
|
156
|
+
if (inSingleQuote) {
|
|
157
|
+
if (singleQuoteEscapeMode && char === "\\") {
|
|
158
|
+
if (next !== undefined) {
|
|
159
|
+
onSkipped?.(`\\${next}`);
|
|
160
|
+
i += 2;
|
|
161
|
+
} else {
|
|
162
|
+
onSkipped?.(char);
|
|
163
|
+
i += 1;
|
|
164
|
+
}
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
if (char === "'") {
|
|
168
|
+
if (next === "'") {
|
|
169
|
+
onSkipped?.("''");
|
|
170
|
+
i += 2;
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
inSingleQuote = false;
|
|
174
|
+
singleQuoteEscapeMode = false;
|
|
175
|
+
}
|
|
176
|
+
onSkipped?.(char);
|
|
177
|
+
i += 1;
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// --- Inside double-quoted identifier ---
|
|
182
|
+
if (inDoubleQuote) {
|
|
183
|
+
if (char === '"') {
|
|
184
|
+
if (next === '"') {
|
|
185
|
+
onSkipped?.('""');
|
|
186
|
+
i += 2;
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
inDoubleQuote = false;
|
|
190
|
+
}
|
|
191
|
+
onSkipped?.(char);
|
|
192
|
+
i += 1;
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// --- Top-level: check for quote/comment openers ---
|
|
197
|
+
if (char === "-" && next === "-") {
|
|
198
|
+
onSkipped?.("--");
|
|
199
|
+
inLineComment = true;
|
|
200
|
+
i += 2;
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
if (char === "/" && next === "*") {
|
|
204
|
+
onSkipped?.("/*");
|
|
205
|
+
inBlockComment = true;
|
|
206
|
+
i += 2;
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (char === "'") {
|
|
210
|
+
inSingleQuote = true;
|
|
211
|
+
singleQuoteEscapeMode = isEscapeStringQuoteStart(text, i);
|
|
212
|
+
onSkipped?.(char);
|
|
213
|
+
i += 1;
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
if (char === '"') {
|
|
217
|
+
inDoubleQuote = true;
|
|
218
|
+
onSkipped?.(char);
|
|
219
|
+
i += 1;
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
if (char === "$") {
|
|
223
|
+
const tag = readDollarTag(text, i);
|
|
224
|
+
if (tag) {
|
|
225
|
+
dollarTag = tag;
|
|
226
|
+
onSkipped?.(tag);
|
|
227
|
+
i += tag.length;
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// --- Depth tracking ---
|
|
233
|
+
if (trackDepth) {
|
|
234
|
+
if (char === "(") {
|
|
235
|
+
depth += 1;
|
|
236
|
+
if (onTopLevel(i, char, depth - 1) === false) return;
|
|
237
|
+
i += 1;
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
if (char === ")") {
|
|
241
|
+
depth = Math.max(0, depth - 1);
|
|
242
|
+
if (onTopLevel(i, char, depth) === false) return;
|
|
243
|
+
i += 1;
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// --- Top-level character: invoke callback ---
|
|
249
|
+
if (onTopLevel(i, char, depth) === false) return;
|
|
250
|
+
i += 1;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
|
+
import { findTopLevelParen, scanTokens, splitByCommas } from "./tokenizer.ts";
|
|
3
|
+
|
|
4
|
+
describe("scanTokens", () => {
|
|
5
|
+
it("extracts word tokens with positions and upper-cased values", () => {
|
|
6
|
+
const tokens = scanTokens("CREATE TABLE foo");
|
|
7
|
+
expect(tokens).toEqual([
|
|
8
|
+
{ value: "CREATE", upper: "CREATE", start: 0, end: 6, depth: 0 },
|
|
9
|
+
{ value: "TABLE", upper: "TABLE", start: 7, end: 12, depth: 0 },
|
|
10
|
+
{ value: "foo", upper: "FOO", start: 13, end: 16, depth: 0 },
|
|
11
|
+
]);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("tracks depth for tokens inside parentheses", () => {
|
|
15
|
+
const tokens = scanTokens("fn(a, b)");
|
|
16
|
+
const inner = tokens.filter((t) => t.depth > 0);
|
|
17
|
+
expect(inner.length).toBe(2);
|
|
18
|
+
expect(inner[0].value).toBe("a");
|
|
19
|
+
expect(inner[0].depth).toBe(1);
|
|
20
|
+
expect(inner[1].value).toBe("b");
|
|
21
|
+
expect(inner[1].depth).toBe(1);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("ignores content in quotes, comments, and dollar-quotes", () => {
|
|
25
|
+
const tokens = scanTokens("SELECT 'hello' -- comment\n FROM $$body$$ tbl");
|
|
26
|
+
const uppers = tokens.map((t) => t.upper);
|
|
27
|
+
expect(uppers).toContain("SELECT");
|
|
28
|
+
expect(uppers).toContain("FROM");
|
|
29
|
+
expect(uppers).toContain("TBL");
|
|
30
|
+
expect(uppers).not.toContain("HELLO");
|
|
31
|
+
expect(uppers).not.toContain("COMMENT");
|
|
32
|
+
expect(uppers).not.toContain("BODY");
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe("findTopLevelParen", () => {
|
|
37
|
+
it("finds matching () at depth 0", () => {
|
|
38
|
+
const result = findTopLevelParen("CREATE TABLE foo (a int)", 0);
|
|
39
|
+
expect(result).toEqual({ open: 17, close: 23 });
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("skips nested parentheses", () => {
|
|
43
|
+
const result = findTopLevelParen("fn((a, b), c)", 0);
|
|
44
|
+
expect(result).toEqual({ open: 2, close: 12 });
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("returns null when no match found", () => {
|
|
48
|
+
const result = findTopLevelParen("no parens here", 0);
|
|
49
|
+
expect(result).toBeNull();
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe("splitByCommas", () => {
|
|
54
|
+
it("splits basic comma-separated items", () => {
|
|
55
|
+
const items = splitByCommas("a, b, c");
|
|
56
|
+
expect(items).toEqual(["a", "b", "c"]);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("preserves commas inside parentheses", () => {
|
|
60
|
+
const items = splitByCommas("a, fn(b, c), d");
|
|
61
|
+
expect(items).toEqual(["a", "fn(b, c)", "d"]);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("preserves commas inside quotes", () => {
|
|
65
|
+
const items = splitByCommas("a, 'b,c', d");
|
|
66
|
+
expect(items).toEqual(["a", "'b,c'", "d"]);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { isWordChar, walkSql } from "./sql-scanner.ts";
|
|
2
|
+
import type { Token } from "./types.ts";
|
|
3
|
+
|
|
4
|
+
export function scanTokens(statement: string): Token[] {
|
|
5
|
+
const tokens: Token[] = [];
|
|
6
|
+
let skipUntil = -1;
|
|
7
|
+
|
|
8
|
+
walkSql(
|
|
9
|
+
statement,
|
|
10
|
+
(index, char, depth) => {
|
|
11
|
+
if (index < skipUntil) return true;
|
|
12
|
+
if (char === "(" || char === ")") return true;
|
|
13
|
+
if (isWordChar(char)) {
|
|
14
|
+
let end = index + 1;
|
|
15
|
+
while (end < statement.length && isWordChar(statement[end])) {
|
|
16
|
+
end += 1;
|
|
17
|
+
}
|
|
18
|
+
const value = statement.slice(index, end);
|
|
19
|
+
tokens.push({
|
|
20
|
+
value,
|
|
21
|
+
upper: value.toUpperCase(),
|
|
22
|
+
start: index,
|
|
23
|
+
end,
|
|
24
|
+
depth,
|
|
25
|
+
});
|
|
26
|
+
skipUntil = end;
|
|
27
|
+
}
|
|
28
|
+
return true;
|
|
29
|
+
},
|
|
30
|
+
{ trackDepth: true },
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
return tokens;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function findTopLevelParen(
|
|
37
|
+
statement: string,
|
|
38
|
+
startIndex: number,
|
|
39
|
+
): { open: number; close: number } | null {
|
|
40
|
+
let result: { open: number; close: number } | null = null;
|
|
41
|
+
let openIndex: number | null = null;
|
|
42
|
+
|
|
43
|
+
walkSql(
|
|
44
|
+
statement,
|
|
45
|
+
(index, char, depth) => {
|
|
46
|
+
if (char === "(") {
|
|
47
|
+
if (depth === 0) {
|
|
48
|
+
openIndex = index;
|
|
49
|
+
}
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
if (char === ")") {
|
|
53
|
+
if (depth === 0 && openIndex !== null) {
|
|
54
|
+
result = { open: openIndex, close: index };
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return true;
|
|
59
|
+
},
|
|
60
|
+
{ trackDepth: true, startIndex },
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Collect the starting positions of top-level clause keywords in a token list.
|
|
68
|
+
* Returns a sorted array of character offsets (Token.start values).
|
|
69
|
+
*/
|
|
70
|
+
export function findClausePositions(
|
|
71
|
+
tokens: Token[],
|
|
72
|
+
keywords: Set<string>,
|
|
73
|
+
): number[] {
|
|
74
|
+
const positions: number[] = [];
|
|
75
|
+
for (let i = 0; i < tokens.length; i += 1) {
|
|
76
|
+
if (tokens[i].depth !== 0) continue;
|
|
77
|
+
if (keywords.has(tokens[i].upper)) {
|
|
78
|
+
positions.push(tokens[i].start);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
positions.sort((a, b) => a - b);
|
|
82
|
+
return positions;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Advance a cursor past a possibly schema-qualified name (e.g. `public.my_table`).
|
|
87
|
+
* Returns the new cursor position (pointing to the first token after the name).
|
|
88
|
+
*/
|
|
89
|
+
export function skipQualifiedName(
|
|
90
|
+
statement: string,
|
|
91
|
+
tokens: Token[],
|
|
92
|
+
cursor: number,
|
|
93
|
+
): number {
|
|
94
|
+
let c = cursor + 1;
|
|
95
|
+
while (
|
|
96
|
+
c < tokens.length &&
|
|
97
|
+
tokens[c].start === tokens[c - 1].end + 1 &&
|
|
98
|
+
statement[tokens[c - 1].end] === "."
|
|
99
|
+
) {
|
|
100
|
+
c += 1;
|
|
101
|
+
}
|
|
102
|
+
return c;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Slice a text into clause strings given sorted clause-start positions.
|
|
107
|
+
* Returns trimmed, non-empty clause strings.
|
|
108
|
+
*/
|
|
109
|
+
export function sliceClauses(text: string, positions: number[]): string[] {
|
|
110
|
+
const clauses: string[] = [];
|
|
111
|
+
for (let i = 0; i < positions.length; i += 1) {
|
|
112
|
+
const start = positions[i];
|
|
113
|
+
const end = positions[i + 1] ?? text.length;
|
|
114
|
+
const clause = text.slice(start, end).trim();
|
|
115
|
+
if (clause.length > 0) clauses.push(clause);
|
|
116
|
+
}
|
|
117
|
+
return clauses;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function splitByCommas(content: string): string[] {
|
|
121
|
+
const items: string[] = [];
|
|
122
|
+
let buffer = "";
|
|
123
|
+
|
|
124
|
+
walkSql(
|
|
125
|
+
content,
|
|
126
|
+
(_index, char, depth) => {
|
|
127
|
+
if (char === "(" || char === ")") {
|
|
128
|
+
buffer += char;
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
if (char === "," && depth === 0) {
|
|
132
|
+
items.push(buffer);
|
|
133
|
+
buffer = "";
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
buffer += char;
|
|
137
|
+
return true;
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
trackDepth: true,
|
|
141
|
+
onSkipped: (chunk) => {
|
|
142
|
+
buffer += chunk;
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
if (buffer.length > 0) {
|
|
148
|
+
items.push(buffer);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return items.map((item) => item.trim()).filter((item) => item.length > 0);
|
|
152
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
type KeywordCase = "upper" | "lower" | "preserve";
|
|
2
|
+
export type CommaStyle = "trailing" | "leading";
|
|
3
|
+
|
|
4
|
+
export type SqlFormatOptions = {
|
|
5
|
+
keywordCase?: KeywordCase;
|
|
6
|
+
indent?: number;
|
|
7
|
+
maxWidth?: number;
|
|
8
|
+
commaStyle?: CommaStyle;
|
|
9
|
+
alignColumns?: boolean;
|
|
10
|
+
alignKeyValues?: boolean;
|
|
11
|
+
preserveRoutineBodies?: boolean;
|
|
12
|
+
preserveViewBodies?: boolean;
|
|
13
|
+
preserveRuleBodies?: boolean;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type NormalizedOptions = Required<SqlFormatOptions>;
|
|
17
|
+
|
|
18
|
+
export type Token = {
|
|
19
|
+
value: string;
|
|
20
|
+
upper: string;
|
|
21
|
+
start: number;
|
|
22
|
+
end: number;
|
|
23
|
+
depth: number;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export type ProtectedSegments = {
|
|
27
|
+
text: string;
|
|
28
|
+
placeholders: Map<string, string>;
|
|
29
|
+
noWrapPlaceholders: Set<string>;
|
|
30
|
+
skipCasing: boolean;
|
|
31
|
+
};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
|
+
import { DEFAULT_OPTIONS } from "./constants.ts";
|
|
3
|
+
import type { NormalizedOptions } from "./types.ts";
|
|
4
|
+
import { wrapStatement } from "./wrap.ts";
|
|
5
|
+
|
|
6
|
+
const opts: NormalizedOptions = { ...DEFAULT_OPTIONS, maxWidth: 40 };
|
|
7
|
+
const noWrap = new Set<string>();
|
|
8
|
+
|
|
9
|
+
describe("wrapStatement", () => {
|
|
10
|
+
it("keeps short lines unwrapped", () => {
|
|
11
|
+
const result = wrapStatement("SELECT 1", opts, noWrap);
|
|
12
|
+
expect(result).toBe("SELECT 1");
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("wraps long lines at whitespace before maxWidth", () => {
|
|
16
|
+
const long = "SELECT column_a, column_b, column_c, column_d FROM my_table";
|
|
17
|
+
const result = wrapStatement(long, opts, noWrap);
|
|
18
|
+
const lines = result.split("\n");
|
|
19
|
+
expect(lines.length).toBeGreaterThan(1);
|
|
20
|
+
expect(lines[0].length).toBeLessThanOrEqual(40);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("does not wrap comment lines regardless of length", () => {
|
|
24
|
+
const comment =
|
|
25
|
+
"-- this is a very long comment that exceeds the maximum line width significantly";
|
|
26
|
+
const result = wrapStatement(comment, opts, noWrap);
|
|
27
|
+
expect(result).toBe(comment);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("does not wrap lines containing noWrap placeholders", () => {
|
|
31
|
+
const placeholder = "__PGDELTA_PLACEHOLDER_0__";
|
|
32
|
+
const noWrapSet = new Set([placeholder]);
|
|
33
|
+
const long = `CREATE FUNCTION foo() ${placeholder}`;
|
|
34
|
+
const result = wrapStatement(long, { ...opts, maxWidth: 20 }, noWrapSet);
|
|
35
|
+
expect(result.split("\n").length).toBe(1);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("adds proper continuation indentation", () => {
|
|
39
|
+
const long = "SELECT column_a, column_b, column_c, column_d FROM my_table";
|
|
40
|
+
const result = wrapStatement(long, opts, noWrap);
|
|
41
|
+
const lines = result.split("\n");
|
|
42
|
+
if (lines.length > 1) {
|
|
43
|
+
expect(lines[1]).toMatch(/^\s+/);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("prefers breaking before SQL keywords over arbitrary whitespace", () => {
|
|
48
|
+
// With maxWidth=60, the line must wrap. The keyword-aware logic should prefer
|
|
49
|
+
// to break before MATCH, FOREIGN, CHECK, ON, etc. rather than at any space.
|
|
50
|
+
const line =
|
|
51
|
+
"ADD CONSTRAINT fk_ref FOREIGN KEY (ref_id) REFERENCES tbl(id) MATCH FULL ON DELETE CASCADE";
|
|
52
|
+
const result = wrapStatement(line, { ...opts, maxWidth: 70 }, noWrap);
|
|
53
|
+
const lines = result.split("\n");
|
|
54
|
+
expect(lines.length).toBeGreaterThan(1);
|
|
55
|
+
// The second line should start with a keyword like MATCH or ON
|
|
56
|
+
const secondLineTrimmed = lines[1].trim();
|
|
57
|
+
expect(secondLineTrimmed).toMatch(
|
|
58
|
+
/^(MATCH|ON|FOREIGN|CHECK|REFERENCES|DEFERRABLE|INITIALLY)/,
|
|
59
|
+
);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("falls back to whitespace when no keyword boundary found", () => {
|
|
63
|
+
const line =
|
|
64
|
+
"this_is_a very_long_line that_has no_sql_keywords at_all_inside";
|
|
65
|
+
const result = wrapStatement(line, { ...opts, maxWidth: 30 }, noWrap);
|
|
66
|
+
const lines = result.split("\n");
|
|
67
|
+
expect(lines.length).toBeGreaterThan(1);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("does not break between CREATE and PUBLICATION", () => {
|
|
71
|
+
const line =
|
|
72
|
+
"CREATE PUBLICATION pub_custom FOR TABLE public.articles_with_a_very_long_name (id, title) WHERE (published = true)";
|
|
73
|
+
const result = wrapStatement(line, { ...opts, maxWidth: 70 }, noWrap);
|
|
74
|
+
const lines = result.split("\n");
|
|
75
|
+
expect(lines[0]).toContain("CREATE PUBLICATION");
|
|
76
|
+
expect(lines[0]).not.toBe("CREATE");
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("does not break between COMMENT and ON", () => {
|
|
80
|
+
const line =
|
|
81
|
+
"COMMENT ON FUNCTION public.calculate_metrics(text,text,integer) IS 'Calculate metrics for a given table'";
|
|
82
|
+
const result = wrapStatement(line, { ...opts, maxWidth: 60 }, noWrap);
|
|
83
|
+
const lines = result.split("\n");
|
|
84
|
+
expect(lines[0]).toContain("COMMENT ON");
|
|
85
|
+
expect(lines[0]).not.toBe("COMMENT");
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("does not break between GRANT/REVOKE ALL and ON", () => {
|
|
89
|
+
const line =
|
|
90
|
+
"GRANT ALL ON FUNCTION public.calculate_metrics_for_analytics_dashboard_with_extended_name(text, text, integer) TO app_user";
|
|
91
|
+
const result = wrapStatement(line, { ...opts, maxWidth: 60 }, noWrap);
|
|
92
|
+
const lines = result.split("\n");
|
|
93
|
+
expect(lines[0]).toContain("GRANT ALL ON");
|
|
94
|
+
expect(lines[0]).not.toBe("GRANT ALL");
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("never produces blank lines from breaking within leading indent", () => {
|
|
98
|
+
// Simulate a continuation line starting with " ON ..." — should not break before ON within the indent
|
|
99
|
+
const line =
|
|
100
|
+
" ON FUNCTION public.calculate_metrics_for_analytics_dashboard_with_extended_name(text,text,integer) IS 'Calculate metrics'";
|
|
101
|
+
const result = wrapStatement(line, { ...opts, maxWidth: 60 }, noWrap);
|
|
102
|
+
const lines = result.split("\n");
|
|
103
|
+
// No line should be empty (the blank-line bug)
|
|
104
|
+
for (const l of lines) {
|
|
105
|
+
expect(l.trim().length).toBeGreaterThan(0);
|
|
106
|
+
}
|
|
107
|
+
// First line should keep "ON FUNCTION" together
|
|
108
|
+
expect(lines[0].trim()).toMatch(/^ON FUNCTION/);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("breaks after commas when within maxWidth (one clause per line)", () => {
|
|
112
|
+
// No parentheses: "a, b" with narrow width breaks after the comma
|
|
113
|
+
const result = wrapStatement("a, b", { ...opts, maxWidth: 3 }, noWrap);
|
|
114
|
+
const lines = result.split("\n");
|
|
115
|
+
expect(lines.length).toBe(2);
|
|
116
|
+
expect(lines[0].trimEnd()).toBe("a,");
|
|
117
|
+
expect(lines[1].trim()).toBe("b");
|
|
118
|
+
});
|
|
119
|
+
});
|