@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
@@ -1,11 +1,10 @@
1
- import type { DefaultPrivilegeState } from "../../base.default-privileges.ts";
2
1
  import { diffObjects } from "../../base.diff.ts";
3
2
  import {
4
3
  diffPrivileges,
4
+ emitObjectPrivilegeChanges,
5
5
  filterPublicBuiltInDefaults,
6
- groupPrivilegesByGrantable,
7
6
  } from "../../base.privilege-diff.ts";
8
- import type { Role } from "../../role/role.model.ts";
7
+ import type { ObjectDiffContext } from "../../diff-context.ts";
9
8
  import { hasNonAlterableChanges } from "../../utils.ts";
10
9
  import { AlterRangeChangeOwner } from "./changes/range.alter.ts";
11
10
  import {
@@ -31,12 +30,10 @@ import type { Range } from "./range.model.ts";
31
30
  * @returns A list of changes to apply to main to make it match branch.
32
31
  */
33
32
  export function diffRanges(
34
- ctx: {
35
- version: number;
36
- currentUser: string;
37
- defaultPrivilegeState: DefaultPrivilegeState;
38
- mainRoles: Record<string, Role>;
39
- },
33
+ ctx: Pick<
34
+ ObjectDiffContext,
35
+ "version" | "currentUser" | "defaultPrivilegeState"
36
+ >,
40
37
  main: Record<string, Range>,
41
38
  branch: Record<string, Range>,
42
39
  ): RangeChange[] {
@@ -73,6 +70,10 @@ export function diffRanges(
73
70
  "range",
74
71
  createdRange.schema ?? "",
75
72
  );
73
+ const creatorFilteredDefaults =
74
+ createdRange.owner !== ctx.currentUser
75
+ ? effectiveDefaults.filter((p) => p.grantee !== ctx.currentUser)
76
+ : effectiveDefaults;
76
77
  // Filter out PUBLIC's built-in default USAGE privilege (PostgreSQL grants it automatically)
77
78
  // Reference: https://www.postgresql.org/docs/17/ddl-priv.html Table 5.2
78
79
  // This prevents generating unnecessary "GRANT USAGE TO PUBLIC" statements
@@ -83,56 +84,25 @@ export function diffRanges(
83
84
  // Filter out owner privileges - owner always has ALL privileges implicitly
84
85
  // and shouldn't be compared. Use the range owner as the reference.
85
86
  const privilegeResults = diffPrivileges(
86
- effectiveDefaults,
87
+ filterPublicBuiltInDefaults("range", creatorFilteredDefaults),
87
88
  desiredPrivileges,
88
89
  createdRange.owner,
89
90
  );
90
91
 
91
- // Generate grant changes
92
- for (const [grantee, result] of privilegeResults) {
93
- if (result.grants.length > 0) {
94
- const grantGroups = groupPrivilegesByGrantable(result.grants);
95
- for (const [grantable, list] of grantGroups) {
96
- void grantable;
97
- changes.push(
98
- new GrantRangePrivileges({
99
- range: createdRange,
100
- grantee,
101
- privileges: list,
102
- version: ctx.version,
103
- }),
104
- );
105
- }
106
- }
107
-
108
- // Generate revoke changes
109
- if (result.revokes.length > 0) {
110
- const revokeGroups = groupPrivilegesByGrantable(result.revokes);
111
- for (const [grantable, list] of revokeGroups) {
112
- void grantable;
113
- changes.push(
114
- new RevokeRangePrivileges({
115
- range: createdRange,
116
- grantee,
117
- privileges: list,
118
- version: ctx.version,
119
- }),
120
- );
121
- }
122
- }
123
-
124
- // Generate revoke grant option changes
125
- if (result.revokeGrantOption.length > 0) {
126
- changes.push(
127
- new RevokeGrantOptionRangePrivileges({
128
- range: createdRange,
129
- grantee,
130
- privilegeNames: result.revokeGrantOption,
131
- version: ctx.version,
132
- }),
133
- );
134
- }
135
- }
92
+ changes.push(
93
+ ...(emitObjectPrivilegeChanges(
94
+ privilegeResults,
95
+ createdRange,
96
+ createdRange,
97
+ "range",
98
+ {
99
+ Grant: GrantRangePrivileges,
100
+ Revoke: RevokeRangePrivileges,
101
+ RevokeGrantOption: RevokeGrantOptionRangePrivileges,
102
+ },
103
+ ctx.version,
104
+ ) as RangeChange[]),
105
+ );
136
106
  }
137
107
 
138
108
  for (const id of dropped) {
@@ -204,54 +174,22 @@ export function diffRanges(
204
174
  mainPrivilegesFiltered,
205
175
  branchPrivilegesFiltered,
206
176
  branchRange.owner,
207
- ctx.mainRoles,
208
177
  );
209
178
 
210
- for (const [grantee, result] of privilegeResults) {
211
- // Generate grant changes
212
- if (result.grants.length > 0) {
213
- const grantGroups = groupPrivilegesByGrantable(result.grants);
214
- for (const [grantable, list] of grantGroups) {
215
- void grantable;
216
- changes.push(
217
- new GrantRangePrivileges({
218
- range: branchRange,
219
- grantee,
220
- privileges: list,
221
- version: ctx.version,
222
- }),
223
- );
224
- }
225
- }
226
-
227
- // Generate revoke changes
228
- if (result.revokes.length > 0) {
229
- const revokeGroups = groupPrivilegesByGrantable(result.revokes);
230
- for (const [grantable, list] of revokeGroups) {
231
- void grantable;
232
- changes.push(
233
- new RevokeRangePrivileges({
234
- range: mainRange,
235
- grantee,
236
- privileges: list,
237
- version: ctx.version,
238
- }),
239
- );
240
- }
241
- }
242
-
243
- // Generate revoke grant option changes
244
- if (result.revokeGrantOption.length > 0) {
245
- changes.push(
246
- new RevokeGrantOptionRangePrivileges({
247
- range: mainRange,
248
- grantee,
249
- privilegeNames: result.revokeGrantOption,
250
- version: ctx.version,
251
- }),
252
- );
253
- }
254
- }
179
+ changes.push(
180
+ ...(emitObjectPrivilegeChanges(
181
+ privilegeResults,
182
+ branchRange,
183
+ mainRange,
184
+ "range",
185
+ {
186
+ Grant: GrantRangePrivileges,
187
+ Revoke: RevokeRangePrivileges,
188
+ RevokeGrantOption: RevokeGrantOptionRangePrivileges,
189
+ },
190
+ ctx.version,
191
+ ) as RangeChange[]),
192
+ );
255
193
  }
256
194
  }
257
195
 
@@ -164,7 +164,7 @@ select
164
164
  )
