@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,451 @@
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
+ AlterMaterializedViewChangeOwner,
11
+ AlterMaterializedViewSetStorageParams,
12
+ } from "./changes/materialized-view.alter.ts";
13
+ import {
14
+ CreateCommentOnMaterializedView,
15
+ CreateCommentOnMaterializedViewColumn,
16
+ DropCommentOnMaterializedView,
17
+ DropCommentOnMaterializedViewColumn,
18
+ } from "./changes/materialized-view.comment.ts";
19
+ import { CreateMaterializedView } from "./changes/materialized-view.create.ts";
20
+ import { DropMaterializedView } from "./changes/materialized-view.drop.ts";
21
+ import {
22
+ GrantMaterializedViewPrivileges,
23
+ RevokeGrantOptionMaterializedViewPrivileges,
24
+ RevokeMaterializedViewPrivileges,
25
+ } from "./changes/materialized-view.privilege.ts";
26
+ import type { MaterializedViewChange } from "./changes/materialized-view.types.ts";
27
+ import type { MaterializedView } from "./materialized-view.model.ts";
28
+
29
+ /**
30
+ * Diff two sets of materialized views from main and branch catalogs.
31
+ *
32
+ * @param ctx - Context containing version, currentUser, and defaultPrivilegeState
33
+ * @param main - The materialized views in the main catalog.
34
+ * @param branch - The materialized views in the branch catalog.
35
+ * @returns A list of changes to apply to main to make it match branch.
36
+ */
37
+ export function diffMaterializedViews(
38
+ ctx: {
39
+ version: number;
40
+ currentUser: string;
41
+ defaultPrivilegeState: DefaultPrivilegeState;
42
+ mainRoles: Record<string, Role>;
43
+ },
44
+ main: Record<string, MaterializedView>,
45
+ branch: Record<string, MaterializedView>,
46
+ ): MaterializedViewChange[] {
47
+ const { created, dropped, altered } = diffObjects(main, branch);
48
+
49
+ const changes: MaterializedViewChange[] = [];
50
+
51
+ for (const materializedViewId of created) {
52
+ const mv = branch[materializedViewId];
53
+ changes.push(
54
+ new CreateMaterializedView({
55
+ materializedView: mv,
56
+ }),
57
+ );
58
+
59
+ // OWNER: If the materialized view should be owned by someone other than the current user,
60
+ // emit ALTER MATERIALIZED VIEW ... OWNER TO after creation
61
+ if (mv.owner !== ctx.currentUser) {
62
+ changes.push(
63
+ new AlterMaterializedViewChangeOwner({
64
+ materializedView: mv,
65
+ owner: mv.owner,
66
+ }),
67
+ );
68
+ }
69
+
70
+ // Note: RLS (row_security, force_row_security) is a non-alterable property for materialized views.
71
+ // If RLS needs to be enabled, the materialized view must be dropped and recreated, which is
72
+ // handled in the "altered" section when non-alterable properties change.
73
+
74
+ // Materialized view comment on creation
75
+ if (mv.comment !== null) {
76
+ changes.push(
77
+ new CreateCommentOnMaterializedView({
78
+ materializedView: mv,
79
+ }),
80
+ );
81
+ }
82
+ // Column comments on creation
83
+ for (const col of mv.columns) {
84
+ if (col.comment !== null) {
85
+ changes.push(
86
+ new CreateCommentOnMaterializedViewColumn({
87
+ materializedView: mv,
88
+ column: col,
89
+ }),
90
+ );
91
+ }
92
+ }
93
+
94
+ // PRIVILEGES: For created objects, compare against default privileges state
95
+ // The migration script will run ALTER DEFAULT PRIVILEGES before CREATE (via constraint spec),
96
+ // so objects are created with the default privileges state in effect.
97
+ // We compare default privileges against desired privileges to generate REVOKE/GRANT statements
98
+ // needed to reach the final desired state.
99
+ const effectiveDefaults = ctx.defaultPrivilegeState.getEffectiveDefaults(
100
+ ctx.currentUser,
101
+ "materialized_view",
102
+ mv.schema ?? "",
103
+ );
104
+ const desiredPrivileges = mv.privileges;
105
+ // Filter out owner privileges - owner always has ALL privileges implicitly
106
+ // and shouldn't be compared. Use the materialized view owner as the reference.
107
+ const privilegeResults = diffPrivileges(
108
+ effectiveDefaults,
109
+ desiredPrivileges,
110
+ mv.owner,
111
+ ctx.mainRoles,
112
+ );
113
+
114
+ // Generate grant changes
115
+ for (const [grantee, result] of privilegeResults) {
116
+ if (result.grants.length > 0) {
117
+ const grantGroups = groupPrivilegesByColumns(result.grants);
118
+ for (const [, group] of grantGroups) {
119
+ for (const [grantable, privSet] of group.byGrant) {
120
+ const privileges = Array.from(privSet).map((priv) => ({
121
+ privilege: priv,
122
+ grantable,
123
+ }));
124
+ changes.push(
125
+ new GrantMaterializedViewPrivileges({
126
+ materializedView: mv,
127
+ grantee,
128
+ privileges,
129
+ columns: group.columns,
130
+ version: ctx.version,
131
+ }),
132
+ );
133
+ }
134
+ }
135
+ }
136
+
137
+ // Generate revoke changes
138
+ if (result.revokes.length > 0) {
139
+ const revokeGroups = groupPrivilegesByColumns(result.revokes);
140
+ for (const [, group] of revokeGroups) {
141
+ const allPrivileges = new Set<string>();
142
+ for (const [, privSet] of group.byGrant) {
143
+ for (const priv of privSet) {
144
+ allPrivileges.add(priv);
145
+ }
146
+ }
147
+ const privileges = Array.from(allPrivileges).map((priv) => ({
148
+ privilege: priv,
149
+ grantable: false,
150
+ }));
151
+ changes.push(
152
+ new RevokeMaterializedViewPrivileges({
153
+ materializedView: mv,
154
+ grantee,
155
+ privileges,
156
+ columns: group.columns,
157
+ version: ctx.version,
158
+ }),
159
+ );
160
+ }
161
+ }
162
+
163
+ // Generate revoke grant option changes
164
+ if (result.revokeGrantOption.length > 0) {
165
+ const revokeGrantGroups = new Map<
166
+ string,
167
+ { columns?: string[]; privileges: Set<string> }
168
+ >();
169
+ for (const r of result.revokeGrantOption) {
170
+ // For revoke grant option, we need to find the columns from the effective defaults
171
+ const originalPriv = effectiveDefaults.find(
172
+ (p) => p.grantee === grantee && p.privilege === r,
173
+ );
174
+ const key = originalPriv?.columns
175
+ ? originalPriv.columns.sort().join(",")
176
+ : "";
177
+ if (!revokeGrantGroups.has(key)) {
178
+ revokeGrantGroups.set(key, {
179
+ columns: originalPriv?.columns
180
+ ? [...originalPriv.columns]
181
+ : undefined,
182
+ privileges: new Set(),
183
+ });
184
+ }
185
+ const group = revokeGrantGroups.get(key);
186
+ if (!group) continue;
187
+ group.privileges.add(r);
188
+ }
189
+ for (const [, group] of revokeGrantGroups) {
190
+ const privilegeNames = Array.from(group.privileges);
191
+ changes.push(
192
+ new RevokeGrantOptionMaterializedViewPrivileges({
193
+ materializedView: mv,
194
+ grantee,
195
+ privilegeNames,
196
+ columns: group.columns,
197
+ version: ctx.version,
198
+ }),
199
+ );
200
+ }
201
+ }
202
+ }
203
+ }
204
+
205
+ for (const materializedViewId of dropped) {
206
+ changes.push(
207
+ new DropMaterializedView({ materializedView: main[materializedViewId] }),
208
+ );
209
+ }
210
+
211
+ for (const materializedViewId of altered) {
212
+ const mainMaterializedView = main[materializedViewId];
213
+ const branchMaterializedView = branch[materializedViewId];
214
+
215
+ // Check if non-alterable properties have changed
216
+ // These require dropping and recreating the materialized view
217
+ const NON_ALTERABLE_FIELDS: Array<keyof MaterializedView> = [
218
+ "definition",
219
+ "row_security",
220
+ "force_row_security",
221
+ "has_indexes",
222
+ "has_rules",
223
+ "has_triggers",
224
+ "has_subclasses",
225
+ "is_populated",
226
+ "replica_identity",
227
+ "is_partition",
228
+ "partition_bound",
229
+ ];
230
+ const nonAlterablePropsChanged = hasNonAlterableChanges(
231
+ mainMaterializedView,
232
+ branchMaterializedView,
233
+ NON_ALTERABLE_FIELDS,
234
+ { options: deepEqual },
235
+ );
236
+
237
+ if (nonAlterablePropsChanged) {
238
+ // Replace the entire materialized view (drop + create)
239
+ changes.push(
240
+ new DropMaterializedView({ materializedView: mainMaterializedView }),
241
+ new CreateMaterializedView({
242
+ materializedView: branchMaterializedView,
243
+ }),
244
+ );
245
+ } else {
246
+ // Only alterable properties changed - check each one
247
+
248
+ // OWNER
249
+ if (mainMaterializedView.owner !== branchMaterializedView.owner) {
250
+ changes.push(
251
+ new AlterMaterializedViewChangeOwner({
252
+ materializedView: mainMaterializedView,
253
+ owner: branchMaterializedView.owner,
254
+ }),
255
+ );
256
+ }
257
+
258
+ // STORAGE PARAMETERS (reloptions)
259
+ // Emit a combined SET/RESET change similar to indexes
260
+ if (
261
+ !deepEqual(mainMaterializedView.options, branchMaterializedView.options)
262
+ ) {
263
+ const parseOptions = (options: string[] | null | undefined) => {
264
+ const map = new Map<string, string>();
265
+ if (!options) return map;
266
+ for (const opt of options) {
267
+ const eqIndex = opt.indexOf("=");
268
+ const key = opt.slice(0, eqIndex).trim();
269
+ const value = opt.slice(eqIndex + 1).trim();
270
+ map.set(key, value);
271
+ }
272
+ return map;
273
+ };
274
+ const mainMap = parseOptions(mainMaterializedView.options);
275
+ const branchMap = parseOptions(branchMaterializedView.options);
276
+ const keysToReset: string[] = [];
277
+ for (const key of mainMap.keys()) {
278
+ if (!branchMap.has(key)) keysToReset.push(key);
279
+ }
280
+ const paramsToSet: string[] = [];
281
+ for (const [key, newValue] of branchMap.entries()) {
282
+ const oldValue = mainMap.get(key);
283
+ const changed = oldValue !== newValue;
284
+ if (changed) {
285
+ paramsToSet.push(
286
+ newValue === undefined ? key : `${key}=${newValue}`,
287
+ );
288
+ }
289
+ }
290
+ changes.push(
291
+ new AlterMaterializedViewSetStorageParams({
292
+ materializedView: mainMaterializedView,
293
+ paramsToSet,
294
+ keysToReset,
295
+ }),
296
+ );
297
+ }
298
+
299
+ // Note: Materialized view renaming would also use ALTER MATERIALIZED VIEW ... RENAME TO ...
300
+ // But since our MaterializedView model uses 'name' as the identity field,
301
+ // a name change would be handled as drop + create by diffObjects()
302
+ // MATERIALIZED VIEW COMMENT (create/drop when comment changes)
303
+ if (mainMaterializedView.comment !== branchMaterializedView.comment) {
304
+ if (branchMaterializedView.comment === null) {
305
+ changes.push(
306
+ new DropCommentOnMaterializedView({
307
+ materializedView: mainMaterializedView,
308
+ }),
309
+ );
310
+ } else {
311
+ changes.push(
312
+ new CreateCommentOnMaterializedView({
313
+ materializedView: branchMaterializedView,
314
+ }),
315
+ );
316
+ }
317
+ }
318
+ // COMMENT changes on columns
319
+ const mainCols = new Map(
320
+ mainMaterializedView.columns.map((c) => [c.name, c]),
321
+ );
322
+ const branchCols = new Map(
323
+ branchMaterializedView.columns.map((c) => [c.name, c]),
324
+ );
325
+ for (const [name, branchCol] of branchCols) {
326
+ const mainCol = mainCols.get(name);
327
+ if (!mainCol) continue;
328
+ if (mainCol.comment !== branchCol.comment) {
329
+ if (branchCol.comment === null) {
330
+ changes.push(
331
+ new DropCommentOnMaterializedViewColumn({
332
+ materializedView: mainMaterializedView,
333
+ column: mainCol,
334
+ }),
335
+ );
336
+ } else {
337
+ changes.push(
338
+ new CreateCommentOnMaterializedViewColumn({
339
+ materializedView: branchMaterializedView,
340
+ column: branchCol,
341
+ }),
342
+ );
343
+ }
344
+ }
345
+ }
346
+
347
+ // PRIVILEGES (unified object and column privileges)
348
+ // Filter out owner privileges - owner always has ALL privileges implicitly
349
+ // and shouldn't be compared. Use branch owner as the reference.
350
+ const privilegeResults = diffPrivileges(
351
+ mainMaterializedView.privileges,
352
+ branchMaterializedView.privileges,
353
+ branchMaterializedView.owner,
354
+ ctx.mainRoles,
355
+ );
356
+
357
+ for (const [grantee, result] of privilegeResults) {
358
+ // Generate grant changes
359
+ if (result.grants.length > 0) {
360
+ const grantGroups = groupPrivilegesByColumns(result.grants);
361
+ for (const [, group] of grantGroups) {
362
+ for (const [grantable, privSet] of group.byGrant) {
363
+ const privileges = Array.from(privSet).map((priv) => ({
364
+ privilege: priv,
365
+ grantable,
366
+ }));
367
+ changes.push(
368
+ new GrantMaterializedViewPrivileges({
369
+ materializedView: branchMaterializedView,
370
+ grantee,
371
+ privileges,
372
+ columns: group.columns,
373
+ version: ctx.version,
374
+ }),
375
+ );
376
+ }
377
+ }
378
+ }
379
+
380
+ // Generate revoke changes
381
+ if (result.revokes.length > 0) {
382
+ const revokeGroups = groupPrivilegesByColumns(result.revokes);
383
+ for (const [, group] of revokeGroups) {
384
+ // Collapse all grantable groups into a single revoke (grantable: false)
385
+ const allPrivileges = new Set<string>();
386
+ for (const [, privSet] of group.byGrant) {
387
+ for (const priv of privSet) {
388
+ allPrivileges.add(priv);
389
+ }
390
+ }
391
+ const privileges = Array.from(allPrivileges).map((priv) => ({
392
+ privilege: priv,
393
+ grantable: false,
394
+ }));
395
+ changes.push(
396
+ new RevokeMaterializedViewPrivileges({
397
+ materializedView: mainMaterializedView,
398
+ grantee,
399
+ privileges,
400
+ columns: group.columns,
401
+ version: ctx.version,
402
+ }),
403
+ );
404
+ }
405
+ }
406
+
407
+ // Generate revoke grant option changes
408
+ if (result.revokeGrantOption.length > 0) {
409
+ const revokeGrantGroups = new Map<
410
+ string,
411
+ { columns?: string[]; privileges: Set<string> }
412
+ >();
413
+ for (const r of result.revokeGrantOption) {
414
+ // For revoke grant option, we need to find the columns from the original privilege
415
+ const originalPriv = mainMaterializedView.privileges.find(
416
+ (p) => p.grantee === grantee && p.privilege === r,
417
+ );
418
+ const key = originalPriv?.columns
419
+ ? originalPriv.columns.sort().join(",")
420
+ : "";
421
+ if (!revokeGrantGroups.has(key)) {
422
+ revokeGrantGroups.set(key, {
423
+ columns: originalPriv?.columns
424
+ ? [...originalPriv.columns]
425
+ : undefined,
426
+ privileges: new Set(),
427
+ });
428
+ }
429
+ const group = revokeGrantGroups.get(key);
430
+ if (!group) continue;
431
+ group.privileges.add(r);
432
+ }
433
+ for (const [, group] of revokeGrantGroups) {
434
+ const privilegeNames = Array.from(group.privileges);
435
+ changes.push(
436
+ new RevokeGrantOptionMaterializedViewPrivileges({
437
+ materializedView: mainMaterializedView,
438
+ grantee,
439
+ privilegeNames,
440
+ columns: group.columns,
441
+ version: ctx.version,
442
+ }),
443
+ );
444
+ }
445
+ }
446
+ }
447
+ }
448
+ }
449
+
450
+ return changes;
451
+ }
@@ -0,0 +1,258 @@
1
+ import { sql } from "@ts-safeql/sql-tag";
2
+ import type { Pool } from "pg";
3
+ import z from "zod";
4
+ import {
5
+ BasePgModel,
6
+ columnPropsSchema,
7
+ type TableLikeObject,
8
+ } from "../base.model.ts";
9
+ import {
10
+ type PrivilegeProps,
11
+ privilegePropsSchema,
12
+ } from "../base.privilege-diff.ts";
13
+ import { ReplicaIdentitySchema } from "../table/table.model.ts";
14
+
15
+ const materializedViewPropsSchema = z.object({
16
+ schema: z.string(),
17
+ name: z.string(),
18
+ definition: z.string(),
19
+ row_security: z.boolean(),
20
+ force_row_security: z.boolean(),
21
+ has_indexes: z.boolean(),
22
+ has_rules: z.boolean(),
23
+ has_triggers: z.boolean(),
24
+ has_subclasses: z.boolean(),
25
+ is_populated: z.boolean(),
26
+ replica_identity: ReplicaIdentitySchema,
27
+ is_partition: z.boolean(),
28
+ options: z.array(z.string()).nullable(),
29
+ partition_bound: z.string().nullable(),
30
+ owner: z.string(),
31
+ comment: z.string().nullable(),
32
+ columns: z.array(columnPropsSchema),
33
+ privileges: z.array(privilegePropsSchema),
34
+ });
35
+
36
+ type MaterializedViewPrivilegeProps = PrivilegeProps;
37
+ export type MaterializedViewProps = z.infer<typeof materializedViewPropsSchema>;
38
+
39
+ export class MaterializedView extends BasePgModel implements TableLikeObject {
40
+ public readonly schema: MaterializedViewProps["schema"];
41
+ public readonly name: MaterializedViewProps["name"];
42
+ public readonly definition: MaterializedViewProps["definition"];
43
+ public readonly row_security: MaterializedViewProps["row_security"];
44
+ public readonly force_row_security: MaterializedViewProps["force_row_security"];
45
+ public readonly has_indexes: MaterializedViewProps["has_indexes"];
46
+ public readonly has_rules: MaterializedViewProps["has_rules"];
47
+ public readonly has_triggers: MaterializedViewProps["has_triggers"];
48
+ public readonly has_subclasses: MaterializedViewProps["has_subclasses"];
49
+ public readonly is_populated: MaterializedViewProps["is_populated"];
50
+ public readonly replica_identity: MaterializedViewProps["replica_identity"];
51
+ public readonly is_partition: MaterializedViewProps["is_partition"];
52
+ public readonly options: MaterializedViewProps["options"];
53
+ public readonly partition_bound: MaterializedViewProps["partition_bound"];
54
+ public readonly owner: MaterializedViewProps["owner"];
55
+ public readonly comment: MaterializedViewProps["comment"];
56
+ public readonly columns: MaterializedViewProps["columns"];
57
+ public readonly privileges: MaterializedViewPrivilegeProps[];
58
+
59
+ constructor(props: MaterializedViewProps) {
60
+ super();
61
+
62
+ // Identity fields
63
+ this.schema = props.schema;
64
+ this.name = props.name;
65
+
66
+ // Data fields
67
+ this.definition = props.definition;
68
+ this.row_security = props.row_security;
69
+ this.force_row_security = props.force_row_security;
70
+ this.has_indexes = props.has_indexes;
71
+ this.has_rules = props.has_rules;
72
+ this.has_triggers = props.has_triggers;
73
+ this.has_subclasses = props.has_subclasses;
74
+ this.is_populated = props.is_populated;
75
+ this.replica_identity = props.replica_identity;
76
+ this.is_partition = props.is_partition;
77
+ this.options = props.options;
78
+ this.partition_bound = props.partition_bound;
79
+ this.owner = props.owner;
80
+ this.comment = props.comment;
81
+ this.columns = props.columns;
82
+ this.privileges = props.privileges;
83
+ }
84
+
85
+ get stableId(): `materializedView:${string}` {
86
+ return `materializedView:${this.schema}.${this.name}`;
87
+ }
88
+
89
+ get identityFields() {
90
+ return {
91
+ schema: this.schema,
92
+ name: this.name,
93
+ };
94
+ }
95
+
96
+ get dataFields() {
97
+ return {
98
+ definition: this.definition,
99
+ row_security: this.row_security,
100
+ force_row_security: this.force_row_security,
101
+ has_indexes: this.has_indexes,
102
+ has_rules: this.has_rules,
103
+ has_triggers: this.has_triggers,
104
+ has_subclasses: this.has_subclasses,
105
+ is_populated: this.is_populated,
106
+ replica_identity: this.replica_identity,
107
+ is_partition: this.is_partition,
108
+ options: this.options,
109
+ partition_bound: this.partition_bound,
110
+ owner: this.owner,
111
+ comment: this.comment,
112
+ columns: this.columns,
113
+ privileges: this.privileges,
114
+ };
115
+ }
116
+
117
+ override stableSnapshot() {
118
+ const normalizeColumns = () =>
119
+ [...this.columns]
120
+ .map((col) => {
121
+ const { position: _pos, ...rest } = col as unknown as Record<
122
+ string,
123
+ unknown
124
+ >;
125
+ return rest;
126
+ })
127
+ .sort((a, b) => {
128
+ const nameA = (a.name as string | undefined) ?? "";
129
+ const nameB = (b.name as string | undefined) ?? "";
130
+ return nameA.localeCompare(nameB);
131
+ });
132
+
133
+ return {
134
+ identity: this.identityFields,
135
+ data: {
136
+ ...this.dataFields,
137
+ columns: normalizeColumns(),
138
+ },
139
+ };
140
+ }
141
+ }
142
+
143
+ export async function extractMaterializedViews(
144
+ pool: Pool,
145
+ ): Promise<MaterializedView[]> {
146
+ const { rows: mvRows } = await pool.query<MaterializedViewProps>(sql`
147
+ with extension_oids as (
148
+ select
149
+ objid
150
+ from
151
+ pg_depend d
152
+ where
153
+ d.refclassid = 'pg_extension'::regclass
154
+ and d.classid = 'pg_class'::regclass
155
+ )
156
+ select
157
+ c.relnamespace::regnamespace::text as schema,
158
+ quote_ident(c.relname) as name,
159
+ -- remove trailing semicolon from the definition if present
160
+ rtrim(pg_get_viewdef(c.oid), ';') as definition,
161
+ c.relrowsecurity as row_security,
162
+ c.relforcerowsecurity as force_row_security,
163
+ c.relhasindex as has_indexes,
164
+ c.relhasrules as has_rules,
165
+ c.relhastriggers as has_triggers,
166
+ c.relhassubclass as has_subclasses,
167
+ c.relispopulated as is_populated,
168
+ c.relreplident as replica_identity,
169
+ c.relispartition as is_partition,
170
+ c.reloptions as options,
171
+ pg_get_expr(c.relpartbound, c.oid) as partition_bound,
172
+ c.relowner::regrole::text as owner,
173
+ obj_description(c.oid, 'pg_class') as comment,
174
+ coalesce(json_agg(
175
+ case when a.attname is not null then
176
+ json_build_object(
177
+ 'name', quote_ident(a.attname),
178
+ 'position', a.attnum,
179
+ 'data_type', a.atttypid::regtype::text,
180
+ 'data_type_str', format_type(a.atttypid, a.atttypmod),
181
+ 'is_custom_type', ty.typnamespace::regnamespace::text not in ('pg_catalog', 'information_schema'),
182
+ 'custom_type_type', case when ty.typnamespace::regnamespace::text not in ('pg_catalog', 'information_schema') then ty.typtype else null end,
183
+ 'custom_type_category', case when ty.typnamespace::regnamespace::text not in ('pg_catalog', 'information_schema') then ty.typcategory else null end,
184
+ 'custom_type_schema', case when ty.typnamespace::regnamespace::text not in ('pg_catalog', 'information_schema') then ty.typnamespace::regnamespace else null end,
185
+ 'custom_type_name', case when ty.typnamespace::regnamespace::text not in ('pg_catalog', 'information_schema') then quote_ident(ty.typname) else null end,
186
+ 'not_null', a.attnotnull,
187
+ 'is_identity', a.attidentity != '',
188
+ 'is_identity_always', a.attidentity = 'a',
189
+ 'is_generated', a.attgenerated != '',
190
+ 'collation', (
191
+ select quote_ident(c2.collname)
192
+ from pg_collation c2, pg_type t2
193
+ where c2.oid = a.attcollation
194
+ and t2.oid = a.atttypid
195
+ and a.attcollation <> t2.typcollation
196
+ ),
197
+ 'default', pg_get_expr(ad.adbin, ad.adrelid),
198
+ 'comment', col_description(a.attrelid, a.attnum)
199
+ )
200
+ end
201
+ order by a.attnum
202
+ ) filter (where a.attname is not null), '[]') as columns,
203
+ coalesce((
204
+ select json_agg(
205
+ json_build_object(
206
+ 'grantee', case when grp.grantee = 0 then 'PUBLIC' else grp.grantee::regrole::text end,
207
+ 'privilege', grp.privilege_type,
208
+ 'grantable', grp.is_grantable,
209
+ 'columns', case when grp.cols is not null and array_length(grp.cols,1) > 0
210
+ then grp.cols
211
+ else null end
212
+ )
213
+ order by grp.grantee, grp.privilege_type
214
+ )
215
+ from (
216
+ select
217
+ x.grantee,
218
+ x.privilege_type,
219
+ bool_or(x.is_grantable) as is_grantable,
220
+ array_agg(quote_ident(src.attname) order by src.attname)
221
+ filter (where src.attname is not null) as cols
222
+ from (
223
+ -- one row for object ACL + one row per column ACL
224
+ select null::name as attname, c.relacl as acl
225
+ union all
226
+ select a2.attname, a2.attacl
227
+ from pg_attribute a2
228
+ where a2.attrelid = c.oid
229
+ and a2.attnum > 0
230
+ and not a2.attisdropped
231
+ and a2.attacl is not null
232
+ ) as src
233
+ join lateral aclexplode(src.acl) as x(grantor, grantee, privilege_type, is_grantable) on true
234
+ group by x.grantee, x.privilege_type
235
+ ) as grp
236
+ ), '[]') as privileges
237
+ from
238
+ pg_catalog.pg_class c
239
+ left outer join extension_oids e on c.oid = e.objid
240
+ left join pg_attribute a on a.attrelid = c.oid and a.attnum > 0 and not a.attisdropped
241
+ left join pg_attrdef ad on a.attrelid = ad.adrelid and a.attnum = ad.adnum
242
+ left join pg_type ty on ty.oid = a.atttypid
243
+ where not c.relnamespace::regnamespace::text like any(array['pg\\_%', 'information\\_schema'])
244
+ and e.objid is null
245
+ and c.relkind = 'm'
246
+ group by
247
+ c.oid, c.relnamespace, c.relname, pg_get_viewdef(c.oid), c.relrowsecurity, c.relforcerowsecurity, c.relhasindex, c.relhasrules, c.relhastriggers, c.relhassubclass, c.relispopulated, c.relreplident, c.relispartition, c.reloptions, pg_get_expr(c.relpartbound, c.oid), c.relowner
248
+ order by
249
+ c.relnamespace::regnamespace, c.relname
250
+ `);
251
+ // Validate and parse each row using the Zod schema
252
+ const validatedRows = mvRows.map((row: unknown) =>
253
+ materializedViewPropsSchema.parse(row),
254
+ );
255
+ return validatedRows.map(
256
+ (row: MaterializedViewProps) => new MaterializedView(row),
257
+ );
258
+ }