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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (359) hide show
  1. package/README.md +40 -23
  2. package/dist/cli/app.js +26 -3
  3. package/dist/cli/bin/cli.js +5 -0
  4. package/dist/cli/commands/catalog-export.d.ts +5 -0
  5. package/dist/cli/commands/catalog-export.js +64 -0
  6. package/dist/cli/commands/declarative-apply.d.ts +6 -0
  7. package/dist/cli/commands/declarative-apply.js +288 -0
  8. package/dist/cli/commands/declarative-export.d.ts +5 -0
  9. package/dist/cli/commands/declarative-export.js +245 -0
  10. package/dist/cli/commands/plan.js +19 -6
  11. package/dist/cli/exit-code.d.ts +2 -0
  12. package/dist/cli/exit-code.js +7 -0
  13. package/dist/cli/formatters/tree/tree.js +3 -2
  14. package/dist/cli/utils/apply-display.d.ts +52 -0
  15. package/dist/cli/utils/apply-display.js +183 -0
  16. package/dist/cli/utils/export-display.d.ts +43 -0
  17. package/dist/cli/utils/export-display.js +202 -0
  18. package/dist/cli/utils/resolve-input.d.ts +7 -0
  19. package/dist/cli/utils/resolve-input.js +13 -0
  20. package/dist/core/catalog-export/index.d.ts +11 -0
  21. package/dist/core/catalog-export/index.js +10 -0
  22. package/dist/core/catalog.diff.d.ts +1 -0
  23. package/dist/core/catalog.diff.js +64 -48
  24. package/dist/core/catalog.model.d.ts +14 -1
  25. package/dist/core/catalog.model.js +103 -1
  26. package/dist/core/catalog.snapshot.d.ts +66 -0
  27. package/dist/core/catalog.snapshot.js +206 -0
  28. package/dist/core/declarative-apply/discover-sql.d.ts +18 -0
  29. package/dist/core/declarative-apply/discover-sql.js +86 -0
  30. package/dist/core/declarative-apply/extract-catalog-providers.d.ts +23 -0
  31. package/dist/core/declarative-apply/extract-catalog-providers.js +159 -0
  32. package/dist/core/declarative-apply/index.d.ts +49 -0
  33. package/dist/core/declarative-apply/index.js +134 -0
  34. package/dist/core/declarative-apply/round-apply.d.ts +100 -0
  35. package/dist/core/declarative-apply/round-apply.js +378 -0
  36. package/dist/core/export/file-mapper.d.ts +71 -0
  37. package/dist/core/export/file-mapper.js +474 -0
  38. package/dist/core/export/grouper.d.ts +13 -0
  39. package/dist/core/export/grouper.js +76 -0
  40. package/dist/core/export/index.d.ts +45 -0
  41. package/dist/core/export/index.js +63 -0
  42. package/dist/core/export/types.d.ts +84 -0
  43. package/dist/core/export/types.js +25 -0
  44. package/dist/core/fixtures/empty-catalogs/postgres-15-16-baseline.json +287 -0
  45. package/dist/core/integrations/filter/dsl.d.ts +38 -1
  46. package/dist/core/integrations/filter/dsl.js +20 -2
  47. package/dist/core/integrations/filter/extractors.js +42 -0
  48. package/dist/core/integrations/integration-dsl.d.ts +10 -0
  49. package/dist/core/integrations/supabase.d.ts +8 -0
  50. package/dist/core/integrations/supabase.js +9 -0
  51. package/dist/core/objects/aggregate/aggregate.diff.d.ts +2 -8
  52. package/dist/core/objects/aggregate/aggregate.diff.js +16 -70
  53. package/dist/core/objects/aggregate/aggregate.model.d.ts +8 -8
  54. package/dist/core/objects/aggregate/aggregate.model.js +1 -1
  55. package/dist/core/objects/aggregate/changes/aggregate.create.js +1 -1
  56. package/dist/core/objects/aggregate/changes/aggregate.drop.js +1 -1
  57. package/dist/core/objects/base.privilege-diff.d.ts +38 -13
  58. package/dist/core/objects/base.privilege-diff.js +104 -22
  59. package/dist/core/objects/base.privilege.d.ts +1 -0
  60. package/dist/core/objects/base.privilege.js +9 -2
  61. package/dist/core/objects/collation/collation.diff.d.ts +2 -3
  62. package/dist/core/objects/diff-context.d.ts +15 -0
  63. package/dist/core/objects/diff-context.js +1 -0
  64. package/dist/core/objects/domain/changes/domain.create.js +4 -2
  65. package/dist/core/objects/domain/domain.diff.d.ts +2 -8
  66. package/dist/core/objects/domain/domain.diff.js +16 -77
  67. package/dist/core/objects/domain/domain.model.js +1 -1
  68. package/dist/core/objects/event-trigger/event-trigger.diff.d.ts +2 -3
  69. package/dist/core/objects/foreign-data-wrapper/foreign-data-wrapper/foreign-data-wrapper.diff.d.ts +2 -8
  70. package/dist/core/objects/foreign-data-wrapper/foreign-data-wrapper/foreign-data-wrapper.diff.js +13 -77
  71. package/dist/core/objects/foreign-data-wrapper/foreign-data-wrapper/foreign-data-wrapper.model.js +2 -2
  72. package/dist/core/objects/foreign-data-wrapper/foreign-table/foreign-table.diff.d.ts +2 -8
  73. package/dist/core/objects/foreign-data-wrapper/foreign-table/foreign-table.diff.js +16 -77
  74. package/dist/core/objects/foreign-data-wrapper/foreign-table/foreign-table.model.js +1 -1
  75. package/dist/core/objects/foreign-data-wrapper/server/server.diff.d.ts +2 -8
  76. package/dist/core/objects/foreign-data-wrapper/server/server.diff.js +13 -77
  77. package/dist/core/objects/language/language.diff.d.ts +2 -5
  78. package/dist/core/objects/language/language.diff.js +7 -39
  79. package/dist/core/objects/materialized-view/materialized-view.diff.d.ts +2 -8
  80. package/dist/core/objects/materialized-view/materialized-view.diff.js +16 -158
  81. package/dist/core/objects/materialized-view/materialized-view.model.d.ts +3 -3
  82. package/dist/core/objects/materialized-view/materialized-view.model.js +1 -1
  83. package/dist/core/objects/procedure/changes/procedure.alter.js +12 -12
  84. package/dist/core/objects/procedure/procedure.diff.d.ts +2 -8
  85. package/dist/core/objects/procedure/procedure.diff.js +16 -77
  86. package/dist/core/objects/procedure/procedure.model.d.ts +9 -9
  87. package/dist/core/objects/procedure/procedure.model.js +1 -1
  88. package/dist/core/objects/publication/changes/publication.alter.d.ts +0 -9
  89. package/dist/core/objects/publication/changes/publication.alter.js +0 -14
  90. package/dist/core/objects/publication/changes/publication.types.d.ts +2 -2
  91. package/dist/core/objects/publication/publication.diff.d.ts +2 -3
  92. package/dist/core/objects/publication/publication.diff.js +8 -13
  93. package/dist/core/objects/rls-policy/changes/rls-policy.alter.js +3 -3
  94. package/dist/core/objects/rls-policy/rls-policy.model.d.ts +2 -2
  95. package/dist/core/objects/role/role.diff.js +22 -1
  96. package/dist/core/objects/role/role.model.d.ts +4 -3
  97. package/dist/core/objects/role/role.model.js +118 -12
  98. package/dist/core/objects/rule/rule.model.d.ts +1 -1
  99. package/dist/core/objects/schema/schema.diff.d.ts +2 -8
  100. package/dist/core/objects/schema/schema.diff.js +16 -77
  101. package/dist/core/objects/schema/schema.model.js +1 -1
  102. package/dist/core/objects/sequence/sequence.diff.d.ts +2 -8
  103. package/dist/core/objects/sequence/sequence.diff.js +16 -79
  104. package/dist/core/objects/sequence/sequence.model.js +1 -1
  105. package/dist/core/objects/subscription/subscription.diff.d.ts +2 -3
  106. package/dist/core/objects/table/changes/table.create.js +3 -0
  107. package/dist/core/objects/table/table.diff.d.ts +2 -8
  108. package/dist/core/objects/table/table.diff.js +26 -157
  109. package/dist/core/objects/table/table.model.d.ts +23 -22
  110. package/dist/core/objects/table/table.model.js +1 -1
  111. package/dist/core/objects/trigger/changes/trigger.create.js +2 -4
  112. package/dist/core/objects/trigger/trigger.model.d.ts +8 -0
  113. package/dist/core/objects/trigger/trigger.model.js +11 -0
  114. package/dist/core/objects/type/composite-type/composite-type.diff.d.ts +2 -8
  115. package/dist/core/objects/type/composite-type/composite-type.diff.js +16 -77
  116. package/dist/core/objects/type/composite-type/composite-type.model.d.ts +3 -3
  117. package/dist/core/objects/type/composite-type/composite-type.model.js +2 -1
  118. package/dist/core/objects/type/enum/enum.diff.d.ts +2 -8
  119. package/dist/core/objects/type/enum/enum.diff.js +25 -112
  120. package/dist/core/objects/type/enum/enum.model.js +1 -1
  121. package/dist/core/objects/type/range/changes/range.create.js +6 -3
  122. package/dist/core/objects/type/range/range.diff.d.ts +2 -8
  123. package/dist/core/objects/type/range/range.diff.js +16 -77
  124. package/dist/core/objects/type/range/range.model.js +1 -1
  125. package/dist/core/objects/view/view.diff.d.ts +2 -8
  126. package/dist/core/objects/view/view.diff.js +16 -158
  127. package/dist/core/objects/view/view.model.d.ts +18 -4
  128. package/dist/core/objects/view/view.model.js +3 -13
  129. package/dist/core/plan/apply.js +9 -26
  130. package/dist/core/plan/create.d.ts +19 -6
  131. package/dist/core/plan/create.js +134 -174
  132. package/dist/core/plan/serialize.js +16 -4
  133. package/dist/core/plan/sql-format/fixtures.js +3 -5
  134. package/dist/core/plan/sql-format/keyword-case.js +26 -1
  135. package/dist/core/plan/ssl-config.d.ts +32 -0
  136. package/dist/core/plan/ssl-config.js +115 -0
  137. package/dist/core/plan/types.d.ts +6 -0
  138. package/dist/core/postgres-config.d.ts +14 -0
  139. package/dist/core/postgres-config.js +53 -2
  140. package/dist/core/sort/graph-builder.js +10 -0
  141. package/dist/core/sort/logical-sort.js +31 -23
  142. package/dist/core/test-utils/assert-valid-sql.d.ts +10 -0
  143. package/dist/core/test-utils/assert-valid-sql.js +19 -0
  144. package/dist/index.d.ts +6 -0
  145. package/dist/index.js +6 -1
  146. package/package.json +21 -4
  147. package/src/cli/app.ts +27 -3
  148. package/src/cli/bin/cli.ts +6 -0
  149. package/src/cli/commands/catalog-export.ts +78 -0
  150. package/src/cli/commands/declarative-apply.diagnostics.test.ts +77 -0
  151. package/src/cli/commands/declarative-apply.ts +380 -0
  152. package/src/cli/commands/declarative-export.ts +330 -0
  153. package/src/cli/commands/plan.ts +28 -7
  154. package/src/cli/exit-code.test.ts +19 -0
  155. package/src/cli/exit-code.ts +7 -0
  156. package/src/cli/formatters/tree/tree.ts +3 -2
  157. package/src/cli/utils/apply-display.test.ts +348 -0
  158. package/src/cli/utils/apply-display.ts +238 -0
  159. package/src/cli/utils/export-display.test.ts +103 -0
  160. package/src/cli/utils/export-display.ts +275 -0
  161. package/src/cli/utils/integrations.test.ts +44 -0
  162. package/src/cli/utils/resolve-input.test.ts +38 -0
  163. package/src/cli/utils/resolve-input.ts +17 -0
  164. package/src/core/catalog-export/index.ts +20 -0
  165. package/src/core/catalog.diff.ts +79 -78
  166. package/src/core/catalog.model.test.ts +122 -0
  167. package/src/core/catalog.model.ts +127 -1
  168. package/src/core/catalog.snapshot.test.ts +464 -0
  169. package/src/core/catalog.snapshot.ts +289 -0
  170. package/src/core/declarative-apply/discover-sql.test.ts +103 -0
  171. package/src/core/declarative-apply/discover-sql.ts +107 -0
  172. package/src/core/declarative-apply/extract-catalog-providers.ts +220 -0
  173. package/src/core/declarative-apply/index.test.ts +67 -0
  174. package/src/core/declarative-apply/index.ts +205 -0
  175. package/src/core/declarative-apply/round-apply.test.ts +504 -0
  176. package/src/core/declarative-apply/round-apply.ts +562 -0
  177. package/src/core/expand-replace-dependencies.test.ts +70 -0
  178. package/src/core/export/file-mapper.test.ts +816 -0
  179. package/src/core/export/file-mapper.ts +574 -0
  180. package/src/core/export/grouper.ts +108 -0
  181. package/src/core/export/index.ts +129 -0
  182. package/src/core/export/types.ts +104 -0
  183. package/src/core/fixtures/empty-catalogs/postgres-15-16-baseline.json +287 -0
  184. package/src/core/integrations/filter/dsl.test.ts +211 -0
  185. package/src/core/integrations/filter/dsl.ts +65 -3
  186. package/src/core/integrations/filter/extractors.test.ts +244 -0
  187. package/src/core/integrations/filter/extractors.ts +42 -0
  188. package/src/core/integrations/integration-dsl.ts +10 -0
  189. package/src/core/integrations/serialize/dsl.test.ts +91 -0
  190. package/src/core/integrations/supabase.ts +9 -0
  191. package/src/core/objects/aggregate/aggregate.diff.ts +39 -95
  192. package/src/core/objects/aggregate/aggregate.model.ts +1 -1
  193. package/src/core/objects/aggregate/changes/aggregate.alter.test.ts +3 -1
  194. package/src/core/objects/aggregate/changes/aggregate.comment.test.ts +5 -2
  195. package/src/core/objects/aggregate/changes/aggregate.create.test.ts +6 -3
  196. package/src/core/objects/aggregate/changes/aggregate.create.ts +1 -1
  197. package/src/core/objects/aggregate/changes/aggregate.drop.test.ts +7 -3
  198. package/src/core/objects/aggregate/changes/aggregate.drop.ts +1 -1
  199. package/src/core/objects/aggregate/changes/aggregate.privilege.test.ts +9 -3
  200. package/src/core/objects/base.privilege-diff.ts +178 -30
  201. package/src/core/objects/base.privilege.ts +9 -2
  202. package/src/core/objects/collation/changes/collation.alter.test.ts +7 -2
  203. package/src/core/objects/collation/changes/collation.create.test.ts +7 -2
  204. package/src/core/objects/collation/changes/collation.drop.test.ts +4 -1
  205. package/src/core/objects/collation/collation.diff.test.ts +9 -12
  206. package/src/core/objects/collation/collation.diff.ts +2 -1
  207. package/src/core/objects/diff-context.ts +16 -0
  208. package/src/core/objects/domain/changes/domain.alter.test.ts +28 -9
  209. package/src/core/objects/domain/changes/domain.create.test.ts +32 -2
  210. package/src/core/objects/domain/changes/domain.create.ts +7 -1
  211. package/src/core/objects/domain/changes/domain.drop.test.ts +4 -1
  212. package/src/core/objects/domain/domain.diff.ts +39 -102
  213. package/src/core/objects/domain/domain.model.ts +1 -1
  214. package/src/core/objects/event-trigger/changes/event-trigger.alter.test.ts +10 -3
  215. package/src/core/objects/event-trigger/changes/event-trigger.create.test.ts +4 -1
  216. package/src/core/objects/event-trigger/changes/event-trigger.drop.test.ts +4 -1
  217. package/src/core/objects/event-trigger/event-trigger.diff.test.ts +12 -7
  218. package/src/core/objects/event-trigger/event-trigger.diff.ts +2 -1
  219. package/src/core/objects/extension/changes/extension.alter.test.ts +7 -2
  220. package/src/core/objects/extension/changes/extension.create.test.ts +4 -1
  221. package/src/core/objects/extension/changes/extension.drop.test.ts +4 -1
  222. package/src/core/objects/extension/extension.model.test.ts +98 -0
  223. package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.alter.test.ts +16 -5
  224. package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.create.test.ts +51 -16
  225. package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.drop.test.ts +4 -1
  226. package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/foreign-data-wrapper.diff.test.ts +111 -4
  227. package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/foreign-data-wrapper.diff.ts +31 -101
  228. package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/foreign-data-wrapper.model.ts +2 -2
  229. package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.alter.test.ts +46 -15
  230. package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.create.test.ts +13 -4
  231. package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.drop.test.ts +4 -1
  232. package/src/core/objects/foreign-data-wrapper/foreign-table/foreign-table.diff.ts +39 -102
  233. package/src/core/objects/foreign-data-wrapper/foreign-table/foreign-table.model.ts +1 -1
  234. package/src/core/objects/foreign-data-wrapper/server/changes/server.alter.test.ts +22 -7
  235. package/src/core/objects/foreign-data-wrapper/server/changes/server.create.test.ts +19 -6
  236. package/src/core/objects/foreign-data-wrapper/server/changes/server.drop.test.ts +4 -1
  237. package/src/core/objects/foreign-data-wrapper/server/server.diff.test.ts +95 -0
  238. package/src/core/objects/foreign-data-wrapper/server/server.diff.ts +31 -101
  239. package/src/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.alter.test.ts +13 -4
  240. package/src/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.create.test.ts +16 -5
  241. package/src/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.drop.test.ts +10 -3
  242. package/src/core/objects/index/changes/index.alter.test.ts +13 -4
  243. package/src/core/objects/index/changes/index.create.test.ts +4 -1
  244. package/src/core/objects/index/changes/index.drop.test.ts +4 -1
  245. package/src/core/objects/language/changes/language.alter.test.ts +4 -1
  246. package/src/core/objects/language/changes/language.create.test.ts +4 -1
  247. package/src/core/objects/language/changes/language.drop.test.ts +4 -1
  248. package/src/core/objects/language/language.diff.test.ts +86 -4
  249. package/src/core/objects/language/language.diff.ts +17 -49
  250. package/src/core/objects/materialized-view/changes/materialized-view.alter.test.ts +10 -3
  251. package/src/core/objects/materialized-view/changes/materialized-view.create.test.ts +7 -2
  252. package/src/core/objects/materialized-view/changes/materialized-view.drop.test.ts +4 -1
  253. package/src/core/objects/materialized-view/materialized-view.diff.test.ts +162 -0
  254. package/src/core/objects/materialized-view/materialized-view.diff.ts +41 -191
  255. package/src/core/objects/materialized-view/materialized-view.model.ts +1 -1
  256. package/src/core/objects/procedure/changes/procedure.alter.test.ts +121 -49
  257. package/src/core/objects/procedure/changes/procedure.alter.ts +15 -12
  258. package/src/core/objects/procedure/changes/procedure.create.test.ts +4 -1
  259. package/src/core/objects/procedure/changes/procedure.drop.test.ts +7 -2
  260. package/src/core/objects/procedure/procedure.diff.ts +39 -102
  261. package/src/core/objects/procedure/procedure.model.ts +1 -1
  262. package/src/core/objects/publication/changes/publication.alter.test.ts +15 -21
  263. package/src/core/objects/publication/changes/publication.alter.ts +0 -18
  264. package/src/core/objects/publication/changes/publication.comment.test.ts +5 -2
  265. package/src/core/objects/publication/changes/publication.create.test.ts +5 -2
  266. package/src/core/objects/publication/changes/publication.drop.test.ts +3 -1
  267. package/src/core/objects/publication/changes/publication.types.ts +0 -2
  268. package/src/core/objects/publication/publication.diff.test.ts +24 -19
  269. package/src/core/objects/publication/publication.diff.ts +9 -15
  270. package/src/core/objects/rls-policy/changes/rls-policy.alter.test.ts +31 -14
  271. package/src/core/objects/rls-policy/changes/rls-policy.alter.ts +3 -3
  272. package/src/core/objects/rls-policy/changes/rls-policy.create.test.ts +10 -3
  273. package/src/core/objects/rls-policy/changes/rls-policy.drop.test.ts +4 -1
  274. package/src/core/objects/role/changes/role.alter.test.ts +31 -15
  275. package/src/core/objects/role/changes/role.create.test.ts +6 -2
  276. package/src/core/objects/role/changes/role.drop.test.ts +4 -1
  277. package/src/core/objects/role/role.diff.test.ts +235 -0
  278. package/src/core/objects/role/role.diff.ts +21 -1
  279. package/src/core/objects/role/role.model.ts +122 -14
  280. package/src/core/objects/rule/changes/rule.alter.test.ts +7 -3
  281. package/src/core/objects/rule/changes/rule.comment.test.ts +5 -2
  282. package/src/core/objects/rule/changes/rule.create.test.ts +6 -2
  283. package/src/core/objects/rule/changes/rule.drop.test.ts +3 -1
  284. package/src/core/objects/schema/changes/schema.alter.test.ts +4 -1
  285. package/src/core/objects/schema/changes/schema.create.test.ts +4 -1
  286. package/src/core/objects/schema/changes/schema.drop.test.ts +4 -1
  287. package/src/core/objects/schema/schema.diff.ts +39 -102
  288. package/src/core/objects/schema/schema.model.ts +1 -1
  289. package/src/core/objects/sequence/changes/sequence.alter.test.ts +11 -5
  290. package/src/core/objects/sequence/changes/sequence.create.test.ts +8 -3
  291. package/src/core/objects/sequence/changes/sequence.drop.test.ts +4 -1
  292. package/src/core/objects/sequence/sequence.diff.test.ts +114 -0
  293. package/src/core/objects/sequence/sequence.diff.ts +39 -104
  294. package/src/core/objects/sequence/sequence.model.ts +1 -1
  295. package/src/core/objects/subscription/changes/subscription.alter.test.ts +15 -5
  296. package/src/core/objects/subscription/changes/subscription.comment.test.ts +5 -2
  297. package/src/core/objects/subscription/changes/subscription.create.test.ts +5 -2
  298. package/src/core/objects/subscription/changes/subscription.drop.test.ts +3 -1
  299. package/src/core/objects/subscription/subscription.diff.test.ts +16 -11
  300. package/src/core/objects/subscription/subscription.diff.ts +2 -1
  301. package/src/core/objects/table/changes/table.alter.test.ts +38 -15
  302. package/src/core/objects/table/changes/table.create.test.ts +41 -3
  303. package/src/core/objects/table/changes/table.create.ts +4 -0
  304. package/src/core/objects/table/changes/table.drop.test.ts +3 -1
  305. package/src/core/objects/table/table.diff.test.ts +157 -0
  306. package/src/core/objects/table/table.diff.ts +54 -190
  307. package/src/core/objects/table/table.model.ts +1 -1
  308. package/src/core/objects/trigger/changes/trigger.alter.test.ts +8 -4
  309. package/src/core/objects/trigger/changes/trigger.create.test.ts +5 -1
  310. package/src/core/objects/trigger/changes/trigger.create.ts +7 -4
  311. package/src/core/objects/trigger/changes/trigger.drop.test.ts +5 -1
  312. package/src/core/objects/trigger/trigger.diff.test.ts +1 -0
  313. package/src/core/objects/trigger/trigger.model.ts +12 -0
  314. package/src/core/objects/type/composite-type/changes/composite-type.alter.test.ts +10 -4
  315. package/src/core/objects/type/composite-type/changes/composite-type.create.test.ts +7 -2
  316. package/src/core/objects/type/composite-type/changes/composite-type.drop.test.ts +4 -1
  317. package/src/core/objects/type/composite-type/composite-type.diff.test.ts +78 -0
  318. package/src/core/objects/type/composite-type/composite-type.diff.ts +39 -101
  319. package/src/core/objects/type/composite-type/composite-type.model.ts +2 -1
  320. package/src/core/objects/type/enum/changes/enum.alter.test.ts +14 -5
  321. package/src/core/objects/type/enum/changes/enum.create.test.ts +4 -1
  322. package/src/core/objects/type/enum/changes/enum.drop.test.ts +4 -1
  323. package/src/core/objects/type/enum/enum.diff.test.ts +181 -0
  324. package/src/core/objects/type/enum/enum.diff.ts +58 -146
  325. package/src/core/objects/type/enum/enum.model.ts +1 -1
  326. package/src/core/objects/type/range/changes/range.alter.test.ts +3 -1
  327. package/src/core/objects/type/range/changes/range.create.test.ts +5 -2
  328. package/src/core/objects/type/range/changes/range.create.ts +6 -2
  329. package/src/core/objects/type/range/changes/range.drop.test.ts +3 -1
  330. package/src/core/objects/type/range/range.diff.test.ts +77 -0
  331. package/src/core/objects/type/range/range.diff.ts +39 -101
  332. package/src/core/objects/type/range/range.model.ts +1 -1
  333. package/src/core/objects/view/changes/view.alter.test.ts +8 -3
  334. package/src/core/objects/view/changes/view.create.test.ts +7 -2
  335. package/src/core/objects/view/changes/view.drop.test.ts +4 -1
  336. package/src/core/objects/view/view.diff.test.ts +82 -0
  337. package/src/core/objects/view/view.diff.ts +41 -191
  338. package/src/core/objects/view/view.model.ts +3 -17
  339. package/src/core/plan/apply.ts +9 -27
  340. package/src/core/plan/create.ts +173 -237
  341. package/src/core/plan/serialize.test.ts +317 -0
  342. package/src/core/plan/serialize.ts +18 -4
  343. package/src/core/plan/sql-format/fixtures.ts +2 -5
  344. package/src/core/plan/sql-format/format-lowercase-coverage.test.ts +52 -0
  345. package/src/core/plan/sql-format/format-off.test.ts +14 -17
  346. package/src/core/plan/sql-format/format-pretty-lower-leading.test.ts +27 -22
  347. package/src/core/plan/sql-format/format-pretty-narrow.test.ts +17 -21
  348. package/src/core/plan/sql-format/format-pretty-preserve.test.ts +25 -20
  349. package/src/core/plan/sql-format/format-pretty-upper.test.ts +23 -20
  350. package/src/core/plan/sql-format/keyword-case.ts +36 -1
  351. package/src/core/plan/ssl-config.ts +172 -0
  352. package/src/core/plan/types.ts +6 -0
  353. package/src/core/postgres-config.ts +71 -2
  354. package/src/core/sort/graph-builder.ts +12 -0
  355. package/src/core/sort/logical-sort.test.ts +371 -0
  356. package/src/core/sort/logical-sort.ts +32 -25
  357. package/src/core/sort/topological-sort.test.ts +275 -0
  358. package/src/core/test-utils/assert-valid-sql.ts +20 -0
  359. package/src/index.ts +26 -2