165
165
  order by x.grantee, x.privilege_type
166
166
  )
167
- from lateral aclexplode(t.typacl) as x(grantor, grantee, privilege_type, is_grantable)
167
+ from lateral aclexplode(COALESCE(t.typacl, acldefault('T', t.typowner))) as x(grantor, grantee, privilege_type, is_grantable)
168
168
  ), '[]'
169
169
  ) as privileges
170
170
  from pg_catalog.pg_range r
@@ -1,4 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
+ import { assertValidSql } from "../../../test-utils/assert-valid-sql.ts";
2
3
  import { View, type ViewProps } from "../view.model.ts";
3
4
  import {
4
5
  AlterViewChangeOwner,
@@ -8,7 +9,7 @@ import {
8
9
 
9
10
  describe.concurrent("view", () => {
10
11
  describe("alter", () => {
11
- test("change owner", () => {
12
+ test("change owner", async () => {
12
13
  const props: Omit<ViewProps, "owner"> = {
13
14
  schema: "public",
14
15
  name: "test_view",
@@ -39,13 +40,15 @@ describe.concurrent("view", () => {
39
40
  owner: "new_owner",
40
41
  });
41
42
 
43
+ await assertValidSql(change.serialize());
44
+
42
45
  expect(change.serialize()).toBe(
43
46
  "ALTER VIEW public.test_view OWNER TO new_owner",
44
47
  );
45
48
  });
46
49
  });
47
50
 
48
- test("set options", () => {
51
+ test("set options", async () => {
49
52
  const props: Omit<ViewProps, "options"> = {
50
53
  schema: "public",
51
54
  name: "test_view",
@@ -72,12 +75,13 @@ describe.concurrent("view", () => {
72
75
  view: main,
73
76
  options: ["security_barrier=false"],
74
77
  });
78
+ await assertValidSql(change.serialize());
75
79
  expect(change.serialize()).toBe(
76
80
  "ALTER VIEW public.test_view SET (security_barrier=false)",
77
81
  );
78
82
  });
79
83
 
80
- test("reset options", () => {
84
+ test("reset options", async () => {
81
85
  const view = new View({
82
86
  schema: "public",
83
87
  name: "test_view",
@@ -103,6 +107,7 @@ describe.concurrent("view", () => {
103
107
  view,
104
108
  params: ["check_option"],
105
109
  });
110
+ await assertValidSql(change.serialize());
106
111
  expect(change.serialize()).toBe(
107
112
  "ALTER VIEW public.test_view RESET (check_option)",
108
113
  );
@@ -1,9 +1,10 @@
1
1
  import { describe, expect, test } from "bun:test";
2
+ import { assertValidSql } from "../../../test-utils/assert-valid-sql.ts";
2
3
  import { View } from "../view.model.ts";
3
4
  import { CreateView } from "./view.create.ts";
4
5
 
5
6
  describe("view", () => {
6
- test("create", () => {
7
+ test("create", async () => {
7
8
  const view = new View({
8
9
  schema: "public",
9
10
  name: "test_view",
@@ -29,12 +30,14 @@ describe("view", () => {
29
30
  view,
30
31
  });
31
32
 
33
+ await assertValidSql(change.serialize());
34
+
32
35
  expect(change.serialize()).toBe(
33
36
  "CREATE VIEW public.test_view AS SELECT * FROM test_table",
34
37
  );
35
38
  });
36
39
 
37
- test("create with options", () => {
40
+ test("create with options", async () => {
38
41
  const view = new View({
39
42
  schema: "public",
40
43
  name: "test_view",
@@ -58,6 +61,8 @@ describe("view", () => {
58
61
 
59
62
  const change = new CreateView({ view });
60
63
 
64
+ await assertValidSql(change.serialize());
65
+
61
66
  expect(change.serialize()).toBe(
62
67
  "CREATE VIEW public.test_view WITH (security_barrier=true, check_option=local) AS SELECT * FROM test_table",
63
68
  );
@@ -1,9 +1,10 @@
1
1
  import { describe, expect, test } from "bun:test";
2
+ import { assertValidSql } from "../../../test-utils/assert-valid-sql.ts";
2
3
  import { View } from "../view.model.ts";
3
4
  import { DropView } from "./view.drop.ts";
4
5
 
5
6
  describe("view", () => {
6
- test("drop", () => {
7
+ test("drop", async () => {
7
8
  const view = new View({
8
9
  schema: "public",
9
10
  name: "test_view",
@@ -29,6 +30,8 @@ describe("view", () => {
29
30
  view,
30
31
  });
31
32
 
33
+ await assertValidSql(change.serialize());
34
+
32
35
  expect(change.serialize()).toBe("DROP VIEW public.test_view");
33
36
  });
34
37
  });
@@ -5,8 +5,17 @@ import {
5
5
  AlterViewResetOptions,
6
6
  AlterViewSetOptions,
7
7
  } from "./changes/view.alter.ts";
8
+ import {
9
+ CreateCommentOnView,
10
+ DropCommentOnView,
11
+ } from "./changes/view.comment.ts";
8
12
  import { CreateView } from "./changes/view.create.ts";
9
13
  import { DropView } from "./changes/view.drop.ts";
14
+ import {
15
+ GrantViewPrivileges,
16
+ RevokeGrantOptionViewPrivileges,
17
+ RevokeViewPrivileges,
18
+ } from "./changes/view.privilege.ts";
10
19
  import { diffViews } from "./view.diff.ts";
11
20
  import { View, type ViewProps } from "./view.model.ts";
12
21
 
@@ -31,6 +40,13 @@ const base: ViewProps = {
31
40
  privileges: [],
32
41
  };
33
42
 
43
+ const makeView = (override: Partial<ViewProps> = {}) =>
44
+ new View({
45
+ ...base,
46
+ ...override,
47
+ privileges: override.privileges ?? [...base.privileges],
48
+ });
49
+
34
50
  const testContext = {
35
51
  version: 170000,
36
52
  currentUser: "postgres",
@@ -88,4 +104,70 @@ describe.concurrent("view.diff", () => {
88
104
  expect(changes).toHaveLength(1);
89
105
  expect(changes[0]).toBeInstanceOf(CreateView);
90
106
  });
107
+
108
+ test("create with privileges emits grant changes", () => {
109
+ const v = makeView({
110
+ privileges: [
111
+ { grantee: "role_select", privilege: "SELECT", grantable: false },
112
+ ],
113
+ });
114
+ const changes = diffViews(testContext, {}, { [v.stableId]: v });
115
+ expect(changes[0]).toBeInstanceOf(CreateView);
116
+ expect(changes.some((c) => c instanceof GrantViewPrivileges)).toBe(true);
117
+ });
118
+
119
+ test("create with comment emits create comment change", () => {
120
+ const v = makeView({ comment: "my view" });
121
+ const changes = diffViews(testContext, {}, { [v.stableId]: v });
122
+ expect(changes[0]).toBeInstanceOf(CreateView);
123
+ expect(changes.some((c) => c instanceof CreateCommentOnView)).toBe(true);
124
+ });
125
+
126
+ test("comment changes emit create/drop comment statements", () => {
127
+ const main = makeView();
128
+ const withComment = makeView({ comment: "view comment" });
129
+
130
+ const addComment = diffViews(
131
+ testContext,
132
+ { [main.stableId]: main },
133
+ { [withComment.stableId]: withComment },
134
+ );
135
+ expect(addComment[0]).toBeInstanceOf(CreateCommentOnView);
136
+
137
+ const dropComment = diffViews(
138
+ testContext,
139
+ { [withComment.stableId]: withComment },
140
+ { [main.stableId]: main },
141
+ );
142
+ expect(dropComment[0]).toBeInstanceOf(DropCommentOnView);
143
+ });
144
+
145
+ test("privilege diffs emit grant, revoke, and revoke grant option statements", () => {
146
+ const main = makeView({
147
+ privileges: [
148
+ { grantee: "role_select", privilege: "SELECT", grantable: false },
149
+ { grantee: "role_with_option", privilege: "SELECT", grantable: true },
150
+ { grantee: "role_removed", privilege: "SELECT", grantable: false },
151
+ ],
152
+ });
153
+ const branch = makeView({
154
+ privileges: [
155
+ { grantee: "role_select", privilege: "SELECT", grantable: true },
156
+ { grantee: "role_with_option", privilege: "SELECT", grantable: false },
157
+ { grantee: "role_new", privilege: "SELECT", grantable: false },
158
+ ],
159
+ });
160
+
161
+ const changes = diffViews(
162
+ testContext,
163
+ { [main.stableId]: main },
164
+ { [branch.stableId]: branch },
165
+ );
166
+
167
+ expect(changes.some((c) => c instanceof GrantViewPrivileges)).toBe(true);
168
+ expect(changes.some((c) => c instanceof RevokeViewPrivileges)).toBe(true);
169
+ expect(
170
+ changes.some((c) => c instanceof RevokeGrantOptionViewPrivileges),
171
+ ).toBe(true);
172
+ });
91
173
  });
@@ -1,10 +1,9 @@
1
- import type { DefaultPrivilegeState } from "../base.default-privileges.ts";
2
1
  import { diffObjects } from "../base.diff.ts";
3
2
  import {
4
3
  diffPrivileges,
5
- groupPrivilegesByColumns,
4
+ emitColumnPrivilegeChanges,
6
5
  } from "../base.privilege-diff.ts";
7
- import type { Role } from "../role/role.model.ts";
6
+ import type { ObjectDiffContext } from "../diff-context.ts";
8
7
  import { deepEqual, hasNonAlterableChanges } from "../utils.ts";
9
8
  import {
10
9
  AlterViewChangeOwner,
@@ -34,12 +33,10 @@ import type { View } from "./view.model.ts";
34
33
  * @returns A list of changes to apply to main to make it match branch.
35
34
  */
36
35
  export function diffViews(
37
- ctx: {
38
- version: number;
39
- currentUser: string;
40
- defaultPrivilegeState: DefaultPrivilegeState;
41
- mainRoles: Record<string, Role>;
42
- },
36
+ ctx: Pick<
37
+ ObjectDiffContext,
38
+ "version" | "currentUser" | "defaultPrivilegeState"
39
+ >,
43
40
  main: Record<string, View>,
44
41
  branch: Record<string, View>,
45
42
  ): ViewChange[] {
@@ -71,105 +68,34 @@ export function diffViews(
71
68
  "view",
72
69
  v.schema ?? "",
73
70
  );
71
+ const creatorFilteredDefaults =
72
+ v.owner !== ctx.currentUser
73
+ ? effectiveDefaults.filter((p) => p.grantee !== ctx.currentUser)
74
+ : effectiveDefaults;
74
75
  const desiredPrivileges = v.privileges;
75
76
  // Filter out owner privileges - owner always has ALL privileges implicitly
76
77
  // and shouldn't be compared. Use the view owner as the reference.
77
78
  const privilegeResults = diffPrivileges(
78
- effectiveDefaults,
79
+ creatorFilteredDefaults,
79
80
  desiredPrivileges,
80
81
  v.owner,
81
- ctx.mainRoles,
82
82
  );
83
83
 
84
- // Generate grant changes
85
- for (const [grantee, result] of privilegeResults) {
86
- if (result.grants.length > 0) {
87
- const grantGroups = groupPrivilegesByColumns(result.grants);
88
- for (const [, group] of grantGroups) {
89
- for (const [grantable, privSet] of group.byGrant) {
90
- const privileges = Array.from(privSet).map((priv) => ({
91
- privilege: priv,
92
- grantable,
93
- }));
94
- changes.push(
95
- new GrantViewPrivileges({
96
- view: v,
97
- grantee,
98
- privileges,
99
- columns: group.columns,
100
- version: ctx.version,
101
- }),
102
- );
103
- }
104
- }
105
- }
106
-
107
- // Generate revoke changes
108
- if (result.revokes.length > 0) {
109
- const revokeGroups = groupPrivilegesByColumns(result.revokes);
110
- for (const [, group] of revokeGroups) {
111
- const allPrivileges = new Set<string>();
112
- for (const [, privSet] of group.byGrant) {
113
- for (const priv of privSet) {
114
- allPrivileges.add(priv);
115
- }
116
- }
117
- const privileges = Array.from(allPrivileges).map((priv) => ({
118
- privilege: priv,
119
- grantable: false,
120
- }));
121
- changes.push(
122
- new RevokeViewPrivileges({
123
- view: v,
124
- grantee,
125
- privileges,
126
- columns: group.columns,
127
- version: ctx.version,
128
- }),
129
- );
130
- }
131
- }
132
-
133
- // Generate revoke grant option changes
134
- if (result.revokeGrantOption.length > 0) {
135
- const revokeGrantGroups = new Map<
136
- string,
137
- { columns?: string[]; privileges: Set<string> }
138
- >();
139
- for (const r of result.revokeGrantOption) {
140
- // For revoke grant option, we need to find the columns from the effective defaults
141
- const originalPriv = effectiveDefaults.find(
142
- (p) => p.grantee === grantee && p.privilege === r,
143
- );
144
- const key = originalPriv?.columns
145
- ? originalPriv.columns.sort().join(",")
146
- : "";
147
- if (!revokeGrantGroups.has(key)) {
148
- revokeGrantGroups.set(key, {
149
- columns: originalPriv?.columns
150
- ? [...originalPriv.columns]
151
- : undefined,
152
- privileges: new Set(),
153
- });
154
- }
155
- const group = revokeGrantGroups.get(key);
156
- if (!group) continue;
157
- group.privileges.add(r);
158
- }
159
- for (const [, group] of revokeGrantGroups) {
160
- const privilegeNames = Array.from(group.privileges);
161
- changes.push(
162
- new RevokeGrantOptionViewPrivileges({
163
- view: v,
164
- grantee,
165
- privilegeNames,
166
- columns: group.columns,
167
- version: ctx.version,
168
- }),
169
- );
170
- }
171
- }
172
- }
84
+ changes.push(
85
+ ...(emitColumnPrivilegeChanges(
86
+ privilegeResults,
87
+ v,
88
+ v,
89
+ "view",
90
+ {
91
+ Grant: GrantViewPrivileges,
92
+ Revoke: RevokeViewPrivileges,
93
+ RevokeGrantOption: RevokeGrantOptionViewPrivileges,
94
+ },
95
+ effectiveDefaults,
96
+ ctx.version,
97
+ ) as ViewChange[]),
98
+ );
173
99
  }
174
100
 
175
101
  for (const viewId of dropped) {
@@ -265,99 +191,23 @@ export function diffViews(
265
191
  mainView.privileges,
266
192
  branchView.privileges,
267
193
  branchView.owner,
268
- ctx.mainRoles,
269
194
  );
270
195
 
271
- for (const [grantee, result] of privilegeResults) {
272
- // Generate grant changes
273
- if (result.grants.length > 0) {
274
- const grantGroups = groupPrivilegesByColumns(result.grants);
275
- for (const [, group] of grantGroups) {
276
- for (const [grantable, privSet] of group.byGrant) {
277
- const privileges = Array.from(privSet).map((priv) => ({
278
- privilege: priv,
279
- grantable,
280
- }));
281
- changes.push(
282
- new GrantViewPrivileges({
283
- view: branchView,
284
- grantee,
285
- privileges,
286
- columns: group.columns,
287
- version: ctx.version,
288
- }),
289
- );
290
- }
291
- }
292
- }
293
-
294
- // Generate revoke changes
295
- if (result.revokes.length > 0) {
296
- const revokeGroups = groupPrivilegesByColumns(result.revokes);
297
- for (const [, group] of revokeGroups) {
298
- // Collapse all grantable groups into a single revoke (grantable: false)
299
- const allPrivileges = new Set<string>();
300
- for (const [, privSet] of group.byGrant) {
301
- for (const priv of privSet) {
302
- allPrivileges.add(priv);
303
- }
304
- }
305
- const privileges = Array.from(allPrivileges).map((priv) => ({
306
- privilege: priv,
307
- grantable: false,
308
- }));
309
- changes.push(
310
- new RevokeViewPrivileges({
311
- view: mainView,
312
- grantee,
313
- privileges,
314
- columns: group.columns,
315
- version: ctx.version,
316
- }),
317
- );
318
- }
319
- }
320
-
321
- // Generate revoke grant option changes
322
- if (result.revokeGrantOption.length > 0) {
323
- const revokeGrantGroups = new Map<
324
- string,
325
- { columns?: string[]; privileges: Set<string> }
326
- >();
327
- for (const r of result.revokeGrantOption) {
328
- // For revoke grant option, we need to find the columns from the original privilege
329
- const originalPriv = mainView.privileges.find(
330
- (p) => p.grantee === grantee && p.privilege === r,
331
- );
332
- const key = originalPriv?.columns
333
- ? originalPriv.columns.sort().join(",")
334
- : "";
335
- if (!revokeGrantGroups.has(key)) {
336
- revokeGrantGroups.set(key, {
337
- columns: originalPriv?.columns
338
- ? [...originalPriv.columns]
339
- : undefined,
340
- privileges: new Set(),
341
- });
342
- }
343
- const group = revokeGrantGroups.get(key);
344
- if (!group) continue;
345
- group.privileges.add(r);
346
- }
347
- for (const [, group] of revokeGrantGroups) {
348
- const privilegeNames = Array.from(group.privileges);
349
- changes.push(
350
- new RevokeGrantOptionViewPrivileges({
351
- view: mainView,
352
- grantee,
353
- privilegeNames,
354
- columns: group.columns,
355
- version: ctx.version,
356
- }),
357
- );
358
- }
359
- }
360
- }
196
+ changes.push(
197
+ ...(emitColumnPrivilegeChanges(
198
+ privilegeResults,
199
+ branchView,
200
+ mainView,
201
+ "view",
202
+ {
203
+ Grant: GrantViewPrivileges,
204
+ Revoke: RevokeViewPrivileges,
205
+ RevokeGrantOption: RevokeGrantOptionViewPrivileges,
206
+ },
207
+ mainView.privileges,
208
+ ctx.version,
209
+ ) as ViewChange[]),
210
+ );
361
211
  }
362
212
  }
363
213