@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,406 @@
1
+ import type { DefaultPrivilegeState } from "../../base.default-privileges.ts";
2
+ import { diffObjects } from "../../base.diff.ts";
3
+ import {
4
+ diffPrivileges,
5
+ filterPublicBuiltInDefaults,
6
+ groupPrivilegesByGrantable,
7
+ } from "../../base.privilege-diff.ts";
8
+ import type { Role } from "../../role/role.model.ts";
9
+ import {
10
+ AlterForeignTableAddColumn,
11
+ AlterForeignTableAlterColumnDropDefault,
12
+ AlterForeignTableAlterColumnDropNotNull,
13
+ AlterForeignTableAlterColumnSetDefault,
14
+ AlterForeignTableAlterColumnSetNotNull,
15
+ AlterForeignTableAlterColumnType,
16
+ AlterForeignTableChangeOwner,
17
+ AlterForeignTableDropColumn,
18
+ AlterForeignTableSetOptions,
19
+ } from "./changes/foreign-table.alter.ts";
20
+ import {
21
+ CreateCommentOnForeignTable,
22
+ DropCommentOnForeignTable,
23
+ } from "./changes/foreign-table.comment.ts";
24
+ import { CreateForeignTable } from "./changes/foreign-table.create.ts";
25
+ import { DropForeignTable } from "./changes/foreign-table.drop.ts";
26
+ import {
27
+ GrantForeignTablePrivileges,
28
+ RevokeForeignTablePrivileges,
29
+ RevokeGrantOptionForeignTablePrivileges,
30
+ } from "./changes/foreign-table.privilege.ts";
31
+ import type { ForeignTableChange } from "./changes/foreign-table.types.ts";
32
+ import type { ForeignTable } from "./foreign-table.model.ts";
33
+
34
+ /**
35
+ * Diff two sets of foreign tables from main and branch catalogs.
36
+ *
37
+ * @param ctx - Context containing version, currentUser, and defaultPrivilegeState
38
+ * @param main - The foreign tables in the main catalog.
39
+ * @param branch - The foreign tables in the branch catalog.
40
+ * @returns A list of changes to apply to main to make it match branch.
41
+ */
42
+ export function diffForeignTables(
43
+ ctx: {
44
+ version: number;
45
+ currentUser: string;
46
+ defaultPrivilegeState: DefaultPrivilegeState;
47
+ mainRoles: Record<string, Role>;
48
+ },
49
+ main: Record<string, ForeignTable>,
50
+ branch: Record<string, ForeignTable>,
51
+ ): ForeignTableChange[] {
52
+ const { created, dropped, altered } = diffObjects(main, branch);
53
+
54
+ const changes: ForeignTableChange[] = [];
55
+
56
+ for (const tableId of created) {
57
+ const createdTable = branch[tableId];
58
+ changes.push(new CreateForeignTable({ foreignTable: createdTable }));
59
+
60
+ // OWNER: If the table should be owned by someone other than the current user,
61
+ // emit ALTER FOREIGN TABLE ... OWNER TO after creation
62
+ if (createdTable.owner !== ctx.currentUser) {
63
+ changes.push(
64
+ new AlterForeignTableChangeOwner({
65
+ foreignTable: createdTable,
66
+ owner: createdTable.owner,
67
+ }),
68
+ );
69
+ }
70
+
71
+ if (createdTable.comment !== null) {
72
+ changes.push(
73
+ new CreateCommentOnForeignTable({ foreignTable: createdTable }),
74
+ );
75
+ }
76
+
77
+ // PRIVILEGES: For created objects, compare against default privileges state
78
+ const effectiveDefaults = ctx.defaultPrivilegeState.getEffectiveDefaults(
79
+ ctx.currentUser,
80
+ "foreign_table",
81
+ createdTable.schema ?? "",
82
+ );
83
+ const desiredPrivileges = filterPublicBuiltInDefaults(
84
+ "foreign_table",
85
+ createdTable.privileges,
86
+ );
87
+ const privilegeResults = diffPrivileges(
88
+ effectiveDefaults,
89
+ desiredPrivileges,
90
+ createdTable.owner,
91
+ ctx.mainRoles,
92
+ );
93
+
94
+ // Generate grant changes
95
+ for (const [grantee, result] of privilegeResults) {
96
+ if (result.grants.length > 0) {
97
+ const grantGroups = groupPrivilegesByGrantable(result.grants);
98
+ for (const [grantable, list] of grantGroups) {
99
+ void grantable;
100
+ changes.push(
101
+ new GrantForeignTablePrivileges({
102
+ foreignTable: createdTable,
103
+ grantee,
104
+ privileges: list,
105
+ version: ctx.version,
106
+ }),
107
+ );
108
+ }
109
+ }
110
+
111
+ // Generate revoke changes
112
+ if (result.revokes.length > 0) {
113
+ const revokeGroups = groupPrivilegesByGrantable(result.revokes);
114
+ for (const [grantable, list] of revokeGroups) {
115
+ void grantable;
116
+ changes.push(
117
+ new RevokeForeignTablePrivileges({
118
+ foreignTable: createdTable,
119
+ grantee,
120
+ privileges: list,
121
+ version: ctx.version,
122
+ }),
123
+ );
124
+ }
125
+ }
126
+
127
+ // Generate revoke grant option changes
128
+ if (result.revokeGrantOption.length > 0) {
129
+ changes.push(
130
+ new RevokeGrantOptionForeignTablePrivileges({
131
+ foreignTable: createdTable,
132
+ grantee,
133
+ privilegeNames: result.revokeGrantOption,
134
+ version: ctx.version,
135
+ }),
136
+ );
137
+ }
138
+ }
139
+ }
140
+
141
+ for (const tableId of dropped) {
142
+ changes.push(new DropForeignTable({ foreignTable: main[tableId] }));
143
+ }
144
+
145
+ for (const tableId of altered) {
146
+ const mainTable = main[tableId];
147
+ const branchTable = branch[tableId];
148
+
149
+ // OWNER
150
+ if (mainTable.owner !== branchTable.owner) {
151
+ changes.push(
152
+ new AlterForeignTableChangeOwner({
153
+ foreignTable: mainTable,
154
+ owner: branchTable.owner,
155
+ }),
156
+ );
157
+ }
158
+
159
+ // SERVER - if changed, need to recreate (not directly alterable)
160
+ if (mainTable.server !== branchTable.server) {
161
+ changes.push(new DropForeignTable({ foreignTable: mainTable }));
162
+ changes.push(new CreateForeignTable({ foreignTable: branchTable }));
163
+ if (branchTable.comment !== null) {
164
+ changes.push(
165
+ new CreateCommentOnForeignTable({ foreignTable: branchTable }),
166
+ );
167
+ }
168
+ continue;
169
+ }
170
+
171
+ // COLUMNS
172
+ const mainColumnsByName = new Map(
173
+ mainTable.columns.map((c) => [c.name, c]),
174
+ );
175
+ const branchColumnsByName = new Map(
176
+ branchTable.columns.map((c) => [c.name, c]),
177
+ );
178
+
179
+ // Added columns
180
+ for (const [name, col] of branchColumnsByName) {
181
+ if (!mainColumnsByName.has(name)) {
182
+ changes.push(
183
+ new AlterForeignTableAddColumn({
184
+ foreignTable: mainTable,
185
+ column: col,
186
+ }),
187
+ );
188
+ }
189
+ }
190
+
191
+ // Dropped columns
192
+ for (const [name] of mainColumnsByName) {
193
+ if (!branchColumnsByName.has(name)) {
194
+ changes.push(
195
+ new AlterForeignTableDropColumn({
196
+ foreignTable: mainTable,
197
+ columnName: name,
198
+ }),
199
+ );
200
+ }
201
+ }
202
+
203
+ // Altered columns
204
+ for (const [name, mainCol] of mainColumnsByName) {
205
+ const branchCol = branchColumnsByName.get(name);
206
+ if (!branchCol) continue;
207
+
208
+ // Type change
209
+ if (mainCol.data_type_str !== branchCol.data_type_str) {
210
+ changes.push(
211
+ new AlterForeignTableAlterColumnType({
212
+ foreignTable: mainTable,
213
+ columnName: name,
214
+ dataType: branchCol.data_type_str,
215
+ }),
216
+ );
217
+ }
218
+
219
+ // Default change
220
+ if (mainCol.default !== branchCol.default) {
221
+ if (branchCol.default === null) {
222
+ changes.push(
223
+ new AlterForeignTableAlterColumnDropDefault({
224
+ foreignTable: mainTable,
225
+ columnName: name,
226
+ }),
227
+ );
228
+ } else {
229
+ changes.push(
230
+ new AlterForeignTableAlterColumnSetDefault({
231
+ foreignTable: mainTable,
232
+ columnName: name,
233
+ defaultValue: branchCol.default,
234
+ }),
235
+ );
236
+ }
237
+ }
238
+
239
+ // NOT NULL change
240
+ if (mainCol.not_null !== branchCol.not_null) {
241
+ if (branchCol.not_null) {
242
+ changes.push(
243
+ new AlterForeignTableAlterColumnSetNotNull({
244
+ foreignTable: mainTable,
245
+ columnName: name,
246
+ }),
247
+ );
248
+ } else {
249
+ changes.push(
250
+ new AlterForeignTableAlterColumnDropNotNull({
251
+ foreignTable: mainTable,
252
+ columnName: name,
253
+ }),
254
+ );
255
+ }
256
+ }
257
+ }
258
+
259
+ // OPTIONS
260
+ const optionsChanged = diffOptions(mainTable.options, branchTable.options);
261
+ if (optionsChanged.length > 0) {
262
+ changes.push(
263
+ new AlterForeignTableSetOptions({
264
+ foreignTable: mainTable,
265
+ options: optionsChanged,
266
+ }),
267
+ );
268
+ }
269
+
270
+ // COMMENT
271
+ if (mainTable.comment !== branchTable.comment) {
272
+ if (branchTable.comment === null) {
273
+ changes.push(
274
+ new DropCommentOnForeignTable({ foreignTable: mainTable }),
275
+ );
276
+ } else {
277
+ changes.push(
278
+ new CreateCommentOnForeignTable({ foreignTable: branchTable }),
279
+ );
280
+ }
281
+ }
282
+
283
+ // PRIVILEGES
284
+ const mainPrivilegesFiltered = filterPublicBuiltInDefaults(
285
+ "foreign_table",
286
+ mainTable.privileges,
287
+ );
288
+ const branchPrivilegesFiltered = filterPublicBuiltInDefaults(
289
+ "foreign_table",
290
+ branchTable.privileges,
291
+ );
292
+ const privilegeResults = diffPrivileges(
293
+ mainPrivilegesFiltered,
294
+ branchPrivilegesFiltered,
295
+ branchTable.owner,
296
+ ctx.mainRoles,
297
+ );
298
+
299
+ for (const [grantee, result] of privilegeResults) {
300
+ // Generate grant changes
301
+ if (result.grants.length > 0) {
302
+ const grantGroups = groupPrivilegesByGrantable(result.grants);
303
+ for (const [grantable, list] of grantGroups) {
304
+ void grantable;
305
+ changes.push(
306
+ new GrantForeignTablePrivileges({
307
+ foreignTable: branchTable,
308
+ grantee,
309
+ privileges: list,
310
+ version: ctx.version,
311
+ }),
312
+ );
313
+ }
314
+ }
315
+
316
+ // Generate revoke changes
317
+ if (result.revokes.length > 0) {
318
+ const revokeGroups = groupPrivilegesByGrantable(result.revokes);
319
+ for (const [grantable, list] of revokeGroups) {
320
+ void grantable;
321
+ changes.push(
322
+ new RevokeForeignTablePrivileges({
323
+ foreignTable: mainTable,
324
+ grantee,
325
+ privileges: list,
326
+ version: ctx.version,
327
+ }),
328
+ );
329
+ }
330
+ }
331
+
332
+ // Generate revoke grant option changes
333
+ if (result.revokeGrantOption.length > 0) {
334
+ changes.push(
335
+ new RevokeGrantOptionForeignTablePrivileges({
336
+ foreignTable: mainTable,
337
+ grantee,
338
+ privilegeNames: result.revokeGrantOption,
339
+ version: ctx.version,
340
+ }),
341
+ );
342
+ }
343
+ }
344
+
345
+ // Note: Foreign table renaming would also use ALTER FOREIGN TABLE ... RENAME TO ...
346
+ // But since our ForeignTable model uses 'name' as the identity field,
347
+ // a name change would be handled as drop + create by diffObjects()
348
+ }
349
+
350
+ return changes;
351
+ }
352
+
353
+ /**
354
+ * Diff options arrays to determine ADD/SET/DROP operations.
355
+ * Options are stored as [key1, value1, key2, value2, ...]
356
+ */
357
+ function diffOptions(
358
+ mainOptions: string[] | null,
359
+ branchOptions: string[] | null,
360
+ ): Array<{ action: "ADD" | "SET" | "DROP"; option: string; value?: string }> {
361
+ const mainMap = new Map<string, string>();
362
+ const branchMap = new Map<string, string>();
363
+
364
+ // Parse main options
365
+ if (mainOptions) {
366
+ for (let i = 0; i < mainOptions.length; i += 2) {
367
+ if (i + 1 < mainOptions.length) {
368
+ mainMap.set(mainOptions[i], mainOptions[i + 1]);
369
+ }
370
+ }
371
+ }
372
+
373
+ // Parse branch options
374
+ if (branchOptions) {
375
+ for (let i = 0; i < branchOptions.length; i += 2) {
376
+ if (i + 1 < branchOptions.length) {
377
+ branchMap.set(branchOptions[i], branchOptions[i + 1]);
378
+ }
379
+ }
380
+ }
381
+
382
+ const changes: Array<{
383
+ action: "ADD" | "SET" | "DROP";
384
+ option: string;
385
+ value?: string;
386
+ }> = [];
387
+
388
+ // Find options to ADD or SET
389
+ for (const [key, value] of branchMap) {
390
+ const mainValue = mainMap.get(key);
391
+ if (mainValue === undefined) {
392
+ changes.push({ action: "ADD", option: key, value });
393
+ } else if (mainValue !== value) {
394
+ changes.push({ action: "SET", option: key, value });
395
+ }
396
+ }
397
+
398
+ // Find options to DROP
399
+ for (const [key] of mainMap) {
400
+ if (!branchMap.has(key)) {
401
+ changes.push({ action: "DROP", option: key });
402
+ }
403
+ }
404
+
405
+ return changes;
406
+ }
@@ -0,0 +1,242 @@
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
+
14
+ /**
15
+ * All properties exposed by CREATE FOREIGN TABLE statement are included in diff output.
16
+ * https://www.postgresql.org/docs/17/sql-createforeigntable.html
17
+ *
18
+ * ALTER FOREIGN TABLE statement can be generated for changes to the following properties:
19
+ * - owner, columns, options
20
+ * https://www.postgresql.org/docs/17/sql-alterforeigntable.html
21
+ *
22
+ * Foreign tables are schema-qualified and similar to regular tables but reference a server.
23
+ */
24
+ const foreignTablePropsSchema = z.object({
25
+ schema: z.string(),
26
+ name: z.string(),
27
+ owner: z.string(),
28
+ server: z.string(),
29
+ options: z.array(z.string()).nullable(),
30
+ comment: z.string().nullable(),
31
+ columns: z.array(columnPropsSchema),
32
+ privileges: z.array(privilegePropsSchema),
33
+ });
34
+
35
+ type ForeignTablePrivilegeProps = PrivilegeProps;
36
+ export type ForeignTableProps = z.infer<typeof foreignTablePropsSchema>;
37
+
38
+ export class ForeignTable extends BasePgModel implements TableLikeObject {
39
+ public readonly schema: ForeignTableProps["schema"];
40
+ public readonly name: ForeignTableProps["name"];
41
+ public readonly owner: ForeignTableProps["owner"];
42
+ public readonly server: ForeignTableProps["server"];
43
+ public readonly options: ForeignTableProps["options"];
44
+ public readonly comment: ForeignTableProps["comment"];
45
+ public readonly columns: ForeignTableProps["columns"];
46
+ public readonly privileges: ForeignTablePrivilegeProps[];
47
+
48
+ constructor(props: ForeignTableProps) {
49
+ super();
50
+
51
+ // Identity fields
52
+ this.schema = props.schema;
53
+ this.name = props.name;
54
+
55
+ // Data fields
56
+ this.owner = props.owner;
57
+ this.server = props.server;
58
+ this.options = props.options;
59
+ this.comment = props.comment;
60
+ this.columns = props.columns;
61
+ this.privileges = props.privileges;
62
+ }
63
+
64
+ get stableId(): `foreignTable:${string}` {
65
+ return `foreignTable:${this.schema}.${this.name}`;
66
+ }
67
+
68
+ get identityFields() {
69
+ return {
70
+ schema: this.schema,
71
+ name: this.name,
72
+ };
73
+ }
74
+
75
+ get dataFields() {
76
+ return {
77
+ owner: this.owner,
78
+ server: this.server,
79
+ options: this.options,
80
+ comment: this.comment,
81
+ columns: this.columns,
82
+ privileges: this.privileges,
83
+ };
84
+ }
85
+
86
+ override stableSnapshot() {
87
+ const normalizeColumns = () =>
88
+ [...this.columns]
89
+ .map((col) => {
90
+ const { position: _pos, ...rest } = col as unknown as Record<
91
+ string,
92
+ unknown
93
+ >;
94
+ return rest;
95
+ })
96
+ .sort((a, b) => {
97
+ const nameA = (a.name as string | undefined) ?? "";
98
+ const nameB = (b.name as string | undefined) ?? "";
99
+ return nameA.localeCompare(nameB);
100
+ });
101
+
102
+ return {
103
+ identity: this.identityFields,
104
+ data: {
105
+ ...this.dataFields,
106
+ columns: normalizeColumns(),
107
+ },
108
+ };
109
+ }
110
+ }
111
+
112
+ export async function extractForeignTables(
113
+ pool: Pool,
114
+ ): Promise<ForeignTable[]> {
115
+ const { rows: tableRows } = await pool.query<ForeignTableProps>(sql`
116
+ with extension_oids as (
117
+ select objid
118
+ from pg_depend d
119
+ where d.refclassid = 'pg_extension'::regclass
120
+ and d.classid = 'pg_class'::regclass
121
+ ), foreign_tables as (
122
+ select
123
+ c.relnamespace::regnamespace::text as schema,
124
+ quote_ident(c.relname) as name,
125
+ c.relowner::regrole::text as owner,
126
+ quote_ident(srv.srvname) as server,
127
+ coalesce(ft.ftoptions, array[]::text[]) as options,
128
+ c.oid as oid
129
+ from
130
+ pg_class c
131
+ inner join pg_foreign_table ft on ft.ftrelid = c.oid
132
+ inner join pg_foreign_server srv on srv.oid = ft.ftserver
133
+ inner join pg_foreign_data_wrapper fdw on fdw.oid = srv.srvfdw
134
+ left outer join extension_oids e1 on c.oid = e1.objid
135
+ where
136
+ c.relkind = 'f'
137
+ and not c.relnamespace::regnamespace::text like any(array['pg\\_%', 'information\\_schema'])
138
+ and e1.objid is null
139
+ and not fdw.fdwname like any(array['pg\\_%'])
140
+ )
141
+ select
142
+ ft.schema,
143
+ ft.name,
144
+ ft.owner,
145
+ ft.server,
146
+ ft.options,
147
+ obj_description(ft.oid, 'pg_class') as comment,
148
+ coalesce(json_agg(
149
+ case when a.attname is not null then
150
+ json_build_object(
151
+ 'name', quote_ident(a.attname),
152
+ 'position', a.attnum,
153
+ 'data_type', a.atttypid::regtype::text,
154
+ 'data_type_str', format_type(a.atttypid, a.atttypmod),
155
+ 'is_custom_type', ty.typnamespace::regnamespace::text not in ('pg_catalog', 'information_schema'),
156
+ 'custom_type_type', case when ty.typnamespace::regnamespace::text not in ('pg_catalog', 'information_schema') then ty.typtype else null end,
157
+ 'custom_type_category', case when ty.typnamespace::regnamespace::text not in ('pg_catalog', 'information_schema') then ty.typcategory else null end,
158
+ 'custom_type_schema', case when ty.typnamespace::regnamespace::text not in ('pg_catalog', 'information_schema') then ty.typnamespace::regnamespace else null end,
159
+ 'custom_type_name', case when ty.typnamespace::regnamespace::text not in ('pg_catalog', 'information_schema') then quote_ident(ty.typname) else null end,
160
+ 'not_null', a.attnotnull,
161
+ 'is_identity', a.attidentity != '',
162
+ 'is_identity_always', a.attidentity = 'a',
163
+ 'is_generated', a.attgenerated != '',
164
+ 'collation', (
165
+ select quote_ident(c2.collname)
166
+ from pg_collation c2, pg_type t2
167
+ where c2.oid = a.attcollation
168
+ and t2.oid = a.atttypid
169
+ and a.attcollation <> t2.typcollation
170
+ ),
171
+ 'default', pg_get_expr(ad.adbin, ad.adrelid),
172
+ 'comment', col_description(a.attrelid, a.attnum)
173
+ )
174
+ end
175
+ order by a.attnum
176
+ ) filter (where a.attname is not null), '[]') as columns,
177
+ coalesce((
178
+ select json_agg(
179
+ json_build_object(
180
+ 'grantee', case when grp.grantee = 0 then 'PUBLIC' else grp.grantee::regrole::text end,
181
+ 'privilege', grp.privilege_type,
182
+ 'grantable', grp.is_grantable,
183
+ 'columns', case when grp.cols is not null and array_length(grp.cols,1) > 0
184
+ then grp.cols
185
+ else null end
186
+ )
187
+ order by grp.grantee, grp.privilege_type
188
+ )
189
+ from (
190
+ select
191
+ x.grantee,
192
+ x.privilege_type,
193
+ bool_or(x.is_grantable) as is_grantable,
194
+ array_agg(quote_ident(src.attname) order by src.attname)
195
+ filter (where src.attname is not null) as cols
196
+ from (
197
+ -- one row for object ACL + one row per column ACL
198
+ select null::name as attname, ft.oid as relacl_oid, (
199
+ select c_rel.relacl from pg_class c_rel where c_rel.oid = ft.oid
200
+ ) as acl
201
+ union all
202
+ select a2.attname, ft.oid as relacl_oid, a2.attacl
203
+ from pg_attribute a2
204
+ where a2.attrelid = ft.oid
205
+ and a2.attnum > 0
206
+ and not a2.attisdropped
207
+ and a2.attacl is not null
208
+ ) as src
209
+ join lateral aclexplode(src.acl) as x(grantor, grantee, privilege_type, is_grantable) on true
210
+ group by x.grantee, x.privilege_type
211
+ ) as grp
212
+ ), '[]') as privileges
213
+ from
214
+ foreign_tables ft
215
+ left join pg_attribute a on a.attrelid = ft.oid and a.attnum > 0 and not a.attisdropped
216
+ left join pg_attrdef ad on a.attrelid = ad.adrelid and a.attnum = ad.adnum
217
+ left join pg_type ty on ty.oid = a.atttypid
218
+ group by
219
+ ft.oid, ft.schema, ft.name, ft.owner, ft.server, ft.options
220
+ order by
221
+ ft.schema, ft.name
222
+ `);
223
+
224
+ // Validate and parse each row using the Zod schema
225
+ const validatedRows = tableRows.map((row: unknown) => {
226
+ const parsed = foreignTablePropsSchema.parse(row);
227
+ // Parse options from PostgreSQL format ['key=value'] to ['key', 'value']
228
+ if (parsed.options && parsed.options.length > 0) {
229
+ const parsedOptions: string[] = [];
230
+ for (const opt of parsed.options) {
231
+ const eqIndex = opt.indexOf("=");
232
+ if (eqIndex > 0) {
233
+ parsedOptions.push(opt.substring(0, eqIndex));
234
+ parsedOptions.push(opt.substring(eqIndex + 1));
235
+ }
236
+ }
237
+ parsed.options = parsedOptions.length > 0 ? parsedOptions : null;
238
+ }
239
+ return parsed;
240
+ });
241
+ return validatedRows.map((row: ForeignTableProps) => new ForeignTable(row));
242
+ }