@@ -4,8 +4,17 @@ import {
4
4
  AlterEnumAddValue,
5
5
  AlterEnumChangeOwner,
6
6
  } from "./changes/enum.alter.ts";
7
+ import {
8
+ CreateCommentOnEnum,
9
+ DropCommentOnEnum,
10
+ } from "./changes/enum.comment.ts";
7
11
  import { CreateEnum } from "./changes/enum.create.ts";
8
12
  import { DropEnum } from "./changes/enum.drop.ts";
13
+ import {
14
+ GrantEnumPrivileges,
15
+ RevokeEnumPrivileges,
16
+ RevokeGrantOptionEnumPrivileges,
17
+ } from "./changes/enum.privilege.ts";
9
18
  import { diffEnums } from "./enum.diff.ts";
10
19
  import { Enum, type EnumProps } from "./enum.model.ts";
11
20
 
@@ -188,4 +197,176 @@ describe.concurrent("enum.diff", () => {
188
197
  expect(add?.position?.after).toBe("b");
189
198
  expect(add?.position?.before).toBeUndefined();
190
199
  });
200
+
201
+ test("create with comment emits CreateCommentOnEnum", () => {
202
+ const e = new Enum({
203
+ schema: "public",
204
+ name: "e1",
205
+ owner: "o1",
206
+ labels: [{ label: "a", sort_order: 1 }],
207
+ comment: "my enum",
208
+ privileges: [],
209
+ });
210
+ const changes = diffEnums(testContext, {}, { [e.stableId]: e });
211
+ expect(changes[0]).toBeInstanceOf(CreateEnum);
212
+ expect(changes.some((c) => c instanceof CreateCommentOnEnum)).toBe(true);
213
+ });
214
+
215
+ test("create with privileges that trigger revoke grant option", () => {
216
+ const dpState = new DefaultPrivilegeState({});
217
+ dpState.applyGrant("postgres", "T", null, "role_downgrade", [
218
+ { privilege: "USAGE", grantable: true },
219
+ ]);
220
+ const ctx = { ...testContext, defaultPrivilegeState: dpState };
221
+ const e = new Enum({
222
+ schema: "public",
223
+ name: "e1",
224
+ owner: "o1",
225
+ labels: [{ label: "a", sort_order: 1 }],
226
+ comment: null,
227
+ privileges: [
228
+ { grantee: "role_downgrade", privilege: "USAGE", grantable: false },
229
+ ],
230
+ });
231
+ const changes = diffEnums(ctx, {}, { [e.stableId]: e });
232
+ expect(changes[0]).toBeInstanceOf(CreateEnum);
233
+ expect(
234
+ changes.some((c) => c instanceof RevokeGrantOptionEnumPrivileges),
235
+ ).toBe(true);
236
+ });
237
+
238
+ test("alter with removed labels triggers drop and recreate", () => {
239
+ const main = new Enum({
240
+ schema: "public",
241
+ name: "e1",
242
+ owner: "o1",
243
+ labels: [
244
+ { label: "a", sort_order: 1 },
245
+ { label: "b", sort_order: 2 },
246
+ { label: "c", sort_order: 3 },
247
+ ],
248
+ comment: null,
249
+ privileges: [],
250
+ });
251
+ const branch = new Enum({
252
+ schema: "public",
253
+ name: "e1",
254
+ owner: "o1",
255
+ labels: [
256
+ { label: "a", sort_order: 1 },
257
+ { label: "c", sort_order: 2 },
258
+ ],
259
+ comment: null,
260
+ privileges: [],
261
+ });
262
+ const changes = diffEnums(
263
+ testContext,
264
+ { [main.stableId]: main },
265
+ { [branch.stableId]: branch },
266
+ );
267
+ expect(changes[0]).toBeInstanceOf(DropEnum);
268
+ expect(changes[1]).toBeInstanceOf(CreateEnum);
269
+ });
270
+
271
+ test("alter with removed labels preserves comment and privileges", () => {
272
+ const main = new Enum({
273
+ schema: "public",
274
+ name: "e1",
275
+ owner: "o1",
276
+ labels: [
277
+ { label: "a", sort_order: 1 },
278
+ { label: "b", sort_order: 2 },
279
+ ],
280
+ comment: "my enum",
281
+ privileges: [{ grantee: "role_a", privilege: "USAGE", grantable: false }],
282
+ });
283
+ const branch = new Enum({
284
+ schema: "public",
285
+ name: "e1",
286
+ owner: "o1",
287
+ labels: [{ label: "a", sort_order: 1 }],
288
+ comment: "my enum",
289
+ privileges: [{ grantee: "role_a", privilege: "USAGE", grantable: false }],
290
+ });
291
+ const changes = diffEnums(
292
+ testContext,
293
+ { [main.stableId]: main },
294
+ { [branch.stableId]: branch },
295
+ );
296
+ expect(changes[0]).toBeInstanceOf(DropEnum);
297
+ expect(changes[1]).toBeInstanceOf(CreateEnum);
298
+ expect(changes.some((c) => c instanceof CreateCommentOnEnum)).toBe(true);
299
+ expect(changes.some((c) => c instanceof GrantEnumPrivileges)).toBe(true);
300
+ });
301
+
302
+ test("alter comment emits create and drop comment", () => {
303
+ const main = new Enum({
304
+ schema: "public",
305
+ name: "e1",
306
+ owner: "o1",
307
+ labels: [{ label: "a", sort_order: 1 }],
308
+ comment: null,
309
+ privileges: [],
310
+ });
311
+ const withComment = new Enum({
312
+ schema: "public",
313
+ name: "e1",
314
+ owner: "o1",
315
+ labels: [{ label: "a", sort_order: 1 }],
316
+ comment: "my enum",
317
+ privileges: [],
318
+ });
319
+
320
+ const addComment = diffEnums(
321
+ testContext,
322
+ { [main.stableId]: main },
323
+ { [withComment.stableId]: withComment },
324
+ );
325
+ expect(addComment.some((c) => c instanceof CreateCommentOnEnum)).toBe(true);
326
+
327
+ const dropComment = diffEnums(
328
+ testContext,
329
+ { [withComment.stableId]: withComment },
330
+ { [main.stableId]: main },
331
+ );
332
+ expect(dropComment.some((c) => c instanceof DropCommentOnEnum)).toBe(true);
333
+ });
334
+
335
+ test("alter privileges emits grant, revoke, and revoke grant option", () => {
336
+ const main = new Enum({
337
+ schema: "public",
338
+ name: "e1",
339
+ owner: "o1",
340
+ labels: [{ label: "a", sort_order: 1 }],
341
+ comment: null,
342
+ privileges: [
343
+ { grantee: "role_a", privilege: "USAGE", grantable: false },
344
+ { grantee: "role_b", privilege: "USAGE", grantable: true },
345
+ { grantee: "role_removed", privilege: "USAGE", grantable: false },
346
+ ],
347
+ });
348
+ const branch = new Enum({
349
+ schema: "public",
350
+ name: "e1",
351
+ owner: "o1",
352
+ labels: [{ label: "a", sort_order: 1 }],
353
+ comment: null,
354
+ privileges: [
355
+ { grantee: "role_a", privilege: "USAGE", grantable: true },
356
+ { grantee: "role_b", privilege: "USAGE", grantable: false },
357
+ { grantee: "role_new", privilege: "USAGE", grantable: false },
358
+ ],
359
+ });
360
+
361
+ const changes = diffEnums(
362
+ testContext,
363
+ { [main.stableId]: main },
364
+ { [branch.stableId]: branch },
365
+ );
366
+ expect(changes.some((c) => c instanceof GrantEnumPrivileges)).toBe(true);
367
+ expect(changes.some((c) => c instanceof RevokeEnumPrivileges)).toBe(true);
368
+ expect(
369
+ changes.some((c) => c instanceof RevokeGrantOptionEnumPrivileges),
370
+ ).toBe(true);
371
+ });
191
372
  });
