@supabase/pg-delta 1.0.0-alpha.3 → 1.0.0-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (463) hide show
  1. package/README.md +22 -0
  2. package/dist/cli/bin/cli.js +0 -0
  3. package/dist/cli/commands/plan.js +21 -0
  4. package/dist/cli/utils.d.ts +2 -0
  5. package/dist/cli/utils.js +1 -1
  6. package/dist/core/objects/table/table.model.d.ts +4 -2
  7. package/dist/core/objects/table/table.model.js +3 -0
  8. package/dist/core/objects/trigger/changes/trigger.alter.js +23 -0
  9. package/dist/core/objects/trigger/changes/trigger.create.js +2 -1
  10. package/dist/core/objects/trigger/trigger.model.d.ts +1 -0
  11. package/dist/core/objects/trigger/trigger.model.js +3 -0
  12. package/dist/core/plan/apply.js +3 -3
  13. package/dist/core/plan/create.js +34 -15
  14. package/dist/core/plan/sql-format/constants.d.ts +2 -0
  15. package/dist/core/plan/sql-format/constants.js +11 -0
  16. package/dist/core/plan/sql-format/fixtures.d.ts +2 -0
  17. package/dist/core/plan/sql-format/fixtures.js +2449 -0
  18. package/dist/core/plan/sql-format/format-utils.d.ts +37 -0
  19. package/dist/core/plan/sql-format/format-utils.js +274 -0
  20. package/dist/core/plan/sql-format/formatters.d.ts +20 -0
  21. package/dist/core/plan/sql-format/formatters.js +737 -0
  22. package/dist/core/plan/sql-format/index.d.ts +2 -0
  23. package/dist/core/plan/sql-format/index.js +98 -0
  24. package/dist/core/plan/sql-format/keyword-case.d.ts +2 -0
  25. package/dist/core/plan/sql-format/keyword-case.js +868 -0
  26. package/dist/core/plan/sql-format/protect.d.ts +3 -0
  27. package/dist/core/plan/sql-format/protect.js +269 -0
  28. package/dist/core/plan/sql-format/sql-scanner.d.ts +59 -0
  29. package/dist/core/plan/sql-format/sql-scanner.js +202 -0
  30. package/dist/core/plan/sql-format/tokenizer.d.ts +22 -0
  31. package/dist/core/plan/sql-format/tokenizer.js +118 -0
  32. package/dist/core/plan/sql-format/types.d.ts +28 -0
  33. package/dist/core/plan/sql-format/types.js +1 -0
  34. package/dist/core/plan/sql-format/wrap.d.ts +2 -0
  35. package/dist/core/plan/sql-format/wrap.js +165 -0
  36. package/dist/core/plan/sql-format.d.ts +2 -0
  37. package/dist/core/plan/sql-format.js +1 -0
  38. package/dist/core/plan/statements.d.ts +2 -1
  39. package/dist/core/plan/statements.js +6 -2
  40. package/dist/core/postgres-config.d.ts +15 -0
  41. package/dist/core/postgres-config.js +30 -0
  42. package/dist/index.d.ts +2 -0
  43. package/dist/index.js +1 -0
  44. package/package.json +37 -22
  45. package/src/cli/app.ts +28 -0
  46. package/src/cli/bin/cli.ts +9 -0
  47. package/src/cli/commands/apply.ts +101 -0
  48. package/src/cli/commands/plan.ts +195 -0
  49. package/src/cli/commands/sync.ts +185 -0
  50. package/src/cli/formatters/index.ts +5 -0
  51. package/src/cli/formatters/tree/tree-builder.ts +380 -0
  52. package/src/cli/formatters/tree/tree-renderer.ts +372 -0
  53. package/src/cli/formatters/tree/tree.ts +237 -0
  54. package/src/cli/utils/integrations.ts +42 -0
  55. package/src/cli/utils.ts +231 -0
  56. package/src/core/catalog.diff.ts +246 -0
  57. package/src/core/catalog.model.ts +384 -0
  58. package/src/core/change.types.ts +44 -0
  59. package/src/core/context.ts +26 -0
  60. package/src/core/depend.ts +1870 -0
  61. package/src/core/expand-replace-dependencies.ts +380 -0
  62. package/src/core/fingerprint.ts +204 -0
  63. package/src/core/integrations/filter/dsl.ts +204 -0
  64. package/src/core/integrations/filter/extractors.ts +145 -0
  65. package/src/core/integrations/filter/filter.types.ts +3 -0
  66. package/src/core/integrations/integration-dsl.ts +24 -0
  67. package/src/core/integrations/integration.types.ts +7 -0
  68. package/src/core/integrations/serialize/dsl.ts +77 -0
  69. package/src/core/integrations/serialize/serialize.types.ts +3 -0
  70. package/src/core/integrations/supabase.ts +121 -0
  71. package/src/core/objects/aggregate/aggregate.diff.test.ts +215 -0
  72. package/src/core/objects/aggregate/aggregate.diff.ts +278 -0
  73. package/src/core/objects/aggregate/aggregate.model.ts +317 -0
  74. package/src/core/objects/aggregate/changes/aggregate.alter.test.ts +64 -0
  75. package/src/core/objects/aggregate/changes/aggregate.alter.ts +32 -0
  76. package/src/core/objects/aggregate/changes/aggregate.base.ts +20 -0
  77. package/src/core/objects/aggregate/changes/aggregate.comment.test.ts +86 -0
  78. package/src/core/objects/aggregate/changes/aggregate.comment.ts +62 -0
  79. package/src/core/objects/aggregate/changes/aggregate.create.test.ts +101 -0
  80. package/src/core/objects/aggregate/changes/aggregate.create.ts +329 -0
  81. package/src/core/objects/aggregate/changes/aggregate.drop.test.ts +78 -0
  82. package/src/core/objects/aggregate/changes/aggregate.drop.ts +32 -0
  83. package/src/core/objects/aggregate/changes/aggregate.privilege.test.ts +130 -0
  84. package/src/core/objects/aggregate/changes/aggregate.privilege.ts +146 -0
  85. package/src/core/objects/aggregate/changes/aggregate.types.ts +12 -0
  86. package/src/core/objects/base.change.ts +62 -0
  87. package/src/core/objects/base.default-privileges.ts +204 -0
  88. package/src/core/objects/base.diff.ts +20 -0
  89. package/src/core/objects/base.model.ts +82 -0
  90. package/src/core/objects/base.privilege-diff.ts +299 -0
  91. package/src/core/objects/base.privilege.ts +184 -0
  92. package/src/core/objects/collation/changes/collation.alter.test.ts +63 -0
  93. package/src/core/objects/collation/changes/collation.alter.ts +79 -0
  94. package/src/core/objects/collation/changes/collation.base.ts +20 -0
  95. package/src/core/objects/collation/changes/collation.comment.ts +68 -0
  96. package/src/core/objects/collation/changes/collation.create.test.ts +51 -0
  97. package/src/core/objects/collation/changes/collation.create.ts +106 -0
  98. package/src/core/objects/collation/changes/collation.drop.test.ts +28 -0
  99. package/src/core/objects/collation/changes/collation.drop.ts +37 -0
  100. package/src/core/objects/collation/changes/collation.types.ts +10 -0
  101. package/src/core/objects/collation/collation.diff.test.ts +100 -0
  102. package/src/core/objects/collation/collation.diff.ts +126 -0
  103. package/src/core/objects/collation/collation.model.ts +224 -0
  104. package/src/core/objects/domain/changes/domain.alter.test.ts +316 -0
  105. package/src/core/objects/domain/changes/domain.alter.ts +286 -0
  106. package/src/core/objects/domain/changes/domain.base.ts +20 -0
  107. package/src/core/objects/domain/changes/domain.comment.ts +59 -0
  108. package/src/core/objects/domain/changes/domain.create.test.ts +65 -0
  109. package/src/core/objects/domain/changes/domain.create.ts +118 -0
  110. package/src/core/objects/domain/changes/domain.drop.test.ts +30 -0
  111. package/src/core/objects/domain/changes/domain.drop.ts +34 -0
  112. package/src/core/objects/domain/changes/domain.privilege.ts +171 -0
  113. package/src/core/objects/domain/changes/domain.types.ts +12 -0
  114. package/src/core/objects/domain/domain.diff.test.ts +284 -0
  115. package/src/core/objects/domain/domain.diff.ts +358 -0
  116. package/src/core/objects/domain/domain.model.ts +190 -0
  117. package/src/core/objects/event-trigger/changes/event-trigger.alter.test.ts +50 -0
  118. package/src/core/objects/event-trigger/changes/event-trigger.alter.ts +82 -0
  119. package/src/core/objects/event-trigger/changes/event-trigger.base.ts +20 -0
  120. package/src/core/objects/event-trigger/changes/event-trigger.comment.ts +66 -0
  121. package/src/core/objects/event-trigger/changes/event-trigger.create.test.ts +24 -0
  122. package/src/core/objects/event-trigger/changes/event-trigger.create.ts +72 -0
  123. package/src/core/objects/event-trigger/changes/event-trigger.drop.test.ts +22 -0
  124. package/src/core/objects/event-trigger/changes/event-trigger.drop.ts +34 -0
  125. package/src/core/objects/event-trigger/changes/event-trigger.types.ts +10 -0
  126. package/src/core/objects/event-trigger/event-trigger.diff.test.ts +126 -0
  127. package/src/core/objects/event-trigger/event-trigger.diff.ts +126 -0
  128. package/src/core/objects/event-trigger/event-trigger.model.ts +106 -0
  129. package/src/core/objects/extension/changes/extension.alter.test.ts +58 -0
  130. package/src/core/objects/extension/changes/extension.alter.ts +78 -0
  131. package/src/core/objects/extension/changes/extension.base.ts +20 -0
  132. package/src/core/objects/extension/changes/extension.comment.ts +64 -0
  133. package/src/core/objects/extension/changes/extension.create.test.ts +25 -0
  134. package/src/core/objects/extension/changes/extension.create.ts +63 -0
  135. package/src/core/objects/extension/changes/extension.drop.test.ts +23 -0
  136. package/src/core/objects/extension/changes/extension.drop.ts +34 -0
  137. package/src/core/objects/extension/changes/extension.types.ts +10 -0
  138. package/src/core/objects/extension/extension.diff.test.ts +42 -0
  139. package/src/core/objects/extension/extension.diff.ts +90 -0
  140. package/src/core/objects/extension/extension.model.ts +280 -0
  141. package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.alter.test.ts +125 -0
  142. package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.alter.ts +101 -0
  143. package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.base.ts +20 -0
  144. package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.comment.ts +72 -0
  145. package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.create.test.ts +125 -0
  146. package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.create.ts +95 -0
  147. package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.drop.test.ts +23 -0
  148. package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.drop.ts +36 -0
  149. package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.privilege.ts +172 -0
  150. package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.types.ts +12 -0
  151. package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/foreign-data-wrapper.diff.test.ts +179 -0
  152. package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/foreign-data-wrapper.diff.ts +341 -0
  153. package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/foreign-data-wrapper.model.ts +149 -0
  154. package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper.types.ts +10 -0
  155. package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.alter.test.ts +309 -0
  156. package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.alter.ts +341 -0
  157. package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.base.ts +20 -0
  158. package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.comment.ts +72 -0
  159. package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.create.test.ts +201 -0
  160. package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.create.ts +81 -0
  161. package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.drop.test.ts +43 -0
  162. package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.drop.ts +37 -0
  163. package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.privilege.ts +181 -0
  164. package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.types.ts +12 -0
  165. package/src/core/objects/foreign-data-wrapper/foreign-table/foreign-table.diff.test.ts +813 -0
  166. package/src/core/objects/foreign-data-wrapper/foreign-table/foreign-table.diff.ts +406 -0
  167. package/src/core/objects/foreign-data-wrapper/foreign-table/foreign-table.model.ts +242 -0
  168. package/src/core/objects/foreign-data-wrapper/server/changes/server.alter.test.ts +168 -0
  169. package/src/core/objects/foreign-data-wrapper/server/changes/server.alter.ts +126 -0
  170. package/src/core/objects/foreign-data-wrapper/server/changes/server.base.ts +20 -0
  171. package/src/core/objects/foreign-data-wrapper/server/changes/server.comment.ts +60 -0
  172. package/src/core/objects/foreign-data-wrapper/server/changes/server.create.test.ts +131 -0
  173. package/src/core/objects/foreign-data-wrapper/server/changes/server.create.ts +81 -0
  174. package/src/core/objects/foreign-data-wrapper/server/changes/server.drop.test.ts +24 -0
  175. package/src/core/objects/foreign-data-wrapper/server/changes/server.drop.ts +34 -0
  176. package/src/core/objects/foreign-data-wrapper/server/changes/server.privilege.ts +164 -0
  177. package/src/core/objects/foreign-data-wrapper/server/changes/server.types.ts +12 -0
  178. package/src/core/objects/foreign-data-wrapper/server/server.diff.test.ts +167 -0
  179. package/src/core/objects/foreign-data-wrapper/server/server.diff.ts +317 -0
  180. package/src/core/objects/foreign-data-wrapper/server/server.model.ts +133 -0
  181. package/src/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.alter.test.ts +82 -0
  182. package/src/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.alter.ts +69 -0
  183. package/src/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.base.ts +20 -0
  184. package/src/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.create.test.ts +85 -0
  185. package/src/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.create.ts +66 -0
  186. package/src/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.drop.test.ts +53 -0
  187. package/src/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.drop.ts +40 -0
  188. package/src/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.types.ts +8 -0
  189. package/src/core/objects/foreign-data-wrapper/user-mapping/user-mapping.diff.test.ts +77 -0
  190. package/src/core/objects/foreign-data-wrapper/user-mapping/user-mapping.diff.ts +107 -0
  191. package/src/core/objects/foreign-data-wrapper/user-mapping/user-mapping.model.ts +96 -0
  192. package/src/core/objects/index/changes/index.alter.test.ts +200 -0
  193. package/src/core/objects/index/changes/index.alter.ts +144 -0
  194. package/src/core/objects/index/changes/index.base.ts +20 -0
  195. package/src/core/objects/index/changes/index.comment.ts +63 -0
  196. package/src/core/objects/index/changes/index.create.test.ts +66 -0
  197. package/src/core/objects/index/changes/index.create.ts +68 -0
  198. package/src/core/objects/index/changes/index.drop.test.ts +44 -0
  199. package/src/core/objects/index/changes/index.drop.ts +34 -0
  200. package/src/core/objects/index/changes/index.types.ts +6 -0
  201. package/src/core/objects/index/changes/utils.ts +16 -0
  202. package/src/core/objects/index/index.diff.test.ts +153 -0
  203. package/src/core/objects/index/index.diff.ts +243 -0
  204. package/src/core/objects/index/index.model.ts +370 -0
  205. package/src/core/objects/language/changes/language.alter.test.ts +33 -0
  206. package/src/core/objects/language/changes/language.alter.ts +53 -0
  207. package/src/core/objects/language/changes/language.base.ts +20 -0
  208. package/src/core/objects/language/changes/language.comment.ts +58 -0
  209. package/src/core/objects/language/changes/language.create.test.ts +27 -0
  210. package/src/core/objects/language/changes/language.create.ts +104 -0
  211. package/src/core/objects/language/changes/language.drop.test.ts +25 -0
  212. package/src/core/objects/language/changes/language.drop.ts +39 -0
  213. package/src/core/objects/language/changes/language.privilege.ts +172 -0
  214. package/src/core/objects/language/changes/language.types.ts +12 -0
  215. package/src/core/objects/language/language.diff.test.ts +53 -0
  216. package/src/core/objects/language/language.diff.ts +176 -0
  217. package/src/core/objects/language/language.model.ts +150 -0
  218. package/src/core/objects/materialized-view/changes/materialized-view.alter.test.ts +123 -0
  219. package/src/core/objects/materialized-view/changes/materialized-view.alter.ts +113 -0
  220. package/src/core/objects/materialized-view/changes/materialized-view.base.ts +20 -0
  221. package/src/core/objects/materialized-view/changes/materialized-view.comment.ts +176 -0
  222. package/src/core/objects/materialized-view/changes/materialized-view.create.test.ts +64 -0
  223. package/src/core/objects/materialized-view/changes/materialized-view.create.ts +93 -0
  224. package/src/core/objects/materialized-view/changes/materialized-view.drop.test.ts +34 -0
  225. package/src/core/objects/materialized-view/changes/materialized-view.drop.ts +60 -0
  226. package/src/core/objects/materialized-view/changes/materialized-view.privilege.ts +212 -0
  227. package/src/core/objects/materialized-view/changes/materialized-view.types.ts +12 -0
  228. package/src/core/objects/materialized-view/materialized-view.diff.test.ts +102 -0
  229. package/src/core/objects/materialized-view/materialized-view.diff.ts +451 -0
  230. package/src/core/objects/materialized-view/materialized-view.model.ts +258 -0
  231. package/src/core/objects/procedure/changes/procedure.alter.test.ts +1005 -0
  232. package/src/core/objects/procedure/changes/procedure.alter.ts +287 -0
  233. package/src/core/objects/procedure/changes/procedure.base.ts +20 -0
  234. package/src/core/objects/procedure/changes/procedure.comment.ts +70 -0
  235. package/src/core/objects/procedure/changes/procedure.create.test.ts +48 -0
  236. package/src/core/objects/procedure/changes/procedure.create.ts +92 -0
  237. package/src/core/objects/procedure/changes/procedure.drop.test.ts +85 -0
  238. package/src/core/objects/procedure/changes/procedure.drop.ts +49 -0
  239. package/src/core/objects/procedure/changes/procedure.privilege.ts +188 -0
  240. package/src/core/objects/procedure/changes/procedure.types.ts +12 -0
  241. package/src/core/objects/procedure/procedure.diff.test.ts +161 -0
  242. package/src/core/objects/procedure/procedure.diff.ts +404 -0
  243. package/src/core/objects/procedure/procedure.model.ts +264 -0
  244. package/src/core/objects/procedure/utils.ts +58 -0
  245. package/src/core/objects/publication/changes/publication.alter.test.ts +223 -0
  246. package/src/core/objects/publication/changes/publication.alter.ts +243 -0
  247. package/src/core/objects/publication/changes/publication.base.ts +20 -0
  248. package/src/core/objects/publication/changes/publication.comment.test.ts +70 -0
  249. package/src/core/objects/publication/changes/publication.comment.ts +64 -0
  250. package/src/core/objects/publication/changes/publication.create.test.ts +87 -0
  251. package/src/core/objects/publication/changes/publication.create.ts +82 -0
  252. package/src/core/objects/publication/changes/publication.drop.test.ts +46 -0
  253. package/src/core/objects/publication/changes/publication.drop.ts +29 -0
  254. package/src/core/objects/publication/changes/publication.types.ts +26 -0
  255. package/src/core/objects/publication/publication.diff.test.ts +292 -0
  256. package/src/core/objects/publication/publication.diff.ts +253 -0
  257. package/src/core/objects/publication/publication.model.ts +206 -0
  258. package/src/core/objects/publication/utils.ts +55 -0
  259. package/src/core/objects/rls-policy/changes/rls-policy.alter.test.ts +250 -0
  260. package/src/core/objects/rls-policy/changes/rls-policy.alter.ts +128 -0
  261. package/src/core/objects/rls-policy/changes/rls-policy.base.ts +20 -0
  262. package/src/core/objects/rls-policy/changes/rls-policy.comment.ts +69 -0
  263. package/src/core/objects/rls-policy/changes/rls-policy.create.test.ts +74 -0
  264. package/src/core/objects/rls-policy/changes/rls-policy.create.ts +100 -0
  265. package/src/core/objects/rls-policy/changes/rls-policy.drop.test.ts +28 -0
  266. package/src/core/objects/rls-policy/changes/rls-policy.drop.ts +39 -0
  267. package/src/core/objects/rls-policy/changes/rls-policy.types.ts +10 -0
  268. package/src/core/objects/rls-policy/rls-policy.diff.test.ts +79 -0
  269. package/src/core/objects/rls-policy/rls-policy.diff.ts +121 -0
  270. package/src/core/objects/rls-policy/rls-policy.model.ts +140 -0
  271. package/src/core/objects/role/changes/role.alter.test.ts +346 -0
  272. package/src/core/objects/role/changes/role.alter.ts +110 -0
  273. package/src/core/objects/role/changes/role.base.ts +24 -0
  274. package/src/core/objects/role/changes/role.comment.ts +55 -0
  275. package/src/core/objects/role/changes/role.create.test.ts +52 -0
  276. package/src/core/objects/role/changes/role.create.ts +102 -0
  277. package/src/core/objects/role/changes/role.drop.test.ts +29 -0
  278. package/src/core/objects/role/changes/role.drop.ts +34 -0
  279. package/src/core/objects/role/changes/role.privilege.ts +376 -0
  280. package/src/core/objects/role/changes/role.types.ts +12 -0
  281. package/src/core/objects/role/role.diff.test.ts +44 -0
  282. package/src/core/objects/role/role.diff.ts +479 -0
  283. package/src/core/objects/role/role.model.ts +344 -0
  284. package/src/core/objects/rule/changes/rule.alter.test.ts +78 -0
  285. package/src/core/objects/rule/changes/rule.alter.ts +72 -0
  286. package/src/core/objects/rule/changes/rule.base.ts +20 -0
  287. package/src/core/objects/rule/changes/rule.comment.test.ts +55 -0
  288. package/src/core/objects/rule/changes/rule.comment.ts +62 -0
  289. package/src/core/objects/rule/changes/rule.create.test.ts +59 -0
  290. package/src/core/objects/rule/changes/rule.create.ts +42 -0
  291. package/src/core/objects/rule/changes/rule.drop.test.ts +38 -0
  292. package/src/core/objects/rule/changes/rule.drop.ts +29 -0
  293. package/src/core/objects/rule/changes/rule.types.ts +12 -0
  294. package/src/core/objects/rule/rule.diff.test.ts +132 -0
  295. package/src/core/objects/rule/rule.diff.ts +79 -0
  296. package/src/core/objects/rule/rule.model.ts +173 -0
  297. package/src/core/objects/schema/changes/schema.alter.test.ts +28 -0
  298. package/src/core/objects/schema/changes/schema.alter.ts +45 -0
  299. package/src/core/objects/schema/changes/schema.base.ts +20 -0
  300. package/src/core/objects/schema/changes/schema.comment.ts +56 -0
  301. package/src/core/objects/schema/changes/schema.create.test.ts +22 -0
  302. package/src/core/objects/schema/changes/schema.create.ts +47 -0
  303. package/src/core/objects/schema/changes/schema.drop.test.ts +20 -0
  304. package/src/core/objects/schema/changes/schema.drop.ts +34 -0
  305. package/src/core/objects/schema/changes/schema.privilege.ts +175 -0
  306. package/src/core/objects/schema/changes/schema.types.ts +12 -0
  307. package/src/core/objects/schema/schema.diff.test.ts +42 -0
  308. package/src/core/objects/schema/schema.diff.ts +209 -0
  309. package/src/core/objects/schema/schema.model.ts +107 -0
  310. package/src/core/objects/sequence/changes/sequence.alter.test.ts +151 -0
  311. package/src/core/objects/sequence/changes/sequence.alter.ts +115 -0
  312. package/src/core/objects/sequence/changes/sequence.base.ts +20 -0
  313. package/src/core/objects/sequence/changes/sequence.comment.ts +60 -0
  314. package/src/core/objects/sequence/changes/sequence.create.test.ts +84 -0
  315. package/src/core/objects/sequence/changes/sequence.create.ts +111 -0
  316. package/src/core/objects/sequence/changes/sequence.drop.test.ts +32 -0
  317. package/src/core/objects/sequence/changes/sequence.drop.ts +37 -0
  318. package/src/core/objects/sequence/changes/sequence.privilege.ts +179 -0
  319. package/src/core/objects/sequence/changes/sequence.types.ts +12 -0
  320. package/src/core/objects/sequence/sequence.diff.test.ts +141 -0
  321. package/src/core/objects/sequence/sequence.diff.ts +359 -0
  322. package/src/core/objects/sequence/sequence.model.ts +185 -0
  323. package/src/core/objects/subscription/changes/subscription.alter.test.ts +124 -0
  324. package/src/core/objects/subscription/changes/subscription.alter.ts +110 -0
  325. package/src/core/objects/subscription/changes/subscription.base.ts +20 -0
  326. package/src/core/objects/subscription/changes/subscription.comment.test.ts +67 -0
  327. package/src/core/objects/subscription/changes/subscription.comment.ts +64 -0
  328. package/src/core/objects/subscription/changes/subscription.create.test.ts +77 -0
  329. package/src/core/objects/subscription/changes/subscription.create.ts +69 -0
  330. package/src/core/objects/subscription/changes/subscription.drop.test.ts +46 -0
  331. package/src/core/objects/subscription/changes/subscription.drop.ts +20 -0
  332. package/src/core/objects/subscription/changes/subscription.types.ts +22 -0
  333. package/src/core/objects/subscription/subscription.diff.test.ts +232 -0
  334. package/src/core/objects/subscription/subscription.diff.ts +241 -0
  335. package/src/core/objects/subscription/subscription.model.ts +190 -0
  336. package/src/core/objects/subscription/utils.ts +156 -0
  337. package/src/core/objects/table/changes/table.alter.test.ts +823 -0
  338. package/src/core/objects/table/changes/table.alter.ts +806 -0
  339. package/src/core/objects/table/changes/table.base.ts +20 -0
  340. package/src/core/objects/table/changes/table.comment.ts +266 -0
  341. package/src/core/objects/table/changes/table.create.test.ts +150 -0
  342. package/src/core/objects/table/changes/table.create.ts +188 -0
  343. package/src/core/objects/table/changes/table.drop.test.ts +34 -0
  344. package/src/core/objects/table/changes/table.drop.ts +45 -0
  345. package/src/core/objects/table/changes/table.privilege.ts +200 -0
  346. package/src/core/objects/table/changes/table.types.ts +12 -0
  347. package/src/core/objects/table/table.diff.test.ts +711 -0
  348. package/src/core/objects/table/table.diff.ts +953 -0
  349. package/src/core/objects/table/table.model.ts +460 -0
  350. package/src/core/objects/trigger/changes/trigger.alter.test.ts +46 -0
  351. package/src/core/objects/trigger/changes/trigger.alter.ts +76 -0
  352. package/src/core/objects/trigger/changes/trigger.base.ts +20 -0
  353. package/src/core/objects/trigger/changes/trigger.comment.ts +64 -0
  354. package/src/core/objects/trigger/changes/trigger.create.test.ts +43 -0
  355. package/src/core/objects/trigger/changes/trigger.create.ts +85 -0
  356. package/src/core/objects/trigger/changes/trigger.drop.test.ts +43 -0
  357. package/src/core/objects/trigger/changes/trigger.drop.ts +39 -0
  358. package/src/core/objects/trigger/changes/trigger.types.ts +10 -0
  359. package/src/core/objects/trigger/trigger.diff.test.ts +83 -0
  360. package/src/core/objects/trigger/trigger.diff.ts +116 -0
  361. package/src/core/objects/trigger/trigger.model.ts +252 -0
  362. package/src/core/objects/type/composite-type/changes/composite-type.alter.test.ts +202 -0
  363. package/src/core/objects/type/composite-type/changes/composite-type.alter.ts +174 -0
  364. package/src/core/objects/type/composite-type/changes/composite-type.base.ts +20 -0
  365. package/src/core/objects/type/composite-type/changes/composite-type.comment.ts +145 -0
  366. package/src/core/objects/type/composite-type/changes/composite-type.create.test.ts +101 -0
  367. package/src/core/objects/type/composite-type/changes/composite-type.create.ts +95 -0
  368. package/src/core/objects/type/composite-type/changes/composite-type.drop.test.ts +33 -0
  369. package/src/core/objects/type/composite-type/changes/composite-type.drop.ts +37 -0
  370. package/src/core/objects/type/composite-type/changes/composite-type.privilege.ts +175 -0
  371. package/src/core/objects/type/composite-type/changes/composite-type.types.ts +12 -0
  372. package/src/core/objects/type/composite-type/composite-type.diff.test.ts +191 -0
  373. package/src/core/objects/type/composite-type/composite-type.diff.ts +372 -0
  374. package/src/core/objects/type/composite-type/composite-type.model.ts +252 -0
  375. package/src/core/objects/type/enum/changes/enum.alter.test.ts +104 -0
  376. package/src/core/objects/type/enum/changes/enum.alter.ts +91 -0
  377. package/src/core/objects/type/enum/changes/enum.base.ts +20 -0
  378. package/src/core/objects/type/enum/changes/enum.comment.ts +64 -0
  379. package/src/core/objects/type/enum/changes/enum.create.test.ts +28 -0
  380. package/src/core/objects/type/enum/changes/enum.create.ts +56 -0
  381. package/src/core/objects/type/enum/changes/enum.drop.test.ts +25 -0
  382. package/src/core/objects/type/enum/changes/enum.drop.ts +34 -0
  383. package/src/core/objects/type/enum/changes/enum.privilege.ts +175 -0
  384. package/src/core/objects/type/enum/changes/enum.types.ts +12 -0
  385. package/src/core/objects/type/enum/enum.diff.test.ts +191 -0
  386. package/src/core/objects/type/enum/enum.diff.ts +396 -0
  387. package/src/core/objects/type/enum/enum.model.ts +194 -0
  388. package/src/core/objects/type/range/changes/range.alter.test.ts +27 -0
  389. package/src/core/objects/type/range/changes/range.alter.ts +51 -0
  390. package/src/core/objects/type/range/changes/range.base.ts +20 -0
  391. package/src/core/objects/type/range/changes/range.comment.ts +64 -0
  392. package/src/core/objects/type/range/changes/range.create.test.ts +51 -0
  393. package/src/core/objects/type/range/changes/range.create.ts +151 -0
  394. package/src/core/objects/type/range/changes/range.drop.test.ts +26 -0
  395. package/src/core/objects/type/range/changes/range.drop.ts +34 -0
  396. package/src/core/objects/type/range/changes/range.privilege.ts +175 -0
  397. package/src/core/objects/type/range/changes/range.types.ts +12 -0
  398. package/src/core/objects/type/range/range.diff.test.ts +70 -0
  399. package/src/core/objects/type/range/range.diff.ts +259 -0
  400. package/src/core/objects/type/range/range.model.ts +187 -0
  401. package/src/core/objects/type/type.types.ts +5 -0
  402. package/src/core/objects/utils.ts +171 -0
  403. package/src/core/objects/view/changes/view.alter.test.ts +110 -0
  404. package/src/core/objects/view/changes/view.alter.ts +112 -0
  405. package/src/core/objects/view/changes/view.base.ts +20 -0
  406. package/src/core/objects/view/changes/view.comment.ts +59 -0
  407. package/src/core/objects/view/changes/view.create.test.ts +65 -0
  408. package/src/core/objects/view/changes/view.create.ts +73 -0
  409. package/src/core/objects/view/changes/view.drop.test.ts +34 -0
  410. package/src/core/objects/view/changes/view.drop.ts +40 -0
  411. package/src/core/objects/view/changes/view.privilege.ts +200 -0
  412. package/src/core/objects/view/changes/view.types.ts +12 -0
  413. package/src/core/objects/view/view.diff.test.ts +91 -0
  414. package/src/core/objects/view/view.diff.ts +365 -0
  415. package/src/core/objects/view/view.model.ts +276 -0
  416. package/src/core/plan/apply.ts +190 -0
  417. package/src/core/plan/create.ts +432 -0
  418. package/src/core/plan/hierarchy.ts +574 -0
  419. package/src/core/plan/index.ts +29 -0
  420. package/src/core/plan/io.ts +20 -0
  421. package/src/core/plan/risk.ts +48 -0
  422. package/src/core/plan/serialize.ts +195 -0
  423. package/src/core/plan/sql-format/constants.ts +13 -0
  424. package/src/core/plan/sql-format/fixtures.ts +2806 -0
  425. package/src/core/plan/sql-format/format-comment-literals.test.ts +96 -0
  426. package/src/core/plan/sql-format/format-functions.test.ts +127 -0
  427. package/src/core/plan/sql-format/format-lowercase-coverage.test.ts +67 -0
  428. package/src/core/plan/sql-format/format-off.test.ts +809 -0
  429. package/src/core/plan/sql-format/format-pretty-lower-leading.test.ts +1056 -0
  430. package/src/core/plan/sql-format/format-pretty-narrow.test.ts +1283 -0
  431. package/src/core/plan/sql-format/format-pretty-preserve.test.ts +1052 -0
  432. package/src/core/plan/sql-format/format-pretty-upper.test.ts +1045 -0
  433. package/src/core/plan/sql-format/format-stress.test.ts +616 -0
  434. package/src/core/plan/sql-format/format-utils.test.ts +91 -0
  435. package/src/core/plan/sql-format/format-utils.ts +391 -0
  436. package/src/core/plan/sql-format/formatters.ts +921 -0
  437. package/src/core/plan/sql-format/index.ts +149 -0
  438. package/src/core/plan/sql-format/keyword-case.test.ts +118 -0
  439. package/src/core/plan/sql-format/keyword-case.ts +1085 -0
  440. package/src/core/plan/sql-format/protect.test.ts +127 -0
  441. package/src/core/plan/sql-format/protect.ts +337 -0
  442. package/src/core/plan/sql-format/sql-scanner.test.ts +240 -0
  443. package/src/core/plan/sql-format/sql-scanner.ts +252 -0
  444. package/src/core/plan/sql-format/tokenizer.test.ts +68 -0
  445. package/src/core/plan/sql-format/tokenizer.ts +152 -0
  446. package/src/core/plan/sql-format/types.ts +31 -0
  447. package/src/core/plan/sql-format/wrap.test.ts +119 -0
  448. package/src/core/plan/sql-format/wrap.ts +196 -0
  449. package/src/core/plan/sql-format.ts +2 -0
  450. package/src/core/plan/statements.ts +22 -0
  451. package/src/core/plan/types.ts +165 -0
  452. package/src/core/postgres-config.ts +169 -0
  453. package/src/core/sort/custom-constraints.ts +161 -0
  454. package/src/core/sort/debug-visualization.ts +239 -0
  455. package/src/core/sort/dependency-filter.ts +224 -0
  456. package/src/core/sort/graph-builder.ts +223 -0
  457. package/src/core/sort/graph-utils.ts +51 -0
  458. package/src/core/sort/logical-sort.ts +590 -0
  459. package/src/core/sort/sort-changes.ts +234 -0
  460. package/src/core/sort/topological-sort.ts +184 -0
  461. package/src/core/sort/types.ts +112 -0
  462. package/src/core/sort/utils.ts +69 -0
  463. package/src/index.ts +14 -0
