@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.
Files changed (359) hide show
  1. package/README.md +40 -23
  2. package/dist/cli/app.js +26 -3
  3. package/dist/cli/bin/cli.js +5 -0
  4. package/dist/cli/commands/catalog-export.d.ts +5 -0
  5. package/dist/cli/commands/catalog-export.js +64 -0
  6. package/dist/cli/commands/declarative-apply.d.ts +6 -0
  7. package/dist/cli/commands/declarative-apply.js +288 -0
  8. package/dist/cli/commands/declarative-export.d.ts +5 -0
  9. package/dist/cli/commands/declarative-export.js +245 -0
  10. package/dist/cli/commands/plan.js +19 -6
  11. package/dist/cli/exit-code.d.ts +2 -0
  12. package/dist/cli/exit-code.js +7 -0
  13. package/dist/cli/formatters/tree/tree.js +3 -2
  14. package/dist/cli/utils/apply-display.d.ts +52 -0
  15. package/dist/cli/utils/apply-display.js +183 -0
  16. package/dist/cli/utils/export-display.d.ts +43 -0
  17. package/dist/cli/utils/export-display.js +202 -0
  18. package/dist/cli/utils/resolve-input.d.ts +7 -0
  19. package/dist/cli/utils/resolve-input.js +13 -0
  20. package/dist/core/catalog-export/index.d.ts +11 -0
  21. package/dist/core/catalog-export/index.js +10 -0
  22. package/dist/core/catalog.diff.d.ts +1 -0
  23. package/dist/core/catalog.diff.js +64 -48
  24. package/dist/core/catalog.model.d.ts +14 -1
  25. package/dist/core/catalog.model.js +103 -1
  26. package/dist/core/catalog.snapshot.d.ts +66 -0
  27. package/dist/core/catalog.snapshot.js +206 -0
  28. package/dist/core/declarative-apply/discover-sql.d.ts +18 -0
  29. package/dist/core/declarative-apply/discover-sql.js +86 -0
  30. package/dist/core/declarative-apply/extract-catalog-providers.d.ts +23 -0
  31. package/dist/core/declarative-apply/extract-catalog-providers.js +159 -0
  32. package/dist/core/declarative-apply/index.d.ts +49 -0
  33. package/dist/core/declarative-apply/index.js +134 -0
  34. package/dist/core/declarative-apply/round-apply.d.ts +100 -0
  35. package/dist/core/declarative-apply/round-apply.js +378 -0
  36. package/dist/core/export/file-mapper.d.ts +71 -0
  37. package/dist/core/export/file-mapper.js +474 -0
  38. package/dist/core/export/grouper.d.ts +13 -0
  39. package/dist/core/export/grouper.js +76 -0
  40. package/dist/core/export/index.d.ts +45 -0
  41. package/dist/core/export/index.js +63 -0
  42. package/dist/core/export/types.d.ts +84 -0
  43. package/dist/core/export/types.js +25 -0
  44. package/dist/core/fixtures/empty-catalogs/postgres-15-16-baseline.json +287 -0
  45. package/dist/core/integrations/filter/dsl.d.ts +38 -1
  46. package/dist/core/integrations/filter/dsl.js +20 -2
  47. package/dist/core/integrations/filter/extractors.js +42 -0
  48. package/dist/core/integrations/integration-dsl.d.ts +10 -0
  49. package/dist/core/integrations/supabase.d.ts +8 -0
  50. package/dist/core/integrations/supabase.js +9 -0
  51. package/dist/core/objects/aggregate/aggregate.diff.d.ts +2 -8
  52. package/dist/core/objects/aggregate/aggregate.diff.js +16 -70
  53. package/dist/core/objects/aggregate/aggregate.model.d.ts +8 -8
  54. package/dist/core/objects/aggregate/aggregate.model.js +1 -1
  55. package/dist/core/objects/aggregate/changes/aggregate.create.js +1 -1
  56. package/dist/core/objects/aggregate/changes/aggregate.drop.js +1 -1
  57. package/dist/core/objects/base.privilege-diff.d.ts +38 -13
  58. package/dist/core/objects/base.privilege-diff.js +104 -22
  59. package/dist/core/objects/base.privilege.d.ts +1 -0
  60. package/dist/core/objects/base.privilege.js +9 -2
  61. package/dist/core/objects/collation/collation.diff.d.ts +2 -3
  62. package/dist/core/objects/diff-context.d.ts +15 -0
  63. package/dist/core/objects/diff-context.js +1 -0
  64. package/dist/core/objects/domain/changes/domain.create.js +4 -2
  65. package/dist/core/objects/domain/domain.diff.d.ts +2 -8
  66. package/dist/core/objects/domain/domain.diff.js +16 -77
  67. package/dist/core/objects/domain/domain.model.js +1 -1
  68. package/dist/core/objects/event-trigger/event-trigger.diff.d.ts +2 -3
  69. package/dist/core/objects/foreign-data-wrapper/foreign-data-wrapper/foreign-data-wrapper.diff.d.ts +2 -8
  70. package/dist/core/objects/foreign-data-wrapper/foreign-data-wrapper/foreign-data-wrapper.diff.js +13 -77
  71. package/dist/core/objects/foreign-data-wrapper/foreign-data-wrapper/foreign-data-wrapper.model.js +2 -2
  72. package/dist/core/objects/foreign-data-wrapper/foreign-table/foreign-table.diff.d.ts +2 -8
  73. package/dist/core/objects/foreign-data-wrapper/foreign-table/foreign-table.diff.js +16 -77
  74. package/dist/core/objects/foreign-data-wrapper/foreign-table/foreign-table.model.js +1 -1
  75. package/dist/core/objects/foreign-data-wrapper/server/server.diff.d.ts +2 -8
  76. package/dist/core/objects/foreign-data-wrapper/server/server.diff.js +13 -77
  77. package/dist/core/objects/language/language.diff.d.ts +2 -5
  78. package/dist/core/objects/language/language.diff.js +7 -39
  79. package/dist/core/objects/materialized-view/materialized-view.diff.d.ts +2 -8
  80. package/dist/core/objects/materialized-view/materialized-view.diff.js +16 -158
  81. package/dist/core/objects/materialized-view/materialized-view.model.d.ts +3 -3
  82. package/dist/core/objects/materialized-view/materialized-view.model.js +1 -1
  83. package/dist/core/objects/procedure/changes/procedure.alter.js +12 -12
  84. package/dist/core/objects/procedure/procedure.diff.d.ts +2 -8
  85. package/dist/core/objects/procedure/procedure.diff.js +16 -77
  86. package/dist/core/objects/procedure/procedure.model.d.ts +9 -9
  87. package/dist/core/objects/procedure/procedure.model.js +1 -1
  88. package/dist/core/objects/publication/changes/publication.alter.d.ts +0 -9
  89. package/dist/core/objects/publication/changes/publication.alter.js +0 -14
  90. package/dist/core/objects/publication/changes/publication.types.d.ts +2 -2
  91. package/dist/core/objects/publication/publication.diff.d.ts +2 -3
  92. package/dist/core/objects/publication/publication.diff.js +8 -13
  93. package/dist/core/objects/rls-policy/changes/rls-policy.alter.js +3 -3
  94. package/dist/core/objects/rls-policy/rls-policy.model.d.ts +2 -2
  95. package/dist/core/objects/role/role.diff.js +22 -1
  96. package/dist/core/objects/role/role.model.d.ts +4 -3
  97. package/dist/core/objects/role/role.model.js +118 -12
  98. package/dist/core/objects/rule/rule.model.d.ts +1 -1
  99. package/dist/core/objects/schema/schema.diff.d.ts +2 -8
  100. package/dist/core/objects/schema/schema.diff.js +16 -77
  101. package/dist/core/objects/schema/schema.model.js +1 -1
  102. package/dist/core/objects/sequence/sequence.diff.d.ts +2 -8
  103. package/dist/core/objects/sequence/sequence.diff.js +16 -79
  104. package/dist/core/objects/sequence/sequence.model.js +1 -1
  105. package/dist/core/objects/subscription/subscription.diff.d.ts +2 -3
  106. package/dist/core/objects/table/changes/table.create.js +3 -0
  107. package/dist/core/objects/table/table.diff.d.ts +2 -8
  108. package/dist/core/objects/table/table.diff.js +26 -157
  109. package/dist/core/objects/table/table.model.d.ts +23 -22
  110. package/dist/core/objects/table/table.model.js +1 -1
  111. package/dist/core/objects/trigger/changes/trigger.create.js +2 -4
  112. package/dist/core/objects/trigger/trigger.model.d.ts +8 -0
  113. package/dist/core/objects/trigger/trigger.model.js +11 -0
  114. package/dist/core/objects/type/composite-type/composite-type.diff.d.ts +2 -8
  115. package/dist/core/objects/type/composite-type/composite-type.diff.js +16 -77
  116. package/dist/core/objects/type/composite-type/composite-type.model.d.ts +3 -3
  117. package/dist/core/objects/type/composite-type/composite-type.model.js +2 -1
  118. package/dist/core/objects/type/enum/enum.diff.d.ts +2 -8
  119. package/dist/core/objects/type/enum/enum.diff.js +25 -112
  120. package/dist/core/objects/type/enum/enum.model.js +1 -1
  121. package/dist/core/objects/type/range/changes/range.create.js +6 -3
  122. package/dist/core/objects/type/range/range.diff.d.ts +2 -8
  123. package/dist/core/objects/type/range/range.diff.js +16 -77
  124. package/dist/core/objects/type/range/range.model.js +1 -1
  125. package/dist/core/objects/view/view.diff.d.ts +2 -8
  126. package/dist/core/objects/view/view.diff.js +16 -158
  127. package/dist/core/objects/view/view.model.d.ts +18 -4
  128. package/dist/core/objects/view/view.model.js +3 -13
  129. package/dist/core/plan/apply.js +9 -26
  130. package/dist/core/plan/create.d.ts +19 -6
  131. package/dist/core/plan/create.js +134 -174
  132. package/dist/core/plan/serialize.js +16 -4
  133. package/dist/core/plan/sql-format/fixtures.js +3 -5
  134. package/dist/core/plan/sql-format/keyword-case.js +26 -1
  135. package/dist/core/plan/ssl-config.d.ts +32 -0
  136. package/dist/core/plan/ssl-config.js +115 -0
  137. package/dist/core/plan/types.d.ts +6 -0
  138. package/dist/core/postgres-config.d.ts +14 -0
  139. package/dist/core/postgres-config.js +53 -2
  140. package/dist/core/sort/graph-builder.js +10 -0
  141. package/dist/core/sort/logical-sort.js +31 -23
  142. package/dist/core/test-utils/assert-valid-sql.d.ts +10 -0
  143. package/dist/core/test-utils/assert-valid-sql.js +19 -0
  144. package/dist/index.d.ts +6 -0
  145. package/dist/index.js +6 -1
  146. package/package.json +21 -4
  147. package/src/cli/app.ts +27 -3
  148. package/src/cli/bin/cli.ts +6 -0
  149. package/src/cli/commands/catalog-export.ts +78 -0
  150. package/src/cli/commands/declarative-apply.diagnostics.test.ts +77 -0
  151. package/src/cli/commands/declarative-apply.ts +380 -0
  152. package/src/cli/commands/declarative-export.ts +330 -0
  153. package/src/cli/commands/plan.ts +28 -7
  154. package/src/cli/exit-code.test.ts +19 -0
  155. package/src/cli/exit-code.ts +7 -0
  156. package/src/cli/formatters/tree/tree.ts +3 -2
  157. package/src/cli/utils/apply-display.test.ts +348 -0
  158. package/src/cli/utils/apply-display.ts +238 -0
  159. package/src/cli/utils/export-display.test.ts +103 -0
  160. package/src/cli/utils/export-display.ts +275 -0
  161. package/src/cli/utils/integrations.test.ts +44 -0
  162. package/src/cli/utils/resolve-input.test.ts +38 -0
  163. package/src/cli/utils/resolve-input.ts +17 -0
  164. package/src/core/catalog-export/index.ts +20 -0
  165. package/src/core/catalog.diff.ts +79 -78
  166. package/src/core/catalog.model.test.ts +122 -0
  167. package/src/core/catalog.model.ts +127 -1
  168. package/src/core/catalog.snapshot.test.ts +464 -0
  169. package/src/core/catalog.snapshot.ts +289 -0
  170. package/src/core/declarative-apply/discover-sql.test.ts +103 -0
  171. package/src/core/declarative-apply/discover-sql.ts +107 -0
  172. package/src/core/declarative-apply/extract-catalog-providers.ts +220 -0
  173. package/src/core/declarative-apply/index.test.ts +67 -0
  174. package/src/core/declarative-apply/index.ts +205 -0
  175. package/src/core/declarative-apply/round-apply.test.ts +504 -0
  176. package/src/core/declarative-apply/round-apply.ts +562 -0
  177. package/src/core/expand-replace-dependencies.test.ts +70 -0
  178. package/src/core/export/file-mapper.test.ts +816 -0
  179. package/src/core/export/file-mapper.ts +574 -0
  180. package/src/core/export/grouper.ts +108 -0
  181. package/src/core/export/index.ts +129 -0
  182. package/src/core/export/types.ts +104 -0
  183. package/src/core/fixtures/empty-catalogs/postgres-15-16-baseline.json +287 -0
  184. package/src/core/integrations/filter/dsl.test.ts +211 -0
  185. package/src/core/integrations/filter/dsl.ts +65 -3
  186. package/src/core/integrations/filter/extractors.test.ts +244 -0
  187. package/src/core/integrations/filter/extractors.ts +42 -0
  188. package/src/core/integrations/integration-dsl.ts +10 -0
  189. package/src/core/integrations/serialize/dsl.test.ts +91 -0
  190. package/src/core/integrations/supabase.ts +9 -0
  191. package/src/core/objects/aggregate/aggregate.diff.ts +39 -95
  192. package/src/core/objects/aggregate/aggregate.model.ts +1 -1
  193. package/src/core/objects/aggregate/changes/aggregate.alter.test.ts +3 -1
  194. package/src/core/objects/aggregate/changes/aggregate.comment.test.ts +5 -2
  195. package/src/core/objects/aggregate/changes/aggregate.create.test.ts +6 -3
  196. package/src/core/objects/aggregate/changes/aggregate.create.ts +1 -1
  197. package/src/core/objects/aggregate/changes/aggregate.drop.test.ts +7 -3
  198. package/src/core/objects/aggregate/changes/aggregate.drop.ts +1 -1
  199. package/src/core/objects/aggregate/changes/aggregate.privilege.test.ts +9 -3
  200. package/src/core/objects/base.privilege-diff.ts +178 -30
  201. package/src/core/objects/base.privilege.ts +9 -2
  202. package/src/core/objects/collation/changes/collation.alter.test.ts +7 -2
  203. package/src/core/objects/collation/changes/collation.create.test.ts +7 -2
  204. package/src/core/objects/collation/changes/collation.drop.test.ts +4 -1
  205. package/src/core/objects/collation/collation.diff.test.ts +9 -12
  206. package/src/core/objects/collation/collation.diff.ts +2 -1
  207. package/src/core/objects/diff-context.ts +16 -0
  208. package/src/core/objects/domain/changes/domain.alter.test.ts +28 -9
  209. package/src/core/objects/domain/changes/domain.create.test.ts +32 -2
  210. package/src/core/objects/domain/changes/domain.create.ts +7 -1
  211. package/src/core/objects/domain/changes/domain.drop.test.ts +4 -1
  212. package/src/core/objects/domain/domain.diff.ts +39 -102
  213. package/src/core/objects/domain/domain.model.ts +1 -1
  214. package/src/core/objects/event-trigger/changes/event-trigger.alter.test.ts +10 -3
  215. package/src/core/objects/event-trigger/changes/event-trigger.create.test.ts +4 -1
  216. package/src/core/objects/event-trigger/changes/event-trigger.drop.test.ts +4 -1
  217. package/src/core/objects/event-trigger/event-trigger.diff.test.ts +12 -7
  218. package/src/core/objects/event-trigger/event-trigger.diff.ts +2 -1
  219. package/src/core/objects/extension/changes/extension.alter.test.ts +7 -2
  220. package/src/core/objects/extension/changes/extension.create.test.ts +4 -1
  221. package/src/core/objects/extension/changes/extension.drop.test.ts +4 -1
  222. package/src/core/objects/extension/extension.model.test.ts +98 -0
  223. package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.alter.test.ts +16 -5
  224. package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.create.test.ts +51 -16
  225. package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.drop.test.ts +4 -1
  226. package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/foreign-data-wrapper.diff.test.ts +111 -4
  227. package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/foreign-data-wrapper.diff.ts +31 -101
  228. package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/foreign-data-wrapper.model.ts +2 -2
  229. package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.alter.test.ts +46 -15
  230. package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.create.test.ts +13 -4
  231. package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.drop.test.ts +4 -1
  232. package/src/core/objects/foreign-data-wrapper/foreign-table/foreign-table.diff.ts +39 -102
  233. package/src/core/objects/foreign-data-wrapper/foreign-table/foreign-table.model.ts +1 -1
  234. package/src/core/objects/foreign-data-wrapper/server/changes/server.alter.test.ts +22 -7
  235. package/src/core/objects/foreign-data-wrapper/server/changes/server.create.test.ts +19 -6
  236. package/src/core/objects/foreign-data-wrapper/server/changes/server.drop.test.ts +4 -1
  237. package/src/core/objects/foreign-data-wrapper/server/server.diff.test.ts +95 -0
  238. package/src/core/objects/foreign-data-wrapper/server/server.diff.ts +31 -101
  239. package/src/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.alter.test.ts +13 -4
  240. package/src/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.create.test.ts +16 -5
  241. package/src/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.drop.test.ts +10 -3
  242. package/src/core/objects/index/changes/index.alter.test.ts +13 -4
  243. package/src/core/objects/index/changes/index.create.test.ts +4 -1
  244. package/src/core/objects/index/changes/index.drop.test.ts +4 -1
  245. package/src/core/objects/language/changes/language.alter.test.ts +4 -1
  246. package/src/core/objects/language/changes/language.create.test.ts +4 -1
  247. package/src/core/objects/language/changes/language.drop.test.ts +4 -1
  248. package/src/core/objects/language/language.diff.test.ts +86 -4
  249. package/src/core/objects/language/language.diff.ts +17 -49
  250. package/src/core/objects/materialized-view/changes/materialized-view.alter.test.ts +10 -3
  251. package/src/core/objects/materialized-view/changes/materialized-view.create.test.ts +7 -2
  252. package/src/core/objects/materialized-view/changes/materialized-view.drop.test.ts +4 -1
  253. package/src/core/objects/materialized-view/materialized-view.diff.test.ts +162 -0
  254. package/src/core/objects/materialized-view/materialized-view.diff.ts +41 -191
  255. package/src/core/objects/materialized-view/materialized-view.model.ts +1 -1
  256. package/src/core/objects/procedure/changes/procedure.alter.test.ts +121 -49
  257. package/src/core/objects/procedure/changes/procedure.alter.ts +15 -12
  258. package/src/core/objects/procedure/changes/procedure.create.test.ts +4 -1
  259. package/src/core/objects/procedure/changes/procedure.drop.test.ts +7 -2
  260. package/src/core/objects/procedure/procedure.diff.ts +39 -102
  261. package/src/core/objects/procedure/procedure.model.ts +1 -1
  262. package/src/core/objects/publication/changes/publication.alter.test.ts +15 -21
  263. package/src/core/objects/publication/changes/publication.alter.ts +0 -18
  264. package/src/core/objects/publication/changes/publication.comment.test.ts +5 -2
  265. package/src/core/objects/publication/changes/publication.create.test.ts +5 -2
  266. package/src/core/objects/publication/changes/publication.drop.test.ts +3 -1
  267. package/src/core/objects/publication/changes/publication.types.ts +0 -2
  268. package/src/core/objects/publication/publication.diff.test.ts +24 -19
  269. package/src/core/objects/publication/publication.diff.ts +9 -15
  270. package/src/core/objects/rls-policy/changes/rls-policy.alter.test.ts +31 -14
  271. package/src/core/objects/rls-policy/changes/rls-policy.alter.ts +3 -3
  272. package/src/core/objects/rls-policy/changes/rls-policy.create.test.ts +10 -3
  273. package/src/core/objects/rls-policy/changes/rls-policy.drop.test.ts +4 -1
  274. package/src/core/objects/role/changes/role.alter.test.ts +31 -15
  275. package/src/core/objects/role/changes/role.create.test.ts +6 -2
  276. package/src/core/objects/role/changes/role.drop.test.ts +4 -1
  277. package/src/core/objects/role/role.diff.test.ts +235 -0
  278. package/src/core/objects/role/role.diff.ts +21 -1
  279. package/src/core/objects/role/role.model.ts +122 -14
  280. package/src/core/objects/rule/changes/rule.alter.test.ts +7 -3
  281. package/src/core/objects/rule/changes/rule.comment.test.ts +5 -2
  282. package/src/core/objects/rule/changes/rule.create.test.ts +6 -2
  283. package/src/core/objects/rule/changes/rule.drop.test.ts +3 -1
  284. package/src/core/objects/schema/changes/schema.alter.test.ts +4 -1
  285. package/src/core/objects/schema/changes/schema.create.test.ts +4 -1
  286. package/src/core/objects/schema/changes/schema.drop.test.ts +4 -1
  287. package/src/core/objects/schema/schema.diff.ts +39 -102
  288. package/src/core/objects/schema/schema.model.ts +1 -1
  289. package/src/core/objects/sequence/changes/sequence.alter.test.ts +11 -5
  290. package/src/core/objects/sequence/changes/sequence.create.test.ts +8 -3
  291. package/src/core/objects/sequence/changes/sequence.drop.test.ts +4 -1
  292. package/src/core/objects/sequence/sequence.diff.test.ts +114 -0
  293. package/src/core/objects/sequence/sequence.diff.ts +39 -104
  294. package/src/core/objects/sequence/sequence.model.ts +1 -1
  295. package/src/core/objects/subscription/changes/subscription.alter.test.ts +15 -5
  296. package/src/core/objects/subscription/changes/subscription.comment.test.ts +5 -2
  297. package/src/core/objects/subscription/changes/subscription.create.test.ts +5 -2
  298. package/src/core/objects/subscription/changes/subscription.drop.test.ts +3 -1
  299. package/src/core/objects/subscription/subscription.diff.test.ts +16 -11
  300. package/src/core/objects/subscription/subscription.diff.ts +2 -1
  301. package/src/core/objects/table/changes/table.alter.test.ts +38 -15
  302. package/src/core/objects/table/changes/table.create.test.ts +41 -3
  303. package/src/core/objects/table/changes/table.create.ts +4 -0
  304. package/src/core/objects/table/changes/table.drop.test.ts +3 -1
  305. package/src/core/objects/table/table.diff.test.ts +157 -0
  306. package/src/core/objects/table/table.diff.ts +54 -190
  307. package/src/core/objects/table/table.model.ts +1 -1
  308. package/src/core/objects/trigger/changes/trigger.alter.test.ts +8 -4
  309. package/src/core/objects/trigger/changes/trigger.create.test.ts +5 -1
  310. package/src/core/objects/trigger/changes/trigger.create.ts +7 -4
  311. package/src/core/objects/trigger/changes/trigger.drop.test.ts +5 -1
  312. package/src/core/objects/trigger/trigger.diff.test.ts +1 -0
  313. package/src/core/objects/trigger/trigger.model.ts +12 -0
  314. package/src/core/objects/type/composite-type/changes/composite-type.alter.test.ts +10 -4
  315. package/src/core/objects/type/composite-type/changes/composite-type.create.test.ts +7 -2
  316. package/src/core/objects/type/composite-type/changes/composite-type.drop.test.ts +4 -1
  317. package/src/core/objects/type/composite-type/composite-type.diff.test.ts +78 -0
  318. package/src/core/objects/type/composite-type/composite-type.diff.ts +39 -101
  319. package/src/core/objects/type/composite-type/composite-type.model.ts +2 -1
  320. package/src/core/objects/type/enum/changes/enum.alter.test.ts +14 -5
  321. package/src/core/objects/type/enum/changes/enum.create.test.ts +4 -1
  322. package/src/core/objects/type/enum/changes/enum.drop.test.ts +4 -1
  323. package/src/core/objects/type/enum/enum.diff.test.ts +181 -0
  324. package/src/core/objects/type/enum/enum.diff.ts +58 -146
  325. package/src/core/objects/type/enum/enum.model.ts +1 -1
  326. package/src/core/objects/type/range/changes/range.alter.test.ts +3 -1
  327. package/src/core/objects/type/range/changes/range.create.test.ts +5 -2
  328. package/src/core/objects/type/range/changes/range.create.ts +6 -2
  329. package/src/core/objects/type/range/changes/range.drop.test.ts +3 -1
  330. package/src/core/objects/type/range/range.diff.test.ts +77 -0
  331. package/src/core/objects/type/range/range.diff.ts +39 -101
  332. package/src/core/objects/type/range/range.model.ts +1 -1
  333. package/src/core/objects/view/changes/view.alter.test.ts +8 -3
  334. package/src/core/objects/view/changes/view.create.test.ts +7 -2
  335. package/src/core/objects/view/changes/view.drop.test.ts +4 -1
  336. package/src/core/objects/view/view.diff.test.ts +82 -0
  337. package/src/core/objects/view/view.diff.ts +41 -191
  338. package/src/core/objects/view/view.model.ts +3 -17
  339. package/src/core/plan/apply.ts +9 -27
  340. package/src/core/plan/create.ts +173 -237
  341. package/src/core/plan/serialize.test.ts +317 -0
  342. package/src/core/plan/serialize.ts +18 -4
  343. package/src/core/plan/sql-format/fixtures.ts +2 -5
  344. package/src/core/plan/sql-format/format-lowercase-coverage.test.ts +52 -0
  345. package/src/core/plan/sql-format/format-off.test.ts +14 -17
  346. package/src/core/plan/sql-format/format-pretty-lower-leading.test.ts +27 -22
  347. package/src/core/plan/sql-format/format-pretty-narrow.test.ts +17 -21
  348. package/src/core/plan/sql-format/format-pretty-preserve.test.ts +25 -20
  349. package/src/core/plan/sql-format/format-pretty-upper.test.ts +23 -20
  350. package/src/core/plan/sql-format/keyword-case.ts +36 -1
  351. package/src/core/plan/ssl-config.ts +172 -0
  352. package/src/core/plan/types.ts +6 -0
  353. package/src/core/postgres-config.ts +71 -2
  354. package/src/core/sort/graph-builder.ts +12 -0
  355. package/src/core/sort/logical-sort.test.ts +371 -0
  356. package/src/core/sort/logical-sort.ts +32 -25
  357. package/src/core/sort/topological-sort.test.ts +275 -0
  358. package/src/core/test-utils/assert-valid-sql.ts +20 -0
  359. package/src/index.ts +26 -2
