@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,47 @@
1
+ import { stableId } from "../../utils.ts";
2
+ import type { Schema } from "../schema.model.ts";
3
+ import { CreateSchemaChange } from "./schema.base.ts";
4
+
5
+ /**
6
+ * Create a schema.
7
+ *
8
+ * @see https://www.postgresql.org/docs/17/sql-createschema.html
9
+ *
10
+ * Synopsis
11
+ * ```sql
12
+ * CREATE SCHEMA [ IF NOT EXISTS ] schema_name [ AUTHORIZATION role_specification ] [ schema_element [ ... ] ]
13
+ * CREATE SCHEMA [ IF NOT EXISTS ] AUTHORIZATION role_specification [ schema_element [ ... ] ]
14
+ * CREATE SCHEMA [ IF NOT EXISTS ] schema_name AUTHORIZATION role_specification [ schema_element [ ... ] ]
15
+ * ```
16
+ */
17
+ export class CreateSchema extends CreateSchemaChange {
18
+ public readonly schema: Schema;
19
+ public readonly scope = "object" as const;
20
+
21
+ constructor(props: { schema: Schema; skipAuthorization?: boolean }) {
22
+ super();
23
+ this.schema = props.schema;
24
+ }
25
+
26
+ get creates() {
27
+ return [this.schema.stableId];
28
+ }
29
+
30
+ get requires() {
31
+ return [stableId.role(this.schema.owner)];
32
+ }
33
+
34
+ serialize(options?: { skipAuthorization?: boolean }): string {
35
+ const parts: string[] = ["CREATE SCHEMA"];
36
+
37
+ // Add schema name
38
+ parts.push(this.schema.name);
39
+
40
+ // Add AUTHORIZATION
41
+ if (!options?.skipAuthorization) {
42
+ parts.push("AUTHORIZATION", this.schema.owner);
43
+ }
44
+
45
+ return parts.join(" ");
46
+ }
47
+ }
@@ -0,0 +1,20 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { Schema } from "../schema.model.ts";
3
+ import { DropSchema } from "./schema.drop.ts";
4
+
5
+ describe("schema", () => {
6
+ test("drop", () => {
7
+ const schema = new Schema({
8
+ name: "test_schema",
9
+ owner: "test",
10
+ comment: null,
11
+ privileges: [],
12
+ });
13
+
14
+ const change = new DropSchema({
15
+ schema,
16
+ });
17
+
18
+ expect(change.serialize()).toBe("DROP SCHEMA test_schema");
19
+ });
20
+ });
@@ -0,0 +1,34 @@
1
+ import type { Schema } from "../schema.model.ts";
2
+ import { DropSchemaChange } from "./schema.base.ts";
3
+
4
+ /**
5
+ * Drop a schema.
6
+ *
7
+ * @see https://www.postgresql.org/docs/17/sql-dropschema.html
8
+ *
9
+ * Synopsis
10
+ * ```sql
11
+ * DROP SCHEMA [ IF EXISTS ] name [, ...] [ CASCADE | RESTRICT ]
12
+ * ```
13
+ */
14
+ export class DropSchema extends DropSchemaChange {
15
+ public readonly schema: Schema;
16
+ public readonly scope = "object" as const;
17
+
18
+ constructor(props: { schema: Schema }) {
19
+ super();
20
+ this.schema = props.schema;
21
+ }
22
+
23
+ get drops() {
24
+ return [this.schema.stableId];
25
+ }
26
+
27
+ get requires() {
28
+ return [this.schema.stableId];
29
+ }
30
+
31
+ serialize(): string {
32
+ return ["DROP SCHEMA", this.schema.name].join(" ");
33
+ }
34
+ }
@@ -0,0 +1,175 @@
1
+ import {
2
+ formatObjectPrivilegeList,
3
+ getObjectKindPrefix,
4
+ } from "../../base.privilege.ts";
5
+ import { stableId } from "../../utils.ts";
6
+ import type { Schema } from "../schema.model.ts";
7
+ import { AlterSchemaChange } from "./schema.base.ts";
8
+
9
+ export type SchemaPrivilege =
10
+ | GrantSchemaPrivileges
11
+ | RevokeSchemaPrivileges
12
+ | RevokeGrantOptionSchemaPrivileges;
13
+
14
+ /**
15
+ * Grant privileges on a schema.
16
+ *
17
+ * @see https://www.postgresql.org/docs/17/sql-grant.html
18
+ *
19
+ * Synopsis
20
+ * ```sql
21
+ * GRANT { { CREATE | USAGE } [, ...] | ALL [ PRIVILEGES ] }
22
+ * ON SCHEMA schema_name [, ...]
23
+ * TO role_specification [, ...] [ WITH GRANT OPTION ]
24
+ * [ GRANTED BY role_specification ]
25
+ * ```
26
+ */
27
+ export class GrantSchemaPrivileges extends AlterSchemaChange {
28
+ public readonly schema: Schema;
29
+ public readonly grantee: string;
30
+ public readonly privileges: { privilege: string; grantable: boolean }[];
31
+ public readonly version: number | undefined;
32
+ public readonly scope = "privilege" as const;
33
+
34
+ constructor(props: {
35
+ schema: Schema;
36
+ grantee: string;
37
+ privileges: { privilege: string; grantable: boolean }[];
38
+ version?: number;
39
+ }) {
40
+ super();
41
+ this.schema = props.schema;
42
+ this.grantee = props.grantee;
43
+ this.privileges = props.privileges;
44
+ this.version = props.version;
45
+ }
46
+
47
+ get creates() {
48
+ return [stableId.acl(this.schema.stableId, this.grantee)];
49
+ }
50
+
51
+ get requires() {
52
+ return [this.schema.stableId, stableId.role(this.grantee)];
53
+ }
54
+
55
+ serialize(): string {
56
+ const hasGrantable = this.privileges.some((p) => p.grantable);
57
+ const hasBase = this.privileges.some((p) => !p.grantable);
58
+ if (hasGrantable && hasBase) {
59
+ throw new Error(
60
+ "GrantSchemaPrivileges expects privileges with uniform grantable flag",
61
+ );
62
+ }
63
+ const withGrant = hasGrantable ? " WITH GRANT OPTION" : "";
64
+ const kindPrefix = getObjectKindPrefix("SCHEMA");
65
+ const list = this.privileges.map((p) => p.privilege);
66
+ const privSql = formatObjectPrivilegeList("SCHEMA", list, this.version);
67
+ const schemaName = this.schema.name;
68
+ return `GRANT ${privSql} ${kindPrefix} ${schemaName} TO ${this.grantee}${withGrant}`;
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Revoke privileges on a schema.
74
+ *
75
+ * @see https://www.postgresql.org/docs/17/sql-revoke.html
76
+ *
77
+ * Synopsis
78
+ * ```sql
79
+ * REVOKE [ GRANT OPTION FOR ]
80
+ * { { CREATE | USAGE } [, ...] | ALL [ PRIVILEGES ] }
81
+ * ON SCHEMA schema_name [, ...]
82
+ * FROM role_specification [, ...]
83
+ * [ GRANTED BY role_specification ]
84
+ * [ CASCADE | RESTRICT ]
85
+ * ```
86
+ */
87
+ export class RevokeSchemaPrivileges extends AlterSchemaChange {
88
+ public readonly schema: Schema;
89
+ public readonly grantee: string;
90
+ public readonly privileges: { privilege: string; grantable: boolean }[];
91
+ public readonly version: number | undefined;
92
+ public readonly scope = "privilege" as const;
93
+
94
+ constructor(props: {
95
+ schema: Schema;
96
+ grantee: string;
97
+ privileges: { privilege: string; grantable: boolean }[];
98
+ version?: number;
99
+ }) {
100
+ super();
101
+ this.schema = props.schema;
102
+ this.grantee = props.grantee;
103
+ this.privileges = props.privileges;
104
+ this.version = props.version;
105
+ }
106
+
107
+ get drops() {
108
+ // Return ACL ID for dependency tracking, even though this is an ALTER operation
109
+ // Phase assignment now uses operation type, so this won't affect phase placement
110
+ return [stableId.acl(this.schema.stableId, this.grantee)];
111
+ }
112
+
113
+ get requires() {
114
+ return [
115
+ stableId.acl(this.schema.stableId, this.grantee),
116
+ this.schema.stableId,
117
+ stableId.role(this.grantee),
118
+ ];
119
+ }
120
+
121
+ serialize(): string {
122
+ const kindPrefix = getObjectKindPrefix("SCHEMA");
123
+ const list = this.privileges.map((p) => p.privilege);
124
+ const privSql = formatObjectPrivilegeList("SCHEMA", list, this.version);
125
+ const schemaName = this.schema.name;
126
+ return `REVOKE ${privSql} ${kindPrefix} ${schemaName} FROM ${this.grantee}`;
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Revoke grant option for privileges on a schema.
132
+ *
133
+ * This removes the ability to grant the privilege to others, but keeps the privilege itself.
134
+ *
135
+ * @see https://www.postgresql.org/docs/17/sql-revoke.html
136
+ */
137
+ export class RevokeGrantOptionSchemaPrivileges extends AlterSchemaChange {
138
+ public readonly schema: Schema;
139
+ public readonly grantee: string;
140
+ public readonly privilegeNames: string[];
141
+ public readonly version: number | undefined;
142
+ public readonly scope = "privilege" as const;
143
+
144
+ constructor(props: {
145
+ schema: Schema;
146
+ grantee: string;
147
+ privilegeNames: string[];
148
+ version?: number;
149
+ }) {
150
+ super();
151
+ this.schema = props.schema;
152
+ this.grantee = props.grantee;
153
+ this.privilegeNames = [...new Set(props.privilegeNames)].sort();
154
+ this.version = props.version;
155
+ }
156
+
157
+ get requires() {
158
+ return [
159
+ stableId.acl(this.schema.stableId, this.grantee),
160
+ this.schema.stableId,
161
+ stableId.role(this.grantee),
162
+ ];
163
+ }
164
+
165
+ serialize(): string {
166
+ const kindPrefix = getObjectKindPrefix("SCHEMA");
167
+ const privSql = formatObjectPrivilegeList(
168
+ "SCHEMA",
169
+ this.privilegeNames,
170
+ this.version,
171
+ );
172
+ const schemaName = this.schema.name;
173
+ return `REVOKE GRANT OPTION FOR ${privSql} ${kindPrefix} ${schemaName} FROM ${this.grantee}`;
174
+ }
175
+ }
@@ -0,0 +1,12 @@
1
+ import type { AlterSchema } from "./schema.alter.ts";
2
+ import type { CommentSchema } from "./schema.comment.ts";
3
+ import type { CreateSchema } from "./schema.create.ts";
4
+ import type { DropSchema } from "./schema.drop.ts";
5
+ import type { SchemaPrivilege } from "./schema.privilege.ts";
6
+
7
+ export type SchemaChange =
8
+ | AlterSchema
9
+ | CommentSchema
10
+ | CreateSchema
11
+ | DropSchema
12
+ | SchemaPrivilege;
@@ -0,0 +1,42 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { DefaultPrivilegeState } from "../base.default-privileges.ts";
3
+ import { AlterSchemaChangeOwner } from "./changes/schema.alter.ts";
4
+ import { CreateSchema } from "./changes/schema.create.ts";
5
+ import { DropSchema } from "./changes/schema.drop.ts";
6
+ import { diffSchemas } from "./schema.diff.ts";
7
+ import { Schema, type SchemaProps } from "./schema.model.ts";
8
+
9
+ const base: SchemaProps = {
10
+ name: "utils",
11
+ owner: "o1",
12
+ comment: null,
13
+ privileges: [],
14
+ };
15
+
16
+ const testContext = {
17
+ version: 170000,
18
+ currentUser: "postgres",
19
+ defaultPrivilegeState: new DefaultPrivilegeState({}),
20
+ mainRoles: {},
21
+ };
22
+
23
+ describe.concurrent("schema.diff", () => {
24
+ test("create and drop", () => {
25
+ const s = new Schema(base);
26
+ const created = diffSchemas(testContext, {}, { [s.stableId]: s });
27
+ expect(created[0]).toBeInstanceOf(CreateSchema);
28
+ const dropped = diffSchemas(testContext, { [s.stableId]: s }, {});
29
+ expect(dropped[0]).toBeInstanceOf(DropSchema);
30
+ });
31
+
32
+ test("alter owner", () => {
33
+ const main = new Schema(base);
34
+ const branch = new Schema({ ...base, owner: "o2" });
35
+ const changes = diffSchemas(
36
+ testContext,
37
+ { [main.stableId]: main },
38
+ { [branch.stableId]: branch },
39
+ );
40
+ expect(changes[0]).toBeInstanceOf(AlterSchemaChangeOwner);
41
+ });
42
+ });
@@ -0,0 +1,209 @@
1
+ import type { DefaultPrivilegeState } from "../base.default-privileges.ts";
2
+ import { diffObjects } from "../base.diff.ts";
3
+ import {
4
+ diffPrivileges,
5
+ groupPrivilegesByGrantable,
6
+ } from "../base.privilege-diff.ts";
7
+ import type { Role } from "../role/role.model.ts";
8
+ import { AlterSchemaChangeOwner } from "./changes/schema.alter.ts";
9
+ import {
10
+ CreateCommentOnSchema,
11
+ DropCommentOnSchema,
12
+ } from "./changes/schema.comment.ts";
13
+ import { CreateSchema } from "./changes/schema.create.ts";
14
+ import { DropSchema } from "./changes/schema.drop.ts";
15
+ import {
16
+ GrantSchemaPrivileges,
17
+ RevokeGrantOptionSchemaPrivileges,
18
+ RevokeSchemaPrivileges,
19
+ } from "./changes/schema.privilege.ts";
20
+ import type { SchemaChange } from "./changes/schema.types.ts";
21
+ import type { Schema } from "./schema.model.ts";
22
+
23
+ /**
24
+ * Diff two sets of schemas from main and branch catalogs.
25
+ *
26
+ * @param ctx - Context containing version, currentUser, and defaultPrivilegeState
27
+ * @param main - The schemas in the main catalog.
28
+ * @param branch - The schemas in the branch catalog.
29
+ * @returns A list of changes to apply to main to make it match branch.
30
+ */
31
+ export function diffSchemas(
32
+ ctx: {
33
+ version: number;
34
+ currentUser: string;
35
+ defaultPrivilegeState: DefaultPrivilegeState;
36
+ mainRoles: Record<string, Role>;
37
+ },
38
+ main: Record<string, Schema>,
39
+ branch: Record<string, Schema>,
40
+ ): SchemaChange[] {
41
+ const { created, dropped, altered } = diffObjects(main, branch);
42
+
43
+ const changes: SchemaChange[] = [];
44
+
45
+ for (const schemaId of created) {
46
+ const sc = branch[schemaId];
47
+ changes.push(new CreateSchema({ schema: sc }));
48
+ if (sc.comment !== null) {
49
+ changes.push(new CreateCommentOnSchema({ schema: sc }));
50
+ }
51
+
52
+ // PRIVILEGES: For created objects, compare against default privileges state
53
+ // The migration script will run ALTER DEFAULT PRIVILEGES before CREATE (via constraint spec),
54
+ // so objects are created with the default privileges state in effect.
55
+ // We compare default privileges against desired privileges to generate REVOKE/GRANT statements
56
+ // needed to reach the final desired state.
57
+ // Note: Schemas don't have a schema property, so we pass empty string
58
+ const effectiveDefaults = ctx.defaultPrivilegeState.getEffectiveDefaults(
59
+ ctx.currentUser,
60
+ "schema",
61
+ "",
62
+ );
63
+ const desiredPrivileges = sc.privileges;
64
+ // Filter out owner privileges - owner always has ALL privileges implicitly
65
+ // and shouldn't be compared. Use the schema owner as the reference.
66
+ const privilegeResults = diffPrivileges(
67
+ effectiveDefaults,
68
+ desiredPrivileges,
69
+ sc.owner,
70
+ ctx.mainRoles,
71
+ );
72
+
73
+ // Generate grant changes
74
+ for (const [grantee, result] of privilegeResults) {
75
+ if (result.grants.length > 0) {
76
+ const grantGroups = groupPrivilegesByGrantable(result.grants);
77
+ for (const [grantable, list] of grantGroups) {
78
+ void grantable;
79
+ changes.push(
80
+ new GrantSchemaPrivileges({
81
+ schema: sc,
82
+ grantee,
83
+ privileges: list,
84
+ version: ctx.version,
85
+ }),
86
+ );
87
+ }
88
+ }
89
+
90
+ // Generate revoke changes
91
+ if (result.revokes.length > 0) {
92
+ const revokeGroups = groupPrivilegesByGrantable(result.revokes);
93
+ for (const [grantable, list] of revokeGroups) {
94
+ void grantable;
95
+ changes.push(
96
+ new RevokeSchemaPrivileges({
97
+ schema: sc,
98
+ grantee,
99
+ privileges: list,
100
+ version: ctx.version,
101
+ }),
102
+ );
103
+ }
104
+ }
105
+
106
+ // Generate revoke grant option changes
107
+ if (result.revokeGrantOption.length > 0) {
108
+ changes.push(
109
+ new RevokeGrantOptionSchemaPrivileges({
110
+ schema: sc,
111
+ grantee,
112
+ privilegeNames: result.revokeGrantOption,
113
+ version: ctx.version,
114
+ }),
115
+ );
116
+ }
117
+ }
118
+ }
119
+
120
+ for (const schemaId of dropped) {
121
+ changes.push(new DropSchema({ schema: main[schemaId] }));
122
+ }
123
+
124
+ for (const schemaId of altered) {
125
+ const mainSchema = main[schemaId];
126
+ const branchSchema = branch[schemaId];
127
+
128
+ // OWNER
129
+ if (mainSchema.owner !== branchSchema.owner) {
130
+ changes.push(
131
+ new AlterSchemaChangeOwner({
132
+ schema: mainSchema,
133
+ owner: branchSchema.owner,
134
+ }),
135
+ );
136
+ }
137
+
138
+ // COMMENT
139
+ if (mainSchema.comment !== branchSchema.comment) {
140
+ if (branchSchema.comment === null) {
141
+ changes.push(new DropCommentOnSchema({ schema: mainSchema }));
142
+ } else {
143
+ changes.push(new CreateCommentOnSchema({ schema: branchSchema }));
144
+ }
145
+ }
146
+
147
+ // PRIVILEGES
148
+ // Filter out owner privileges - owner always has ALL privileges implicitly
149
+ // and shouldn't be compared. Use branch owner as the reference.
150
+ const privilegeResults = diffPrivileges(
151
+ mainSchema.privileges,
152
+ branchSchema.privileges,
153
+ branchSchema.owner,
154
+ ctx.mainRoles,
155
+ );
156
+
157
+ for (const [grantee, result] of privilegeResults) {
158
+ // Generate grant changes
159
+ if (result.grants.length > 0) {
160
+ const grantGroups = groupPrivilegesByGrantable(result.grants);
161
+ for (const [grantable, list] of grantGroups) {
162
+ void grantable;
163
+ changes.push(
164
+ new GrantSchemaPrivileges({
165
+ schema: branchSchema,
166
+ grantee,
167
+ privileges: list,
168
+ version: ctx.version,
169
+ }),
170
+ );
171
+ }
172
+ }
173
+
174
+ // Generate revoke changes
175
+ if (result.revokes.length > 0) {
176
+ const revokeGroups = groupPrivilegesByGrantable(result.revokes);
177
+ for (const [grantable, list] of revokeGroups) {
178
+ void grantable;
179
+ changes.push(
180
+ new RevokeSchemaPrivileges({
181
+ schema: mainSchema,
182
+ grantee,
183
+ privileges: list,
184
+ version: ctx.version,
185
+ }),
186
+ );
187
+ }
188
+ }
189
+
190
+ // Generate revoke grant option changes
191
+ if (result.revokeGrantOption.length > 0) {
192
+ changes.push(
193
+ new RevokeGrantOptionSchemaPrivileges({
194
+ schema: mainSchema,
195
+ grantee,
196
+ privilegeNames: result.revokeGrantOption,
197
+ version: ctx.version,
198
+ }),
199
+ );
200
+ }
201
+ }
202
+
203
+ // Note: Schema renaming would also use ALTER SCHEMA ... RENAME TO ...
204
+ // But since our Schema model uses 'schema' as the identity field,
205
+ // a name change would be handled as drop + create by diffObjects()
206
+ }
207
+
208
+ return changes;
209
+ }
@@ -0,0 +1,107 @@
1
+ import { sql } from "@ts-safeql/sql-tag";
2
+ import type { Pool } from "pg";
3
+ import z from "zod";
4
+ import { BasePgModel } from "../base.model.ts";
5
+ import {
6
+ type PrivilegeProps,
7
+ privilegePropsSchema,
8
+ } from "../base.privilege-diff.ts";
9
+
10
+ /**
11
+ * All properties exposed by CREATE SCHEMA statement are included in diff output.
12
+ * https://www.postgresql.org/docs/current/sql-createschema.html
13
+ *
14
+ * ALTER SCHEMA statement can be generated for all properties.
15
+ * https://www.postgresql.org/docs/current/sql-alterschema.html
16
+ */
17
+ const schemaPropsSchema = z.object({
18
+ name: z.string(),
19
+ owner: z.string(),
20
+ comment: z.string().nullable(),
21
+ privileges: z.array(privilegePropsSchema),
22
+ });
23
+
24
+ type SchemaPrivilegeProps = PrivilegeProps;
25
+ export type SchemaProps = z.infer<typeof schemaPropsSchema>;
26
+
27
+ export class Schema extends BasePgModel {
28
+ public readonly name: SchemaProps["name"];
29
+ public readonly owner: SchemaProps["owner"];
30
+ public readonly comment: SchemaProps["comment"];
31
+ public readonly privileges: SchemaPrivilegeProps[];
32
+
33
+ constructor(props: SchemaProps) {
34
+ super();
35
+
36
+ // Identity fields
37
+ this.name = props.name;
38
+
39
+ // Data fields
40
+ this.owner = props.owner;
41
+ this.comment = props.comment;
42
+ this.privileges = props.privileges;
43
+ }
44
+
45
+ get stableId(): `schema:${string}` {
46
+ return `schema:${this.name}`;
47
+ }
48
+
49
+ get identityFields() {
50
+ return {
51
+ name: this.name,
52
+ };
53
+ }
54
+
55
+ get dataFields() {
56
+ return {
57
+ owner: this.owner,
58
+ comment: this.comment,
59
+ privileges: this.privileges,
60
+ };
61
+ }
62
+ }
63
+
64
+ export async function extractSchemas(pool: Pool): Promise<Schema[]> {
65
+ const { rows: schemaRows } = await pool.query<SchemaProps>(sql`
66
+ with extension_oids as (
67
+ select
68
+ objid
69
+ from
70
+ pg_depend d
71
+ where
72
+ d.refclassid = 'pg_extension'::regclass
73
+ and d.classid = 'pg_namespace'::regclass
74
+ )
75
+ select
76
+ quote_ident(nspname) as name,
77
+ nspowner::regrole::text as owner,
78
+ obj_description(oid, 'pg_namespace') as comment,
79
+ coalesce(
80
+ (
81
+ select json_agg(
82
+ json_build_object(
83
+ 'grantee', case when x.grantee = 0 then 'PUBLIC' else x.grantee::regrole::text end,
84
+ 'privilege', x.privilege_type,
85
+ 'grantable', x.is_grantable
86
+ )
87
+ order by x.grantee, x.privilege_type
88
+ )
89
+ from lateral aclexplode(nspacl) as x(grantor, grantee, privilege_type, is_grantable)
90
+ ), '[]'
91
+ ) as privileges
92
+ from
93
+ pg_catalog.pg_namespace
94
+ left outer join extension_oids e on e.objid = oid
95
+ -- <EXCLUDE_INTERNAL>
96
+ where not nspname like any(array['pg\\_%', 'information\\_schema'])
97
+ and e.objid is null
98
+ -- </EXCLUDE_INTERNAL>
99
+ order by
100
+ 1
101
+ `);
102
+ // Validate and parse each row using the Zod schema
103
+ const validatedRows = schemaRows.map((row: unknown) =>
104
+ schemaPropsSchema.parse(row),
105
+ );
106
+ return validatedRows.map((row: SchemaProps) => new Schema(row));
107
+ }