@@ -0,0 +1,200 @@
1
+ import {
2
+ formatObjectPrivilegeList,
3
+ getObjectKindPrefix,
4
+ } from "../../base.privilege.ts";
5
+ import { stableId } from "../../utils.ts";
6
+ import type { View } from "../view.model.ts";
7
+ import { AlterViewChange } from "./view.base.ts";
8
+
9
+ export type ViewPrivilege =
10
+ | GrantViewPrivileges
11
+ | RevokeViewPrivileges
12
+ | RevokeGrantOptionViewPrivileges;
13
+
14
+ /**
15
+ * Grant privileges on a view.
16
+ *
17
+ * @see https://www.postgresql.org/docs/17/sql-grant.html
18
+ *
19
+ * Synopsis
20
+ * ```sql
21
+ * GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
22
+ * [, ...] | ALL [ PRIVILEGES ] }
23
+ * ON { [ TABLE ] view_name [, ...]
24
+ * | ALL TABLES IN SCHEMA schema_name [, ...] }
25
+ * TO role_specification [, ...] [ WITH GRANT OPTION ]
26
+ * [ GRANTED BY role_specification ]
27
+ * ```
28
+ */
29
+ export class GrantViewPrivileges extends AlterViewChange {
30
+ public readonly view: View;
31
+ public readonly grantee: string;
32
+ public readonly privileges: { privilege: string; grantable: boolean }[];
33
+ public readonly columns?: string[];
34
+ public readonly version: number | undefined;
35
+ public readonly scope = "privilege" as const;
36
+
37
+ constructor(props: {
38
+ view: View;
39
+ grantee: string;
40
+ privileges: { privilege: string; grantable: boolean }[];
41
+ columns?: string[];
42
+ version?: number;
43
+ }) {
44
+ super();
45
+ this.view = props.view;
46
+ this.grantee = props.grantee;
47
+ this.privileges = props.privileges;
48
+ this.columns = props.columns;
49
+ this.version = props.version;
50
+ }
51
+
52
+ get creates() {
53
+ return [stableId.acl(this.view.stableId, this.grantee)];
54
+ }
55
+
56
+ get requires() {
57
+ return [this.view.stableId, stableId.role(this.grantee)];
58
+ }
59
+
60
+ serialize(): string {
61
+ const hasGrantable = this.privileges.some((p) => p.grantable);
62
+ const hasBase = this.privileges.some((p) => !p.grantable);
63
+ if (hasGrantable && hasBase) {
64
+ throw new Error(
65
+ "GrantViewPrivileges expects privileges with uniform grantable flag",
66
+ );
67
+ }
68
+ const withGrant = hasGrantable ? " WITH GRANT OPTION" : "";
69
+ const kindPrefix = getObjectKindPrefix("VIEW");
70
+ const list = this.privileges.map((p) => p.privilege);
71
+ const privSql = formatObjectPrivilegeList("VIEW", list, this.version);
72
+ const viewName = `${this.view.schema}.${this.view.name}`;
73
+ const columnSpec =
74
+ this.columns && this.columns.length > 0
75
+ ? ` (${this.columns.join(", ")})`
76
+ : "";
77
+ return `GRANT ${privSql}${columnSpec} ${kindPrefix} ${viewName} TO ${this.grantee}${withGrant}`;
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Revoke privileges on a view.
83
+ *
84
+ * @see https://www.postgresql.org/docs/17/sql-revoke.html
85
+ *
86
+ * Synopsis
87
+ * ```sql
88
+ * REVOKE [ GRANT OPTION FOR ]
89
+ * { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
90
+ * [, ...] | ALL [ PRIVILEGES ] }
91
+ * ON { [ TABLE ] view_name [, ...]
92
+ * | ALL TABLES IN SCHEMA schema_name [, ...] }
93
+ * FROM role_specification [, ...]
94
+ * [ GRANTED BY role_specification ]
95
+ * [ CASCADE | RESTRICT ]
96
+ * ```
97
+ */
98
+ export class RevokeViewPrivileges extends AlterViewChange {
99
+ public readonly view: View;
100
+ public readonly grantee: string;
101
+ public readonly privileges: { privilege: string; grantable: boolean }[];
102
+ public readonly columns?: string[];
103
+ public readonly version: number | undefined;
104
+ public readonly scope = "privilege" as const;
105
+
106
+ constructor(props: {
107
+ view: View;
108
+ grantee: string;
109
+ privileges: { privilege: string; grantable: boolean }[];
110
+ columns?: string[];
111
+ version?: number;
112
+ }) {
113
+ super();
114
+ this.view = props.view;
115
+ this.grantee = props.grantee;
116
+ this.privileges = props.privileges;
117
+ this.columns = props.columns;
118
+ this.version = props.version;
119
+ }
120
+
121
+ get drops() {
122
+ // Return ACL ID for dependency tracking, even though this is an ALTER operation
123
+ // Phase assignment now uses operation type, so this won't affect phase placement
124
+ return [stableId.acl(this.view.stableId, this.grantee)];
125
+ }
126
+
127
+ get requires() {
128
+ return [
129
+ stableId.acl(this.view.stableId, this.grantee),
130
+ this.view.stableId,
131
+ stableId.role(this.grantee),
132
+ ];
133
+ }
134
+
135
+ serialize(): string {
136
+ const kindPrefix = getObjectKindPrefix("VIEW");
137
+ const list = this.privileges.map((p) => p.privilege);
138
+ const privSql = formatObjectPrivilegeList("VIEW", list, this.version);
139
+ const viewName = `${this.view.schema}.${this.view.name}`;
140
+ const columnSpec =
141
+ this.columns && this.columns.length > 0
142
+ ? ` (${this.columns.join(", ")})`
143
+ : "";
144
+ return `REVOKE ${privSql}${columnSpec} ${kindPrefix} ${viewName} FROM ${this.grantee}`;
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Revoke grant option for privileges on a view.
150
+ *
151
+ * This removes the ability to grant the privilege to others, but keeps the privilege itself.
152
+ *
153
+ * @see https://www.postgresql.org/docs/17/sql-revoke.html
154
+ */
155
+ export class RevokeGrantOptionViewPrivileges extends AlterViewChange {
156
+ public readonly view: View;
157
+ public readonly grantee: string;
158
+ public readonly privilegeNames: string[];
159
+ public readonly columns?: string[];
160
+ public readonly version: number | undefined;
161
+ public readonly scope = "privilege" as const;
162
+
163
+ constructor(props: {
164
+ view: View;
165
+ grantee: string;
166
+ privilegeNames: string[];
167
+ columns?: string[];
168
+ version?: number;
169
+ }) {
170
+ super();
171
+ this.view = props.view;
172
+ this.grantee = props.grantee;
173
+ this.privilegeNames = [...new Set(props.privilegeNames)].sort();
174
+ this.columns = props.columns;
175
+ this.version = props.version;
176
+ }
177
+
178
+ get requires() {
179
+ return [
180
+ stableId.acl(this.view.stableId, this.grantee),
181
+ this.view.stableId,
182
+ stableId.role(this.grantee),
183
+ ];
184
+ }
185
+
186
+ serialize(): string {
187
+ const kindPrefix = getObjectKindPrefix("VIEW");
188
+ const privSql = formatObjectPrivilegeList(
189
+ "VIEW",
190
+ this.privilegeNames,
191
+ this.version,
192
+ );
193
+ const viewName = `${this.view.schema}.${this.view.name}`;
194
+ const columnSpec =
195
+ this.columns && this.columns.length > 0
196
+ ? ` (${this.columns.join(", ")})`
197
+ : "";
198
+ return `REVOKE GRANT OPTION FOR ${privSql}${columnSpec} ${kindPrefix} ${viewName} FROM ${this.grantee}`;
199
+ }
200
+ }
@@ -0,0 +1,12 @@
1
+ import type { AlterView } from "./view.alter.ts";
2
+ import type { CommentView } from "./view.comment.ts";
3
+ import type { CreateView } from "./view.create.ts";
4
+ import type { DropView } from "./view.drop.ts";
5
+ import type { ViewPrivilege } from "./view.privilege.ts";
6
+
7
+ export type ViewChange =
8
+ | AlterView
9
+ | CommentView
10
+ | CreateView
11
+ | DropView
12
+ | ViewPrivilege;
@@ -0,0 +1,91 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { DefaultPrivilegeState } from "../base.default-privileges.ts";
3
+ import {
4
+ AlterViewChangeOwner,
5
+ AlterViewResetOptions,
6
+ AlterViewSetOptions,
7
+ } from "./changes/view.alter.ts";
8
+ import { CreateView } from "./changes/view.create.ts";
9
+ import { DropView } from "./changes/view.drop.ts";
10
+ import { diffViews } from "./view.diff.ts";
11
+ import { View, type ViewProps } from "./view.model.ts";
12
+
13
+ const base: ViewProps = {
14
+ schema: "public",
15
+ name: "v",
16
+ definition: "select 1",
17
+ row_security: false,
18
+ force_row_security: false,
19
+ has_indexes: false,
20
+ has_rules: false,
21
+ has_triggers: false,
22
+ has_subclasses: false,
23
+ is_populated: true,
24
+ replica_identity: "d",
25
+ is_partition: false,
26
+ options: null,
27
+ partition_bound: null,
28
+ owner: "o1",
29
+ comment: null,
30
+ columns: [],
31
+ privileges: [],
32
+ };
33
+
34
+ const testContext = {
35
+ version: 170000,
36
+ currentUser: "postgres",
37
+ defaultPrivilegeState: new DefaultPrivilegeState({}),
38
+ mainRoles: {},
39
+ };
40
+
41
+ describe.concurrent("view.diff", () => {
42
+ test("create and drop", () => {
43
+ const v = new View(base);
44
+ const created = diffViews(testContext, {}, { [v.stableId]: v });
45
+ expect(created[0]).toBeInstanceOf(CreateView);
46
+ const dropped = diffViews(testContext, { [v.stableId]: v }, {});
47
+ expect(dropped[0]).toBeInstanceOf(DropView);
48
+ });
49
+
50
+ test("alter owner", () => {
51
+ const main = new View(base);
52
+ const branch = new View({ ...base, owner: "o2" });
53
+ const changes = diffViews(
54
+ testContext,
55
+ { [main.stableId]: main },
56
+ { [branch.stableId]: branch },
57
+ );
58
+ expect(changes[0]).toBeInstanceOf(AlterViewChangeOwner);
59
+ });
60
+
61
+ test("alter: set and reset options", () => {
62
+ const main = new View({
63
+ ...base,
64
+ options: ["security_barrier=true", "check_option=local"],
65
+ });
66
+ const branch = new View({ ...base, options: ["security_barrier=false"] });
67
+ const changes = diffViews(
68
+ testContext,
69
+ { [main.stableId]: main },
70
+ { [branch.stableId]: branch },
71
+ );
72
+ expect(changes.some((c) => c instanceof AlterViewSetOptions)).toBe(true);
73
+ expect(changes.some((c) => c instanceof AlterViewResetOptions)).toBe(true);
74
+ });
75
+
76
+ test("create or replace when non-alterable property changes", () => {
77
+ const main = new View(base);
78
+ const branch = new View({
79
+ ...base,
80
+ definition: "select 2",
81
+ row_security: true,
82
+ });
83
+ const changes = diffViews(
84
+ testContext,
85
+ { [main.stableId]: main },
86
+ { [branch.stableId]: branch },
87
+ );
88
+ expect(changes).toHaveLength(1);
89
+ expect(changes[0]).toBeInstanceOf(CreateView);
90
+ });
91
+ });
@@ -0,0 +1,365 @@
1
+ import type { DefaultPrivilegeState } from "../base.default-privileges.ts";
2
+ import { diffObjects } from "../base.diff.ts";
3
+ import {
4
+ diffPrivileges,
5
+ groupPrivilegesByColumns,
6
+ } from "../base.privilege-diff.ts";
7
+ import type { Role } from "../role/role.model.ts";
8
+ import { deepEqual, hasNonAlterableChanges } from "../utils.ts";
9
+ import {
10
+ AlterViewChangeOwner,
11
+ AlterViewResetOptions,
12
+ AlterViewSetOptions,
13
+ } from "./changes/view.alter.ts";
14
+ import {
15
+ CreateCommentOnView,
16
+ DropCommentOnView,
17
+ } from "./changes/view.comment.ts";
18
+ import { CreateView } from "./changes/view.create.ts";
19
+ import { DropView } from "./changes/view.drop.ts";
20
+ import {
21
+ GrantViewPrivileges,
22
+ RevokeGrantOptionViewPrivileges,
23
+ RevokeViewPrivileges,
24
+ } from "./changes/view.privilege.ts";
25
+ import type { ViewChange } from "./changes/view.types.ts";
26
+ import type { View } from "./view.model.ts";
27
+
28
+ /**
29
+ * Diff two sets of views from main and branch catalogs.
30
+ *
31
+ * @param ctx - Context containing version, currentUser, and defaultPrivilegeState
32
+ * @param main - The views in the main catalog.
33
+ * @param branch - The views in the branch catalog.
34
+ * @returns A list of changes to apply to main to make it match branch.
35
+ */
36
+ export function diffViews(
37
+ ctx: {
38
+ version: number;
39
+ currentUser: string;
40
+ defaultPrivilegeState: DefaultPrivilegeState;
41
+ mainRoles: Record<string, Role>;
42
+ },
43
+ main: Record<string, View>,
44
+ branch: Record<string, View>,
45
+ ): ViewChange[] {
46
+ const { created, dropped, altered } = diffObjects(main, branch);
47
+
48
+ const changes: ViewChange[] = [];
49
+
50
+ for (const viewId of created) {
51
+ const v = branch[viewId];
52
+ changes.push(new CreateView({ view: v }));
53
+
54
+ // OWNER: If the view should be owned by someone other than the current user,
55
+ // emit ALTER VIEW ... OWNER TO after creation
56
+ if (v.owner !== ctx.currentUser) {
57
+ changes.push(new AlterViewChangeOwner({ view: v, owner: v.owner }));
58
+ }
59
+
60
+ if (v.comment !== null) {
61
+ changes.push(new CreateCommentOnView({ view: v }));
62
+ }
63
+
64
+ // PRIVILEGES: For created objects, compare against default privileges state
65
+ // The migration script will run ALTER DEFAULT PRIVILEGES before CREATE (via constraint spec),
66
+ // so objects are created with the default privileges state in effect.
67
+ // We compare default privileges against desired privileges to generate REVOKE/GRANT statements
68
+ // needed to reach the final desired state.
69
+ const effectiveDefaults = ctx.defaultPrivilegeState.getEffectiveDefaults(
70
+ ctx.currentUser,
71
+ "view",
72
+ v.schema ?? "",
73
+ );
74
+ const desiredPrivileges = v.privileges;
75
+ // Filter out owner privileges - owner always has ALL privileges implicitly
76
+ // and shouldn't be compared. Use the view owner as the reference.
77
+ const privilegeResults = diffPrivileges(
78
+ effectiveDefaults,
79
+ desiredPrivileges,
80
+ v.owner,
81
+ ctx.mainRoles,
82
+ );
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
+ }
173
+ }
174
+
175
+ for (const viewId of dropped) {
176
+ changes.push(new DropView({ view: main[viewId] }));
177
+ }
178
+
179
+ for (const viewId of altered) {
180
+ const mainView = main[viewId];
181
+ const branchView = branch[viewId];
182
+
183
+ // Check if non-alterable properties have changed
184
+ // These require dropping and recreating the view
185
+ const NON_ALTERABLE_FIELDS: Array<keyof View> = [
186
+ "definition",
187
+ "row_security",
188
+ "force_row_security",
189
+ "has_indexes",
190
+ "has_rules",
191
+ "has_triggers",
192
+ "has_subclasses",
193
+ "is_populated",
194
+ "replica_identity",
195
+ "is_partition",
196
+ "partition_bound",
197
+ ];
198
+ const nonAlterablePropsChanged = hasNonAlterableChanges(
199
+ mainView,
200
+ branchView,
201
+ NON_ALTERABLE_FIELDS,
202
+ { options: deepEqual },
203
+ );
204
+
205
+ if (nonAlterablePropsChanged) {
206
+ // Replace the entire view using CREATE OR REPLACE to avoid drop when possible
207
+ changes.push(new CreateView({ view: branchView, orReplace: true }));
208
+ } else {
209
+ // Only alterable properties changed - check each one
210
+
211
+ // OWNER
212
+ if (mainView.owner !== branchView.owner) {
213
+ changes.push(
214
+ new AlterViewChangeOwner({ view: mainView, owner: branchView.owner }),
215
+ );
216
+ }
217
+
218
+ // VIEW OPTIONS (WITH (...))
219
+ if (!deepEqual(mainView.options, branchView.options)) {
220
+ const mainOpts = mainView.options ?? [];
221
+ const branchOpts = branchView.options ?? [];
222
+
223
+ // Always set branch options when provided
224
+ if (branchOpts.length > 0) {
225
+ changes.push(
226
+ new AlterViewSetOptions({ view: mainView, options: branchOpts }),
227
+ );
228
+ }
229
+
230
+ // Reset any params that are present in main but absent in branch
231
+ if (mainOpts.length > 0) {
232
+ const mainNames = new Set(mainOpts.map((opt) => opt.split("=")[0]));
233
+ const branchNames = new Set(
234
+ branchOpts.map((opt) => opt.split("=")[0]),
235
+ );
236
+ const removed: string[] = [];
237
+ for (const name of mainNames) {
238
+ if (!branchNames.has(name)) removed.push(name);
239
+ }
240
+ if (removed.length > 0) {
241
+ changes.push(
242
+ new AlterViewResetOptions({ view: mainView, params: removed }),
243
+ );
244
+ }
245
+ }
246
+ }
247
+
248
+ // COMMENT
249
+ if (mainView.comment !== branchView.comment) {
250
+ if (branchView.comment === null) {
251
+ changes.push(new DropCommentOnView({ view: mainView }));
252
+ } else {
253
+ changes.push(new CreateCommentOnView({ view: branchView }));
254
+ }
255
+ }
256
+
257
+ // Note: View renaming would also use ALTER VIEW ... RENAME TO ...
258
+ // But since our View model uses 'name' as the identity field,
259
+ // a name change would be handled as drop + create by diffObjects()
260
+
261
+ // PRIVILEGES (unified object and column privileges)
262
+ // Filter out owner privileges - owner always has ALL privileges implicitly
263
+ // and shouldn't be compared. Use branch owner as the reference.
264
+ const privilegeResults = diffPrivileges(
265
+ mainView.privileges,
266
+ branchView.privileges,
267
+ branchView.owner,
268
+ ctx.mainRoles,
269
+ );
270
+
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
+ }
361
+ }
362
+ }
363
+
364
+ return changes;
365
+ }