@@ -21,7 +21,7 @@ export class AlterRlsPolicySetRoles extends AlterRlsPolicyChange {
21
21
  const rolesSql = toPublic ? "PUBLIC" : targetRoles.join(", ");
22
22
  return [
23
23
  "ALTER POLICY",
24
- `${this.policy.schema}.${this.policy.name}`,
24
+ this.policy.name,
25
25
  "ON",
26
26
  `${this.policy.schema}.${this.policy.table_name}`,
27
27
  "TO",
@@ -48,7 +48,7 @@ export class AlterRlsPolicySetUsingExpression extends AlterRlsPolicyChange {
48
48
  const expr = this.usingExpression ?? "true";
49
49
  return [
50
50
  "ALTER POLICY",
51
- `${this.policy.schema}.${this.policy.name}`,
51
+ this.policy.name,
52
52
  "ON",
53
53
  `${this.policy.schema}.${this.policy.table_name}`,
54
54
  "USING",
@@ -75,7 +75,7 @@ export class AlterRlsPolicySetWithCheckExpression extends AlterRlsPolicyChange {
75
75
  const expr = this.withCheckExpression ?? "true";
76
76
  return [
77
77
  "ALTER POLICY",
78
- `${this.policy.schema}.${this.policy.name}`,
78
+ this.policy.name,
79
79
  "ON",
80
80
  `${this.policy.schema}.${this.policy.table_name}`,
81
81
  "WITH CHECK",
@@ -6,9 +6,9 @@ declare const rlsPolicyPropsSchema: z.ZodObject<{
6
6
  name: z.ZodString;
7
7
  table_name: z.ZodString;
8
8
  command: z.ZodEnum<{
9
+ a: "a";
9
10
  r: "r";
10
11
  w: "w";
11
- a: "a";
12
12
  d: "d";
13
13
  "*": "*";
14
14
  }>;
@@ -39,7 +39,7 @@ export declare class RlsPolicy extends BasePgModel {
39
39
  name: string;
40
40
  };
41
41
  get dataFields(): {
42
- command: "r" | "w" | "a" | "d" | "*";
42
+ command: "a" | "r" | "w" | "d" | "*";
43
43
  permissive: boolean;
44
44
  roles: string[];
45
45
  using_expression: string | null;
@@ -31,8 +31,15 @@ export function diffRoles(ctx, main, branch) {
31
31
  if (role.comment !== null) {
32
32
  changes.push(new CreateCommentOnRole({ role }));
33
33
  }
34
- // MEMBERSHIPS: Grant memberships immediately after role creation
34
+ // MEMBERSHIPS: Grant memberships immediately after role creation.
35
+ // Members are already deduplicated by the Role model constructor.
35
36
  for (const membership of role.members) {
37
+ // Skip memberships where the member is the grantor (auto-created by
38
+ // CREATE ROLE — re-granting them, especially WITH ADMIN OPTION, fails
39
+ // with "ADMIN option cannot be granted back to your own grantor").
40
+ if (membership.grantor === membership.member) {
41
+ continue;
42
+ }
36
43
  changes.push(new GrantRoleMembership({
37
44
  role,
38
45
  member: membership.member,
@@ -45,6 +52,8 @@ export function diffRoles(ctx, main, branch) {
45
52
  }
46
53
  // DEFAULT PRIVILEGES: Grant default privileges immediately after role creation
47
54
  for (const defaultPriv of role.default_privileges) {
55
+ if (defaultPriv.is_implicit)
56
+ continue;
48
57
  if (defaultPriv.privileges.length === 0)
49
58
  continue;
50
59
  const grantGroups = new Map();
@@ -163,11 +172,18 @@ export function diffRoles(ctx, main, branch) {
163
172
  }
164
173
  }
165
174
  // MEMBERSHIPS
175
+ // Members are already deduplicated by the Role model constructor.
166
176
  const mainMembers = new Map(mainRole.members.map((m) => [m.member, m]));
167
177
  const branchMembers = new Map(branchRole.members.map((m) => [m.member, m]));
168
178
  // Find new members to grant
169
179
  for (const [member, membership] of branchMembers) {
170
180
  if (!mainMembers.has(member)) {
181
+ // Skip memberships where the member is the grantor (auto-created by
182
+ // CREATE ROLE — re-granting them fails with "ADMIN option cannot be
183
+ // granted back to your own grantor").
184
+ if (membership.grantor === membership.member) {
185
+ continue;
186
+ }
171
187
  changes.push(new GrantRoleMembership({
172
188
  role: branchRole,
173
189
  member: membership.member,
@@ -224,6 +240,11 @@ export function diffRoles(ctx, main, branch) {
224
240
  }));
225
241
  }
226
242
  if (toGrant.admin || toGrant.inherit || toGrant.set) {
243
+ // Skip granting options back to the grantor (same restriction as
244
+ // for newly created roles).
245
+ if (branchMembership.grantor === branchMembership.member) {
246
+ continue;
247
+ }
227
248
  changes.push(new GrantRoleMembership({
228
249
  role: branchRole,
229
250
  member: branchMembership.member,
@@ -23,17 +23,18 @@ declare const rolePropsSchema: z.ZodObject<{
23
23
  default_privileges: z.ZodArray<z.ZodObject<{
24
24
  in_schema: z.ZodNullable<z.ZodString>;
25
25
  objtype: z.ZodEnum<{
26
+ n: "n";
26
27
  r: "r";
27
- S: "S";
28
28
  f: "f";
29
+ S: "S";
29
30
  T: "T";
30
- n: "n";
31
31
  }>;
32
32
  grantee: z.ZodString;
33
33
  privileges: z.ZodArray<z.ZodObject<{
34
34
  privilege: z.ZodString;
35
35
  grantable: z.ZodBoolean;
36
36
  }, z.z.core.$strip>>;
37
+ is_implicit: z.ZodBoolean;
37
38
  }, z.z.core.$strip>>;
38
39
  }, z.z.core.$strip>;
39
40
  export type RoleProps = z.infer<typeof rolePropsSchema>;
@@ -80,7 +81,7 @@ export declare class Role extends BasePgModel {
80
81
  grantable: boolean;
81
82
  }[];
82
83
  in_schema: string | null;
83
- objtype: "r" | "S" | "f" | "T" | "n";
84
+ objtype: "n" | "r" | "f" | "S" | "T";
84
85
  grantee: string;
85
86
  }[];
86
87
  };
@@ -13,6 +13,7 @@ const defaultPrivilegeSchema = z.object({
13
13
  objtype: z.enum(["r", "S", "f", "T", "n"]),
14
14
  grantee: z.string(),
15
15
  privileges: z.array(z.object({ privilege: z.string(), grantable: z.boolean() })),
16
+ is_implicit: z.boolean(),
16
17
  });
17
18
  const rolePropsSchema = z.object({
18
19
  name: z.string(),
@@ -58,7 +59,7 @@ export class Role extends BasePgModel {
58
59
  this.can_bypass_rls = props.can_bypass_rls;
59
60
  this.config = props.config;
60
61
  this.comment = props.comment;
61
- this.members = props.members;
62
+ this.members = deduplicateMembers(props.members);
62
63
  this.default_privileges = props.default_privileges;
63
64
  }
64
65
  get stableId() {
@@ -77,13 +78,16 @@ export class Role extends BasePgModel {
77
78
  Number(a.inherit_option ?? false) - Number(b.inherit_option ?? false) ||
78
79
  Number(a.set_option ?? false) - Number(b.set_option ?? false));
79
80
  });
80
- const sortedDefaultPrivs = [...this.default_privileges].map((dp) => ({
81
- ...dp,
82
- privileges: [...dp.privileges].sort((a, b) => {
83
- return (a.privilege.localeCompare(b.privilege) ||
84
- Number(a.grantable) - Number(b.grantable));
85
- }),
86
- }));
81
+ const sortedDefaultPrivs = [...this.default_privileges].map((dp) => {
82
+ const { is_implicit: _, ...rest } = dp;
83
+ return {
84
+ ...rest,
85
+ privileges: [...dp.privileges].sort((a, b) => {
86
+ return (a.privilege.localeCompare(b.privilege) ||
87
+ Number(a.grantable) - Number(b.grantable));
88
+ }),
89
+ };
90
+ });
87
91
  sortedDefaultPrivs.sort((a, b) => {
88
92
  return ((a.in_schema ?? "").localeCompare(b.in_schema ?? "") ||
89
93
  a.objtype.localeCompare(b.objtype) ||
@@ -105,6 +109,46 @@ export class Role extends BasePgModel {
105
109
  };
106
110
  }
107
111
  }
112
+ /**
113
+ * Deduplicate members by member name.
114
+ *
115
+ * In PostgreSQL 16+, `pg_auth_members` can have multiple rows for the same
116
+ * (roleid, member) pair with different grantors. Merge them into a single
117
+ * entry per member, combining options with OR so the most permissive wins.
118
+ *
119
+ * When merging, prefer a non-self grantor (grantor !== member) so that
120
+ * downstream code can detect true self-grants (auto-created by CREATE ROLE)
121
+ * by checking `grantor === member`.
122
+ */
123
+ function deduplicateMembers(members) {
124
+ const map = new Map();
125
+ for (const m of members) {
126
+ const existing = map.get(m.member);
127
+ if (existing) {
128
+ // admin_option is always boolean (non-nullable in schema)
129
+ existing.admin_option = existing.admin_option || m.admin_option;
130
+ // inherit_option and set_option are nullish (only available in PG 16+)
131
+ if (m.inherit_option != null) {
132
+ existing.inherit_option =
133
+ (existing.inherit_option ?? false) || m.inherit_option;
134
+ }
135
+ if (m.set_option != null) {
136
+ existing.set_option = (existing.set_option ?? false) || m.set_option;
137
+ }
138
+ // Prefer a non-self grantor so diff can detect true self-grants.
139
+ // Once a non-self grantor is chosen the value is kept (the specific
140
+ // non-self grantor doesn't matter — only the self vs non-self
141
+ // distinction is used downstream).
142
+ if (existing.grantor === existing.member && m.grantor !== m.member) {
143
+ existing.grantor = m.grantor;
144
+ }
145
+ }
146
+ else {
147
+ map.set(m.member, { ...m });
148
+ }
149
+ }
150
+ return [...map.values()];
151
+ }
108
152
  export async function extractRoles(pool) {
109
153
  // Check PostgreSQL version capabilities for membership options
110
154
  const { rows: capabilitiesRows } = await pool.query(sql `
@@ -172,13 +216,15 @@ export async function extractRoles(pool) {
172
216
  THEN 'PUBLIC'
173
217
  ELSE s.grantee::regrole::text
174
218
  END,
175
- 'privileges', s.privileges
219
+ 'privileges', s.privileges,
220
+ 'is_implicit', s.is_implicit
176
221
  )
177
222
  ORDER BY s.defaclnamespace NULLS FIRST,
178
223
  s.defaclobjtype,
179
224
  s.grantee
180
225
  )
181
226
  FROM (
227
+ -- Explicit entries from pg_default_acl
182
228
  SELECT
183
229
  d.defaclnamespace,
184
230
  d.defaclobjtype,
@@ -189,12 +235,41 @@ export async function extractRoles(pool) {
189
235
  'grantable', x.is_grantable
190
236
  )
191
237
  ORDER BY x.privilege_type, x.is_grantable
192
- ) AS privileges
238
+ ) AS privileges,
239
+ false AS is_implicit
193
240
  FROM pg_default_acl d
194
241
  CROSS JOIN LATERAL aclexplode(COALESCE(d.defaclacl, ARRAY[]::aclitem[]))
195
242
  AS x(grantor, grantee, privilege_type, is_grantable)
196
243
  WHERE d.defaclrole = r.oid
197
244
  GROUP BY d.defaclnamespace, d.defaclobjtype, x.grantee
245
+ UNION ALL
246
+ -- Implicit defaults from acldefault() for objtypes without a
247
+ -- global pg_default_acl entry. PostgreSQL applies these implicit
248
+ -- defaults (e.g. PUBLIC gets EXECUTE on functions) when no
249
+ -- explicit ALTER DEFAULT PRIVILEGES has been issued. Including
250
+ -- them lets the diff detect REVOKEs of implicit grants.
251
+ SELECT
252
+ 0 AS defaclnamespace,
253
+ v.t::"char" AS defaclobjtype,
254
+ x.grantee,
255
+ json_agg(
256
+ json_build_object(
257
+ 'privilege', x.privilege_type,
258
+ 'grantable', x.is_grantable
259
+ )
260
+ ORDER BY x.privilege_type, x.is_grantable
261
+ ) AS privileges,
262
+ true AS is_implicit
263
+ FROM (VALUES ('r'), ('S'), ('f'), ('T'), ('n')) AS v(t)
264
+ CROSS JOIN LATERAL aclexplode(acldefault(v.t::"char", r.oid))
265
+ AS x(grantor, grantee, privilege_type, is_grantable)
266
+ WHERE NOT EXISTS (
267
+ SELECT 1 FROM pg_default_acl d2
268
+ WHERE d2.defaclrole = r.oid
269
+ AND d2.defaclobjtype = v.t::"char"
270
+ AND d2.defaclnamespace = 0
271
+ )
272
+ GROUP BY v.t, x.grantee
198
273
  ) AS s
199
274
  ),
200
275
  '[]'
@@ -263,13 +338,15 @@ export async function extractRoles(pool) {
263
338
  THEN 'PUBLIC'
264
339
  ELSE s.grantee::regrole::text
265
340
  END,
266
- 'privileges', s.privileges
341
+ 'privileges', s.privileges,
342
+ 'is_implicit', s.is_implicit
267
343
  )
268
344
  ORDER BY s.defaclnamespace NULLS FIRST,
269
345
  s.defaclobjtype,
270
346
  s.grantee
271
347
  )
272
348
  FROM (
349
+ -- Explicit entries from pg_default_acl
273
350
  SELECT
274
351
  d.defaclnamespace,
275
352
  d.defaclobjtype,
@@ -280,12 +357,41 @@ export async function extractRoles(pool) {
280
357
  'grantable', x.is_grantable
281
358
  )
282
359
  ORDER BY x.privilege_type, x.is_grantable
283
- ) AS privileges
360
+ ) AS privileges,
361
+ false AS is_implicit
284
362
  FROM pg_default_acl d
285
363
  CROSS JOIN LATERAL aclexplode(COALESCE(d.defaclacl, ARRAY[]::aclitem[]))
286
364
  AS x(grantor, grantee, privilege_type, is_grantable)
287
365
  WHERE d.defaclrole = r.oid
288
366
  GROUP BY d.defaclnamespace, d.defaclobjtype, x.grantee
367
+ UNION ALL
368
+ -- Implicit defaults from acldefault() for objtypes without a
369
+ -- global pg_default_acl entry. PostgreSQL applies these implicit
370
+ -- defaults (e.g. PUBLIC gets EXECUTE on functions) when no
371
+ -- explicit ALTER DEFAULT PRIVILEGES has been issued. Including
372
+ -- them lets the diff detect REVOKEs of implicit grants.
373
+ SELECT
374
+ 0 AS defaclnamespace,
375
+ v.t::"char" AS defaclobjtype,
376
+ x.grantee,
377
+ json_agg(
378
+ json_build_object(
379
+ 'privilege', x.privilege_type,
380
+ 'grantable', x.is_grantable
381
+ )
382
+ ORDER BY x.privilege_type, x.is_grantable
383
+ ) AS privileges,
384
+ true AS is_implicit
385
+ FROM (VALUES ('r'), ('S'), ('f'), ('T'), ('n')) AS v(t)
386
+ CROSS JOIN LATERAL aclexplode(acldefault(v.t::"char", r.oid))
387
+ AS x(grantor, grantee, privilege_type, is_grantable)
388
+ WHERE NOT EXISTS (
389
+ SELECT 1 FROM pg_default_acl d2
390
+ WHERE d2.defaclrole = r.oid
391
+ AND d2.defaclobjtype = v.t::"char"
392
+ AND d2.defaclnamespace = 0
393
+ )
394
+ GROUP BY v.t, x.grantee
289
395
  ) AS s
290
396
  ),
291
397
  '[]'
@@ -13,10 +13,10 @@ declare const rulePropsSchema: z.ZodObject<{
13
13
  table_name: z.ZodString;
14
14
  relation_kind: z.ZodEnum<{
15
15
  r: "r";
16
- f: "f";
17
16
  v: "v";
18
17
  m: "m";
19
18
  p: "p";
19
+ f: "f";
20
20
  }>;
21
21
  event: z.ZodEnum<{
22
22
  DELETE: "DELETE";
@@ -1,5 +1,4 @@
1
- import type { DefaultPrivilegeState } from "../base.default-privileges.ts";
2
- import type { Role } from "../role/role.model.ts";
1
+ import type { ObjectDiffContext } from "../diff-context.ts";
3
2
  import type { SchemaChange } from "./changes/schema.types.ts";
4
3
  import type { Schema } from "./schema.model.ts";
5
4
  /**
@@ -10,9 +9,4 @@ import type { Schema } from "./schema.model.ts";
10
9
  * @param branch - The schemas in the branch catalog.
11
10
  * @returns A list of changes to apply to main to make it match branch.
12
11
  */
13
- export declare function diffSchemas(ctx: {
14
- version: number;
15
- currentUser: string;
16
- defaultPrivilegeState: DefaultPrivilegeState;
17
- mainRoles: Record<string, Role>;
18
- }, main: Record<string, Schema>, branch: Record<string, Schema>): SchemaChange[];
12
+ export declare function diffSchemas(ctx: Pick<ObjectDiffContext, "version" | "currentUser" | "defaultPrivilegeState">, main: Record<string, Schema>, branch: Record<string, Schema>): SchemaChange[];
@@ -1,5 +1,5 @@
1
1
  import { diffObjects } from "../base.diff.js";
2
- import { diffPrivileges, groupPrivilegesByGrantable, } from "../base.privilege-diff.js";
2
+ import { diffPrivileges, emitObjectPrivilegeChanges, } from "../base.privilege-diff.js";
3
3
  import { AlterSchemaChangeOwner } from "./changes/schema.alter.js";
4
4
  import { CreateCommentOnSchema, DropCommentOnSchema, } from "./changes/schema.comment.js";
5
5
  import { CreateSchema } from "./changes/schema.create.js";
@@ -29,47 +29,18 @@ export function diffSchemas(ctx, main, branch) {
29
29
  // needed to reach the final desired state.
30
30
  // Note: Schemas don't have a schema property, so we pass empty string
31
31
  const effectiveDefaults = ctx.defaultPrivilegeState.getEffectiveDefaults(ctx.currentUser, "schema", "");
32
+ const creatorFilteredDefaults = sc.owner !== ctx.currentUser
33
+ ? effectiveDefaults.filter((p) => p.grantee !== ctx.currentUser)
34
+ : effectiveDefaults;
32
35
  const desiredPrivileges = sc.privileges;
33
36
  // Filter out owner privileges - owner always has ALL privileges implicitly
34
37
  // and shouldn't be compared. Use the schema owner as the reference.
35
- const privilegeResults = diffPrivileges(effectiveDefaults, desiredPrivileges, sc.owner, ctx.mainRoles);
36
- // Generate grant changes
37
- for (const [grantee, result] of privilegeResults) {
38
- if (result.grants.length > 0) {
39
- const grantGroups = groupPrivilegesByGrantable(result.grants);
40
- for (const [grantable, list] of grantGroups) {
41
- void grantable;
42
- changes.push(new GrantSchemaPrivileges({
43
- schema: sc,
44
- grantee,
45
- privileges: list,
46
- version: ctx.version,
47
- }));
48
- }
49
- }
50
- // Generate revoke changes
51
- if (result.revokes.length > 0) {
52
- const revokeGroups = groupPrivilegesByGrantable(result.revokes);
53
- for (const [grantable, list] of revokeGroups) {
54
- void grantable;
55
- changes.push(new RevokeSchemaPrivileges({
56
- schema: sc,
57
- grantee,
58
- privileges: list,
59
- version: ctx.version,
60
- }));
61
- }
62
- }
63
- // Generate revoke grant option changes
64
- if (result.revokeGrantOption.length > 0) {
65
- changes.push(new RevokeGrantOptionSchemaPrivileges({
66
- schema: sc,
67
- grantee,
68
- privilegeNames: result.revokeGrantOption,
69
- version: ctx.version,
70
- }));
71
- }
72
- }
38
+ const privilegeResults = diffPrivileges(creatorFilteredDefaults, desiredPrivileges, sc.owner);
39
+ changes.push(...emitObjectPrivilegeChanges(privilegeResults, sc, sc, "schema", {
40
+ Grant: GrantSchemaPrivileges,
41
+ Revoke: RevokeSchemaPrivileges,
42
+ RevokeGrantOption: RevokeGrantOptionSchemaPrivileges,
43
+ }, ctx.version));
73
44
  }
74
45
  for (const schemaId of dropped) {
75
46
  changes.push(new DropSchema({ schema: main[schemaId] }));
@@ -96,44 +67,12 @@ export function diffSchemas(ctx, main, branch) {
96
67
  // PRIVILEGES
97
68
  // Filter out owner privileges - owner always has ALL privileges implicitly
98
69
  // and shouldn't be compared. Use branch owner as the reference.
99
- const privilegeResults = diffPrivileges(mainSchema.privileges, branchSchema.privileges, branchSchema.owner, ctx.mainRoles);
100
- for (const [grantee, result] of privilegeResults) {
101
- // Generate grant changes
102
- if (result.grants.length > 0) {
103
- const grantGroups = groupPrivilegesByGrantable(result.grants);
104
- for (const [grantable, list] of grantGroups) {
105
- void grantable;
106
- changes.push(new GrantSchemaPrivileges({
107
- schema: branchSchema,
108
- grantee,
109
- privileges: list,
110
- version: ctx.version,
111
- }));
112
- }
113
- }
114
- // Generate revoke changes
115
- if (result.revokes.length > 0) {
116
- const revokeGroups = groupPrivilegesByGrantable(result.revokes);
117
- for (const [grantable, list] of revokeGroups) {
118
- void grantable;
119
- changes.push(new RevokeSchemaPrivileges({
120
- schema: mainSchema,
121
- grantee,
122
- privileges: list,
123
- version: ctx.version,
124
- }));
125
- }
126
- }
127
- // Generate revoke grant option changes
128
- if (result.revokeGrantOption.length > 0) {
129
- changes.push(new RevokeGrantOptionSchemaPrivileges({
130
- schema: mainSchema,
131
- grantee,
132
- privilegeNames: result.revokeGrantOption,
133
- version: ctx.version,
134
- }));
135
- }
136
- }
70
+ const privilegeResults = diffPrivileges(mainSchema.privileges, branchSchema.privileges, branchSchema.owner);
71
+ changes.push(...emitObjectPrivilegeChanges(privilegeResults, branchSchema, mainSchema, "schema", {
72
+ Grant: GrantSchemaPrivileges,
73
+ Revoke: RevokeSchemaPrivileges,
74
+ RevokeGrantOption: RevokeGrantOptionSchemaPrivileges,
75
+ }, ctx.version));
137
76
  // Note: Schema renaming would also use ALTER SCHEMA ... RENAME TO ...
138
77
  // But since our Schema model uses 'schema' as the identity field,
139
78
  // a name change would be handled as drop + create by diffObjects()
@@ -70,7 +70,7 @@ export async function extractSchemas(pool) {
70
70
  )
71
71
  order by x.grantee, x.privilege_type
72
72
  )
73
- from lateral aclexplode(nspacl) as x(grantor, grantee, privilege_type, is_grantable)
73
+ from lateral aclexplode(COALESCE(nspacl, acldefault('n', nspowner))) as x(grantor, grantee, privilege_type, is_grantable)
74
74
  ), '[]'
75
75
  ) as privileges
76
76
  from
@@ -1,5 +1,4 @@
1
- import type { DefaultPrivilegeState } from "../base.default-privileges.ts";
2
- import type { Role } from "../role/role.model.ts";
1
+ import type { ObjectDiffContext } from "../diff-context.ts";
3
2
  import type { Table } from "../table/table.model.ts";
4
3
  import type { SequenceChange } from "./changes/sequence.types.ts";
5
4
  import type { Sequence } from "./sequence.model.ts";
@@ -12,9 +11,4 @@ import type { Sequence } from "./sequence.model.ts";
12
11
  * @param branchTables - The tables in the branch catalog (used to check if owning tables are being dropped).
13
12
  * @returns A list of changes to apply to main to make it match branch.
14
13
  */
15
- export declare function diffSequences(ctx: {
16
- version: number;
17
- currentUser: string;
18
- defaultPrivilegeState: DefaultPrivilegeState;
19
- mainRoles: Record<string, Role>;
20
- }, main: Record<string, Sequence>, branch: Record<string, Sequence>, branchTables?: Record<string, Table>): SequenceChange[];
14
+ export declare function diffSequences(ctx: Pick<ObjectDiffContext, "version" | "currentUser" | "defaultPrivilegeState">, main: Record<string, Sequence>, branch: Record<string, Sequence>, branchTables?: Record<string, Table>): SequenceChange[];
@@ -1,5 +1,5 @@
1
1
  import { diffObjects } from "../base.diff.js";
2
- import { diffPrivileges, groupPrivilegesByGrantable, } from "../base.privilege-diff.js";
2
+ import { diffPrivileges, emitObjectPrivilegeChanges, } from "../base.privilege-diff.js";
3
3
  import { hasNonAlterableChanges } from "../utils.js";
4
4
  import { AlterSequenceSetOptions, AlterSequenceSetOwnedBy, } from "./changes/sequence.alter.js";
5
5
  import { CreateCommentOnSequence, DropCommentOnSequence, } from "./changes/sequence.comment.js";
@@ -43,48 +43,18 @@ export function diffSequences(ctx, main, branch, branchTables = {}) {
43
43
  // We compare default privileges against desired privileges to generate REVOKE/GRANT statements
44
44
  // needed to reach the final desired state.
45
45
  const effectiveDefaults = ctx.defaultPrivilegeState.getEffectiveDefaults(ctx.currentUser, "sequence", createdSeq.schema ?? "");
46
+ const creatorFilteredDefaults = createdSeq.owner !== ctx.currentUser
47
+ ? effectiveDefaults.filter((p) => p.grantee !== ctx.currentUser)
48
+ : effectiveDefaults;
46
49
  const desiredPrivileges = createdSeq.privileges;
47
50
  // Filter out owner privileges - owner always has ALL privileges implicitly
48
51
  // and shouldn't be compared. Use the sequence owner as the reference.
49
- // Superuser privileges are filtered inside diffPrivileges.
50
- const privilegeResults = diffPrivileges(effectiveDefaults, desiredPrivileges, createdSeq.owner, ctx.mainRoles);
51
- // Generate grant changes
52
- for (const [grantee, result] of privilegeResults) {
53
- if (result.grants.length > 0) {
54
- const grantGroups = groupPrivilegesByGrantable(result.grants);
55
- for (const [grantable, list] of grantGroups) {
56
- void grantable;
57
- changes.push(new GrantSequencePrivileges({
58
- sequence: createdSeq,
59
- grantee,
60
- privileges: list,
61
- version: ctx.version,
62
- }));
63
- }
64
- }
65
- // Generate revoke changes
66
- if (result.revokes.length > 0) {
67
- const revokeGroups = groupPrivilegesByGrantable(result.revokes);
68
- for (const [grantable, list] of revokeGroups) {
69
- void grantable;
70
- changes.push(new RevokeSequencePrivileges({
71
- sequence: createdSeq,
72
- grantee,
73
- privileges: list,
74
- version: ctx.version,
75
- }));
76
- }
77
- }
78
- // Generate revoke grant option changes
79
- if (result.revokeGrantOption.length > 0) {
80
- changes.push(new RevokeGrantOptionSequencePrivileges({
81
- sequence: createdSeq,
82
- grantee,
83
- privilegeNames: result.revokeGrantOption,
84
- version: ctx.version,
85
- }));
86
- }
87
- }
52
+ const privilegeResults = diffPrivileges(creatorFilteredDefaults, desiredPrivileges, createdSeq.owner);
53
+ changes.push(...emitObjectPrivilegeChanges(privilegeResults, createdSeq, createdSeq, "sequence", {
54
+ Grant: GrantSequencePrivileges,
55
+ Revoke: RevokeSequencePrivileges,
56
+ RevokeGrantOption: RevokeGrantOptionSequencePrivileges,
57
+ }, ctx.version));
88
58
  }
89
59
  for (const sequenceId of dropped) {
90
60
  const sequence = main[sequenceId];
@@ -210,45 +180,12 @@ export function diffSequences(ctx, main, branch, branchTables = {}) {
210
180
  // PRIVILEGES
211
181
  // Filter out owner privileges - owner always has ALL privileges implicitly
212
182
  // and shouldn't be compared. Use branch owner as the reference.
213
- // Superuser privileges are filtered inside diffPrivileges.
214
- const privilegeResults = diffPrivileges(mainSequence.privileges, branchSequence.privileges, branchSequence.owner, ctx.mainRoles);
215
- for (const [grantee, result] of privilegeResults) {
216
- // Generate grant changes
217
- if (result.grants.length > 0) {
218
- const grantGroups = groupPrivilegesByGrantable(result.grants);
219
- for (const [grantable, list] of grantGroups) {
220
- void grantable;
221
- changes.push(new GrantSequencePrivileges({
222
- sequence: branchSequence,
223
- grantee,
224
- privileges: list,
225
- version: ctx.version,
226
- }));
227
- }
228
- }
229
- // Generate revoke changes
230
- if (result.revokes.length > 0) {
231
- const revokeGroups = groupPrivilegesByGrantable(result.revokes);
232
- for (const [grantable, list] of revokeGroups) {
233
- void grantable;
234
- changes.push(new RevokeSequencePrivileges({
235
- sequence: mainSequence,
236
- grantee,
237
- privileges: list,
238
- version: ctx.version,
239
- }));
240
- }
241
- }
242
- // Generate revoke grant option changes
243
- if (result.revokeGrantOption.length > 0) {
244
- changes.push(new RevokeGrantOptionSequencePrivileges({
245
- sequence: mainSequence,
246
- grantee,
247
- privilegeNames: result.revokeGrantOption,
248
- version: ctx.version,
249
- }));
250
- }
251
- }
183
+ const privilegeResults = diffPrivileges(mainSequence.privileges, branchSequence.privileges, branchSequence.owner);
184
+ changes.push(...emitObjectPrivilegeChanges(privilegeResults, branchSequence, mainSequence, "sequence", {
185
+ Grant: GrantSequencePrivileges,
186
+ Revoke: RevokeSequencePrivileges,
187
+ RevokeGrantOption: RevokeGrantOptionSequencePrivileges,
188
+ }, ctx.version));
252
189
  // Note: Sequence renaming would also use ALTER SEQUENCE ... RENAME TO ...
253
190
  // But since our Sequence model uses 'name' as the identity field,
254
191
  // a name change would be handled as drop + create by diffObjects()
@@ -132,7 +132,7 @@ select
132
132
  )
133
133
  order by x.grantee, x.privilege_type
134
134
  )
135
- from lateral aclexplode(c.relacl) as x(grantor, grantee, privilege_type, is_grantable)
135
+ from lateral aclexplode(COALESCE(c.relacl, acldefault('S', c.relowner))) as x(grantor, grantee, privilege_type, is_grantable)
136
136
  ), '[]'
137
137
  ) as privileges,
138
138
  c.relowner::regrole::text as owner
@@ -1,5 +1,4 @@
1
+ import type { ObjectDiffContext } from "../diff-context.ts";
1
2
  import type { SubscriptionChange } from "./changes/subscription.types.ts";
2
3
  import type { Subscription } from "./subscription.model.ts";
3
- export declare function diffSubscriptions(ctx: {
4
- currentUser: string;
5
- }, main: Record<string, Subscription>, branch: Record<string, Subscription>): SubscriptionChange[];
4
+ export declare function diffSubscriptions(ctx: Pick<ObjectDiffContext, "currentUser">, main: Record<string, Subscription>, branch: Record<string, Subscription>): SubscriptionChange[];