@@ -1,11 +1,10 @@
1
- import type { DefaultPrivilegeState } from "../../base.default-privileges.ts";
2
1
  import { diffObjects } from "../../base.diff.ts";
3
2
  import {
4
3
  diffPrivileges,
4
+ emitObjectPrivilegeChanges,
5
5
  filterPublicBuiltInDefaults,
6
- groupPrivilegesByGrantable,
7
6
  } from "../../base.privilege-diff.ts";
8
- import type { Role } from "../../role/role.model.ts";
7
+ import type { ObjectDiffContext } from "../../diff-context.ts";
9
8
  import {
10
9
  AlterEnumAddValue,
11
10
  AlterEnumChangeOwner,
@@ -33,12 +32,10 @@ import type { Enum } from "./enum.model.ts";
33
32
  * @returns A list of changes to apply to main to make it match branch.
34
33
  */
35
34
  export function diffEnums(
36
- ctx: {
37
- version: number;
38
- currentUser: string;
39
- defaultPrivilegeState: DefaultPrivilegeState;
40
- mainRoles: Record<string, Role>;
41
- },
35
+ ctx: Pick<
36
+ ObjectDiffContext,
37
+ "version" | "currentUser" | "defaultPrivilegeState"
38
+ >,
42
39
  main: Record<string, Enum>,
43
40
  branch: Record<string, Enum>,
44
41
  ): EnumChange[] {
@@ -75,6 +72,10 @@ export function diffEnums(
75
72
  "enum",
76
73
  createdEnum.schema ?? "",
77
74
  );
75
+ const creatorFilteredDefaults =
76
+ createdEnum.owner !== ctx.currentUser
77
+ ? effectiveDefaults.filter((p) => p.grantee !== ctx.currentUser)
78
+ : effectiveDefaults;
78
79
  // Filter out PUBLIC's built-in default USAGE privilege (PostgreSQL grants it automatically)
79
80
  // Reference: https://www.postgresql.org/docs/17/ddl-priv.html Table 5.2
80
81
  // This prevents generating unnecessary "GRANT USAGE TO PUBLIC" statements
@@ -85,57 +86,25 @@ export function diffEnums(
85
86
  // Filter out owner privileges - owner always has ALL privileges implicitly
86
87
  // and shouldn't be compared. Use the enum owner as the reference.
87
88
  const privilegeResults = diffPrivileges(
88
- effectiveDefaults,
89
+ filterPublicBuiltInDefaults("enum", creatorFilteredDefaults),
89
90
  desiredPrivileges,
90
91
  createdEnum.owner,
91
- ctx.mainRoles,
92
92
  );
93
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 GrantEnumPrivileges({
102
- enum: createdEnum,
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 RevokeEnumPrivileges({
118
- enum: createdEnum,
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 RevokeGrantOptionEnumPrivileges({
131
- enum: createdEnum,
132
- grantee,
133
- privilegeNames: result.revokeGrantOption,
134
- version: ctx.version,
135
- }),
136
- );
137
- }
138
- }
94
+ changes.push(
95
+ ...(emitObjectPrivilegeChanges(
96
+ privilegeResults,
97
+ createdEnum,
98
+ createdEnum,
99
+ "enum",
100
+ {
101
+ Grant: GrantEnumPrivileges,
102
+ Revoke: RevokeEnumPrivileges,
103
+ RevokeGrantOption: RevokeGrantOptionEnumPrivileges,
104
+ },
105
+ ctx.version,
106
+ ) as EnumChange[]),
107
+ );
139
108
  }
140
109
 
141
110
  for (const enumId of dropped) {
@@ -173,59 +142,34 @@ export function diffEnums(
173
142
  "enum",
174
143
  branchEnum.schema ?? "",
175
144
  );
145
+ const creatorFilteredDefaults =
146
+ branchEnum.owner !== ctx.currentUser
147
+ ? effectiveDefaults.filter((p) => p.grantee !== ctx.currentUser)
148
+ : effectiveDefaults;
176
149
  const desiredPrivileges = filterPublicBuiltInDefaults(
177
150
  "enum",
178
151
  branchEnum.privileges,
179
152
  );
180
153
  const privilegeResults = diffPrivileges(
181
- effectiveDefaults,
154
+ filterPublicBuiltInDefaults("enum", creatorFilteredDefaults),
182
155
  desiredPrivileges,
183
156
  branchEnum.owner,
184
- ctx.mainRoles,
185
157
  );
186
158
 
187
- for (const [grantee, result] of privilegeResults) {
188
- if (result.grants.length > 0) {
189
- const grantGroups = groupPrivilegesByGrantable(result.grants);
190
- for (const [grantable, list] of grantGroups) {
191
- void grantable;
192
- changes.push(
193
- new GrantEnumPrivileges({
194
- enum: branchEnum,
195
- grantee,
196
- privileges: list,
197
- version: ctx.version,
198
- }),
199
- );
200
- }
201
- }
202
-
203
- if (result.revokes.length > 0) {
204
- const revokeGroups = groupPrivilegesByGrantable(result.revokes);
205
- for (const [grantable, list] of revokeGroups) {
206
- void grantable;
207
- changes.push(
208
- new RevokeEnumPrivileges({
209
- enum: branchEnum,
210
- grantee,
211
- privileges: list,
212
- version: ctx.version,
213
- }),
214
- );
215
- }
216
- }
217
-
218
- if (result.revokeGrantOption.length > 0) {
219
- changes.push(
220
- new RevokeGrantOptionEnumPrivileges({
221
- enum: branchEnum,
222
- grantee,
223
- privilegeNames: result.revokeGrantOption,
224
- version: ctx.version,
225
- }),
226
- );
227
- }
228
- }
159
+ changes.push(
160
+ ...(emitObjectPrivilegeChanges(
161
+ privilegeResults,
162
+ branchEnum,
163
+ branchEnum,
164
+ "enum",
165
+ {
166
+ Grant: GrantEnumPrivileges,
167
+ Revoke: RevokeEnumPrivileges,
168
+ RevokeGrantOption: RevokeGrantOptionEnumPrivileges,
169
+ },
170
+ ctx.version,
171
+ ) as EnumChange[]),
172
+ );
229
173
 
230
174
  continue;
231
175
  }
@@ -270,54 +214,22 @@ export function diffEnums(
270
214
  mainPrivilegesFiltered,
271
215
  branchPrivilegesFiltered,
272
216
  branchEnum.owner,
273
- ctx.mainRoles,
274
217
  );
275
218
 
276
- for (const [grantee, result] of privilegeResults) {
277
- // Generate grant changes
278
- if (result.grants.length > 0) {
279
- const grantGroups = groupPrivilegesByGrantable(result.grants);
280
- for (const [grantable, list] of grantGroups) {
281
- void grantable;
282
- changes.push(
283
- new GrantEnumPrivileges({
284
- enum: branchEnum,
285
- grantee,
286
- privileges: list,
287
- version: ctx.version,
288
- }),
289
- );
290
- }
291
- }
292
-
293
- // Generate revoke changes
294
- if (result.revokes.length > 0) {
295
- const revokeGroups = groupPrivilegesByGrantable(result.revokes);
296
- for (const [grantable, list] of revokeGroups) {
297
- void grantable;
298
- changes.push(
299
- new RevokeEnumPrivileges({
300
- enum: mainEnum,
301
- grantee,
302
- privileges: list,
303
- version: ctx.version,
304
- }),
305
- );
306
- }
307
- }
308
-
309
- // Generate revoke grant option changes
310
- if (result.revokeGrantOption.length > 0) {
311
- changes.push(
312
- new RevokeGrantOptionEnumPrivileges({
313
- enum: mainEnum,
314
- grantee,
315
- privilegeNames: result.revokeGrantOption,
316
- version: ctx.version,
317
- }),
318
- );
319
- }
320
- }
219
+ changes.push(
220
+ ...(emitObjectPrivilegeChanges(
221
+ privilegeResults,
222
+ branchEnum,
223
+ mainEnum,
224
+ "enum",
225
+ {
226
+ Grant: GrantEnumPrivileges,
227
+ Revoke: RevokeEnumPrivileges,
228
+ RevokeGrantOption: RevokeGrantOptionEnumPrivileges,
229
+ },
230
+ ctx.version,
231
+ ) as EnumChange[]),
232
+ );
321
233
 
322
234
  // Note: Enum renaming would also use ALTER TYPE ... RENAME TO ...
323
235
  // But since our Enum model uses 'name' as the identity field,
@@ -145,7 +145,7 @@ select
145
145
  )
146
146
  order by x.grantee, x.privilege_type
147
147
  )
148
- from lateral aclexplode(t.typacl) as x(grantor, grantee, privilege_type, is_grantable)
148
+ from lateral aclexplode(COALESCE(t.typacl, acldefault('T', t.typowner))) as x(grantor, grantee, privilege_type, is_grantable)
149
149
  ), '[]'
150
150
  ) as privileges
151
151
  from
@@ -1,9 +1,10 @@
1
1
  import { describe, expect, test } from "bun:test";
2
+ import { assertValidSql } from "../../../../test-utils/assert-valid-sql.ts";
2
3
  import { Range, type RangeProps } from "../range.model.ts";
3
4
  import { AlterRangeChangeOwner } from "./range.alter.ts";
4
5
 
5
6
  describe.concurrent("range", () => {
6
- test("change owner", () => {
7
+ test("change owner", async () => {
7
8
  const base: RangeProps = {
8
9
  schema: "public",
9
10
  name: "ts_custom",
@@ -22,6 +23,7 @@ describe.concurrent("range", () => {
22
23
  };
23
24
  const main = new Range(base);
24
25
  const change = new AlterRangeChangeOwner({ range: main, owner: "o2" });
26
+ await assertValidSql(change.serialize());
25
27
  expect(change.serialize()).toBe("ALTER TYPE public.ts_custom OWNER TO o2");
26
28
  });
27
29
  });
@@ -1,9 +1,10 @@
1
1
  import { describe, expect, test } from "bun:test";
2
+ import { assertValidSql } from "../../../../test-utils/assert-valid-sql.ts";
2
3
  import { Range } from "../range.model.ts";
3
4
  import { CreateRange } from "./range.create.ts";
4
5
 
5
6
  describe("range", () => {
6
- test("create minimal", () => {
7
+ test("create minimal", async () => {
7
8
  const r = new Range({
8
9
  schema: "public",
9
10
  name: "tsrange_custom",
@@ -21,12 +22,13 @@ describe("range", () => {
21
22
  privileges: [],
22
23
  });
23
24
  const change = new CreateRange({ range: r });
25
+ await assertValidSql(change.serialize());
24
26
  expect(change.serialize()).toBe(
25
27
  "CREATE TYPE public.tsrange_custom AS RANGE (SUBTYPE = int4)",
26
28
  );
27
29
  });
28
30
 
29
- test("create with options", () => {
31
+ test("create with options", async () => {
30
32
  const r = new Range({
31
33
  schema: "public",
32
34
  name: "daterange_custom",
@@ -44,6 +46,7 @@ describe("range", () => {
44
46
  privileges: [],
45
47
  });
46
48
  const change = new CreateRange({ range: r });
49
+ await assertValidSql(change.serialize());
47
50
  expect(change.serialize()).toBe(
48
51
  'CREATE TYPE public.daterange_custom AS RANGE (SUBTYPE = date, SUBTYPE_OPCLASS = public.date_ops, COLLATION = "en_US", CANONICAL = public.canon_fn, SUBTYPE_DIFF = public.diff_fn)',
49
52
  );
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  isUserDefinedTypeSchema,
3
3
  parseProcedureReference,
4
+ parseTypeString,
4
5
  stableId,
5
6
  } from "../../../utils.ts";
6
7
  import type { Range } from "../range.model.ts";
@@ -103,9 +104,12 @@ export class CreateRange extends CreateRangeChange {
103
104
 
104
105
  const opts: string[] = [];
105
106
 
106
- // Required subtype
107
+ // Required subtype (format_type may already return schema-qualified name)
108
+ const alreadyQualified = parseTypeString(this.range.subtype_str);
107
109
  const subtypeQualified =
108
- this.range.subtype_schema && this.range.subtype_schema !== "pg_catalog"
110
+ !alreadyQualified &&
111
+ this.range.subtype_schema &&
112
+ this.range.subtype_schema !== "pg_catalog"
109
113
  ? `${this.range.subtype_schema}.${this.range.subtype_str}`
110
114
  : this.range.subtype_str;
111
115
  opts.push(`SUBTYPE = ${subtypeQualified}`);
@@ -1,9 +1,10 @@
1
1
  import { describe, expect, test } from "bun:test";
2
+ import { assertValidSql } from "../../../../test-utils/assert-valid-sql.ts";
2
3
  import { Range } from "../range.model.ts";
3
4
  import { DropRange } from "./range.drop.ts";
4
5
 
5
6
  describe("range", () => {
6
- test("drop", () => {
7
+ test("drop", async () => {
7
8
  const r = new Range({
8
9
  schema: "public",
9
10
  name: "tsrange_custom",
@@ -21,6 +22,7 @@ describe("range", () => {
21
22
  privileges: [],
22
23
  });
23
24
  const change = new DropRange({ range: r });
25
+ await assertValidSql(change.serialize());
24
26
  expect(change.serialize()).toBe("DROP TYPE public.tsrange_custom");
25
27
  });
26
28
  });
@@ -1,8 +1,17 @@
1
1
  import { describe, expect, test } from "bun:test";
2
2
  import { DefaultPrivilegeState } from "../../base.default-privileges.ts";
3
3
  import { AlterRangeChangeOwner } from "./changes/range.alter.ts";
4
+ import {
5
+ CreateCommentOnRange,
6
+ DropCommentOnRange,
7
+ } from "./changes/range.comment.ts";
4
8
  import { CreateRange } from "./changes/range.create.ts";
5
9
  import { DropRange } from "./changes/range.drop.ts";
10
+ import {
11
+ GrantRangePrivileges,
12
+ RevokeGrantOptionRangePrivileges,
13
+ RevokeRangePrivileges,
14
+ } from "./changes/range.privilege.ts";
6
15
  import { diffRanges } from "./range.diff.ts";
7
16
  import { Range, type RangeProps } from "./range.model.ts";
8
17
 
@@ -67,4 +76,72 @@ describe.concurrent("range.diff", () => {
67
76
  expect(changes[0]).toBeInstanceOf(DropRange);
68
77
  expect(changes[1]).toBeInstanceOf(CreateRange);
69
78
  });
79
+
80
+ test("create with comment emits CreateCommentOnRange", () => {
81
+ const r = new Range({ ...base, comment: "my range" });
82
+ const changes = diffRanges(testContext, {}, { [r.stableId]: r });
83
+ expect(changes[0]).toBeInstanceOf(CreateRange);
84
+ expect(changes.some((c) => c instanceof CreateCommentOnRange)).toBe(true);
85
+ });
86
+
87
+ test("create with privileges emits grant changes", () => {
88
+ const r = new Range({
89
+ ...base,
90
+ privileges: [{ grantee: "role_a", privilege: "USAGE", grantable: false }],
91
+ });
92
+ const changes = diffRanges(testContext, {}, { [r.stableId]: r });
93
+ expect(changes[0]).toBeInstanceOf(CreateRange);
94
+ expect(changes.some((c) => c instanceof GrantRangePrivileges)).toBe(true);
95
+ });
96
+
97
+ test("alter comment emits create and drop comment", () => {
98
+ const main = new Range(base);
99
+ const withComment = new Range({ ...base, comment: "my range" });
100
+
101
+ const addComment = diffRanges(
102
+ testContext,
103
+ { [main.stableId]: main },
104
+ { [withComment.stableId]: withComment },
105
+ );
106
+ expect(addComment.some((c) => c instanceof CreateCommentOnRange)).toBe(
107
+ true,
108
+ );
109
+
110
+ const dropComment = diffRanges(
111
+ testContext,
112
+ { [withComment.stableId]: withComment },
113
+ { [main.stableId]: main },
114
+ );
115
+ expect(dropComment.some((c) => c instanceof DropCommentOnRange)).toBe(true);
116
+ });
117
+
118
+ test("alter privileges emits grant, revoke, and revoke grant option", () => {
119
+ const main = new Range({
120
+ ...base,
121
+ privileges: [
122
+ { grantee: "role_a", privilege: "USAGE", grantable: false },
123
+ { grantee: "role_b", privilege: "USAGE", grantable: true },
124
+ { grantee: "role_removed", privilege: "USAGE", grantable: false },
125
+ ],
126
+ });
127
+ const branch = new Range({
128
+ ...base,
129
+ privileges: [
130
+ { grantee: "role_a", privilege: "USAGE", grantable: true },
131
+ { grantee: "role_b", privilege: "USAGE", grantable: false },
132
+ { grantee: "role_new", privilege: "USAGE", grantable: false },
133
+ ],
134
+ });
135
+
136
+ const changes = diffRanges(
137
+ testContext,
138
+ { [main.stableId]: main },
139
+ { [branch.stableId]: branch },
140
+ );
141
+ expect(changes.some((c) => c instanceof GrantRangePrivileges)).toBe(true);
142
+ expect(changes.some((c) => c instanceof RevokeRangePrivileges)).toBe(true);
143
+ expect(
144
+ changes.some((c) => c instanceof RevokeGrantOptionRangePrivileges),
145
+ ).toBe(true);
146
+ });
70
147
  });