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

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
@@ -2,6 +2,7 @@ import { describe, expect, test } from "bun:test";
2
2
  import { AlterRoleSetOptions } from "./changes/role.alter.ts";
3
3
  import { CreateRole } from "./changes/role.create.ts";
4
4
  import { DropRole } from "./changes/role.drop.ts";
5
+ import { GrantRoleMembership } from "./changes/role.privilege.ts";
5
6
  import { diffRoles } from "./role.diff.ts";
6
7
  import { Role, type RoleProps } from "./role.model.ts";
7
8
 
@@ -41,4 +42,238 @@ describe.concurrent("role.diff", () => {
41
42
  );
42
43
  expect(changes[0]).toBeInstanceOf(AlterRoleSetOptions);
43
44
  });
45
+
46
+ test("no duplicate membership grants when members have multiple grantors", () => {
47
+ // In PG 16+, pg_auth_members can have multiple rows for the same
48
+ // (roleid, member) pair with different grantors. The Role constructor
49
+ // should deduplicate these so diffRoles doesn't emit duplicate changes.
50
+ const parentRole = new Role({
51
+ ...base,
52
+ name: "postgres",
53
+ members: [
54
+ {
55
+ member: "cli_login_postgres",
56
+ grantor: "postgres",
57
+ admin_option: false,
58
+ inherit_option: false,
59
+ set_option: false,
60
+ },
61
+ {
62
+ member: "cli_login_postgres",
63
+ grantor: "supabase_admin",
64
+ admin_option: false,
65
+ inherit_option: false,
66
+ set_option: false,
67
+ },
68
+ ],
69
+ });
70
+
71
+ // After deduplication, should have only one member entry
72
+ expect(parentRole.members).toHaveLength(1);
73
+ expect(parentRole.members[0].member).toBe("cli_login_postgres");
74
+
75
+ // When diffing, the created role should emit only one GRANT
76
+ const changes = diffRoles(
77
+ { version: 170000 },
78
+ {},
79
+ { [parentRole.stableId]: parentRole },
80
+ );
81
+ const grantChanges = changes.filter(
82
+ (c) => c instanceof GrantRoleMembership,
83
+ );
84
+ expect(grantChanges).toHaveLength(1);
85
+ });
86
+
87
+ test("duplicate members are merged with most permissive options", () => {
88
+ const role = new Role({
89
+ ...base,
90
+ name: "test_role",
91
+ members: [
92
+ {
93
+ member: "member1",
94
+ grantor: "grantor_a",
95
+ admin_option: false,
96
+ inherit_option: false,
97
+ set_option: true,
98
+ },
99
+ {
100
+ member: "member1",
101
+ grantor: "grantor_b",
102
+ admin_option: true,
103
+ inherit_option: false,
104
+ set_option: false,
105
+ },
106
+ ],
107
+ });
108
+
109
+ expect(role.members).toHaveLength(1);
110
+ expect(role.members[0].admin_option).toBe(true);
111
+ expect(role.members[0].set_option).toBe(true);
112
+ });
113
+
114
+ test("no false alter when both sides have duplicate members from different grantors", () => {
115
+ // Both main and branch have the same membership but from different
116
+ // grantors. After deduplication the roles should be equal, producing
117
+ // no changes.
118
+ const main = new Role({
119
+ ...base,
120
+ name: "parent",
121
+ members: [
122
+ {
123
+ member: "child",
124
+ grantor: "postgres",
125
+ admin_option: false,
126
+ inherit_option: false,
127
+ set_option: false,
128
+ },
129
+ {
130
+ member: "child",
131
+ grantor: "supabase_admin",
132
+ admin_option: false,
133
+ inherit_option: false,
134
+ set_option: false,
135
+ },
136
+ ],
137
+ });
138
+ const branch = new Role({
139
+ ...base,
140
+ name: "parent",
141
+ members: [
142
+ {
143
+ member: "child",
144
+ grantor: "another_admin",
145
+ admin_option: false,
146
+ inherit_option: false,
147
+ set_option: false,
148
+ },
149
+ ],
150
+ });
151
+
152
+ const changes = diffRoles(
153
+ { version: 170000 },
154
+ { [main.stableId]: main },
155
+ { [branch.stableId]: branch },
156
+ );
157
+ expect(changes).toHaveLength(0);
158
+ });
159
+
160
+ test("create role skips self-granted membership (member === grantor)", () => {
161
+ // Simulates the auto-created membership when postgres creates a role:
162
+ // PostgreSQL automatically makes the creator a member with grantor=self.
163
+ const role = new Role({
164
+ ...base,
165
+ name: "developer",
166
+ members: [
167
+ {
168
+ member: "postgres",
169
+ grantor: "postgres",
170
+ admin_option: true,
171
+ inherit_option: true,
172
+ set_option: true,
173
+ },
174
+ ],
175
+ });
176
+ const changes = diffRoles(
177
+ { version: 170000 },
178
+ {},
179
+ { [role.stableId]: role },
180
+ );
181
+ // Should only have CreateRole, no GrantRoleMembership for postgres
182
+ expect(changes).toHaveLength(1);
183
+ expect(changes[0]).toBeInstanceOf(CreateRole);
184
+ });
185
+
186
+ test("create role keeps membership when member differs from grantor", () => {
187
+ const role = new Role({
188
+ ...base,
189
+ name: "developer",
190
+ members: [
191
+ {
192
+ member: "app_user",
193
+ grantor: "postgres",
194
+ admin_option: true,
195
+ inherit_option: true,
196
+ set_option: true,
197
+ },
198
+ ],
199
+ });
200
+ const changes = diffRoles(
201
+ { version: 170000 },
202
+ {},
203
+ { [role.stableId]: role },
204
+ );
205
+ // Should have CreateRole + GrantRoleMembership
206
+ expect(changes).toHaveLength(2);
207
+ expect(changes[0]).toBeInstanceOf(CreateRole);
208
+ expect(changes[1]).toBeInstanceOf(GrantRoleMembership);
209
+ });
210
+
211
+ test("create role keeps mixed-grantor membership where not all grantors equal member", () => {
212
+ // Model dedup should prefer the non-self grantor, so diff keeps the membership
213
+ const role = new Role({
214
+ ...base,
215
+ name: "developer",
216
+ members: [
217
+ {
218
+ member: "postgres",
219
+ grantor: "postgres",
220
+ admin_option: false,
221
+ inherit_option: true,
222
+ set_option: true,
223
+ },
224
+ {
225
+ member: "postgres",
226
+ grantor: "supabase_admin",
227
+ admin_option: true,
228
+ inherit_option: true,
229
+ set_option: true,
230
+ },
231
+ ],
232
+ });
233
+ const changes = diffRoles(
234
+ { version: 170000 },
235
+ {},
236
+ { [role.stableId]: role },
237
+ );
238
+ // One grantor is different from member, dedup prefers it → membership kept
239
+ expect(changes).toHaveLength(2);
240
+ expect(changes[0]).toBeInstanceOf(CreateRole);
241
+ expect(changes[1]).toBeInstanceOf(GrantRoleMembership);
242
+ });
243
+
244
+ test("alter role skips granting admin to self-granted membership", () => {
245
+ const mainRole = new Role({
246
+ ...base,
247
+ name: "developer",
248
+ members: [
249
+ {
250
+ member: "postgres",
251
+ grantor: "postgres",
252
+ admin_option: false,
253
+ inherit_option: true,
254
+ set_option: true,
255
+ },
256
+ ],
257
+ });
258
+ const branchRole = new Role({
259
+ ...base,
260
+ name: "developer",
261
+ members: [
262
+ {
263
+ member: "postgres",
264
+ grantor: "postgres",
265
+ admin_option: true,
266
+ inherit_option: true,
267
+ set_option: true,
268
+ },
269
+ ],
270
+ });
271
+ const changes = diffRoles(
272
+ { version: 170000 },
273
+ { [mainRole.stableId]: mainRole },
274
+ { [branchRole.stableId]: branchRole },
275
+ );
276
+ // Should produce no changes — granting admin back to self would fail
277
+ expect(changes).toHaveLength(0);
278
+ });
44
279
  });
@@ -51,8 +51,15 @@ export function diffRoles(
51
51
  if (role.comment !== null) {
52
52
  changes.push(new CreateCommentOnRole({ role }));
53
53
  }
54
- // MEMBERSHIPS: Grant memberships immediately after role creation
54
+ // MEMBERSHIPS: Grant memberships immediately after role creation.
55
+ // Members are already deduplicated by the Role model constructor.
55
56
  for (const membership of role.members) {
57
+ // Skip memberships where the member is the grantor (auto-created by
58
+ // CREATE ROLE — re-granting them, especially WITH ADMIN OPTION, fails
59
+ // with "ADMIN option cannot be granted back to your own grantor").
60
+ if (membership.grantor === membership.member) {
61
+ continue;
62
+ }
56
63
  changes.push(
57
64
  new GrantRoleMembership({
58
65
  role,
@@ -67,6 +74,7 @@ export function diffRoles(
67
74
  }
68
75
  // DEFAULT PRIVILEGES: Grant default privileges immediately after role creation
69
76
  for (const defaultPriv of role.default_privileges) {
77
+ if (defaultPriv.is_implicit) continue;
70
78
  if (defaultPriv.privileges.length === 0) continue;
71
79
  const grantGroups = new Map<
72
80
  boolean,
@@ -208,12 +216,19 @@ export function diffRoles(
208
216
  }
209
217
 
210
218
  // MEMBERSHIPS
219
+ // Members are already deduplicated by the Role model constructor.
211
220
  const mainMembers = new Map(mainRole.members.map((m) => [m.member, m]));
212
221
  const branchMembers = new Map(branchRole.members.map((m) => [m.member, m]));
213
222
 
214
223
  // Find new members to grant
215
224
  for (const [member, membership] of branchMembers) {
216
225
  if (!mainMembers.has(member)) {
226
+ // Skip memberships where the member is the grantor (auto-created by
227
+ // CREATE ROLE — re-granting them fails with "ADMIN option cannot be
228
+ // granted back to your own grantor").
229
+ if (membership.grantor === membership.member) {
230
+ continue;
231
+ }
217
232
  changes.push(
218
233
  new GrantRoleMembership({
219
234
  role: branchRole,
@@ -280,6 +295,11 @@ export function diffRoles(
280
295
  );
281
296
  }
282
297
  if (toGrant.admin || toGrant.inherit || toGrant.set) {
298
+ // Skip granting options back to the grantor (same restriction as
299
+ // for newly created roles).
300
+ if (branchMembership.grantor === branchMembership.member) {
301
+ continue;
302
+ }
283
303
  changes.push(
284
304
  new GrantRoleMembership({
285
305
  role: branchRole,
@@ -18,6 +18,7 @@ const defaultPrivilegeSchema = z.object({
18
18
  privileges: z.array(
19
19
  z.object({ privilege: z.string(), grantable: z.boolean() }),
20
20
  ),
21
+ is_implicit: z.boolean(),
21
22
  });
22
23
 
23
24
  const rolePropsSchema = z.object({
@@ -70,7 +71,7 @@ export class Role extends BasePgModel {
70
71
  this.can_bypass_rls = props.can_bypass_rls;
71
72
  this.config = props.config;
72
73
  this.comment = props.comment;
73
- this.members = props.members;
74
+ this.members = deduplicateMembers(props.members);
74
75
  this.default_privileges = props.default_privileges;
75
76
  }
76
77
 
@@ -95,15 +96,18 @@ export class Role extends BasePgModel {
95
96
  );
96
97
  });
97
98
 
98
- const sortedDefaultPrivs = [...this.default_privileges].map((dp) => ({
99
- ...dp,
100
- privileges: [...dp.privileges].sort((a, b) => {
101
- return (
102
- a.privilege.localeCompare(b.privilege) ||
103
- Number(a.grantable) - Number(b.grantable)
104
- );
105
- }),
106
- }));
99
+ const sortedDefaultPrivs = [...this.default_privileges].map((dp) => {
100
+ const { is_implicit: _, ...rest } = dp;
101
+ return {
102
+ ...rest,
103
+ privileges: [...dp.privileges].sort((a, b) => {
104
+ return (
105
+ a.privilege.localeCompare(b.privilege) ||
106
+ Number(a.grantable) - Number(b.grantable)
107
+ );
108
+ }),
109
+ };
110
+ });
107
111
  sortedDefaultPrivs.sort((a, b) => {
108
112
  return (
109
113
  (a.in_schema ?? "").localeCompare(b.in_schema ?? "") ||
@@ -129,6 +133,48 @@ export class Role extends BasePgModel {
129
133
  }
130
134
  }
131
135
 
136
+ /**
137
+ * Deduplicate members by member name.
138
+ *
139
+ * In PostgreSQL 16+, `pg_auth_members` can have multiple rows for the same
140
+ * (roleid, member) pair with different grantors. Merge them into a single
141
+ * entry per member, combining options with OR so the most permissive wins.
142
+ *
143
+ * When merging, prefer a non-self grantor (grantor !== member) so that
144
+ * downstream code can detect true self-grants (auto-created by CREATE ROLE)
145
+ * by checking `grantor === member`.
146
+ */
147
+ function deduplicateMembers(
148
+ members: RoleProps["members"],
149
+ ): RoleProps["members"] {
150
+ const map = new Map<string, RoleProps["members"][number]>();
151
+ for (const m of members) {
152
+ const existing = map.get(m.member);
153
+ if (existing) {
154
+ // admin_option is always boolean (non-nullable in schema)
155
+ existing.admin_option = existing.admin_option || m.admin_option;
156
+ // inherit_option and set_option are nullish (only available in PG 16+)
157
+ if (m.inherit_option != null) {
158
+ existing.inherit_option =
159
+ (existing.inherit_option ?? false) || m.inherit_option;
160
+ }
161
+ if (m.set_option != null) {
162
+ existing.set_option = (existing.set_option ?? false) || m.set_option;
163
+ }
164
+ // Prefer a non-self grantor so diff can detect true self-grants.
165
+ // Once a non-self grantor is chosen the value is kept (the specific
166
+ // non-self grantor doesn't matter — only the self vs non-self
167
+ // distinction is used downstream).
168
+ if (existing.grantor === existing.member && m.grantor !== m.member) {
169
+ existing.grantor = m.grantor;
170
+ }
171
+ } else {
172
+ map.set(m.member, { ...m });
173
+ }
174
+ }
175
+ return [...map.values()];
176
+ }
177
+
132
178
  export async function extractRoles(pool: Pool): Promise<Role[]> {
133
179
  // Check PostgreSQL version capabilities for membership options
134
180
  const { rows: capabilitiesRows } = await pool.query<{
@@ -202,13 +248,15 @@ export async function extractRoles(pool: Pool): Promise<Role[]> {
202
248
  THEN 'PUBLIC'
203
249
  ELSE s.grantee::regrole::text
204
250
  END,
205
- 'privileges', s.privileges
251
+ 'privileges', s.privileges,
252
+ 'is_implicit', s.is_implicit
206
253
  )
207
254
  ORDER BY s.defaclnamespace NULLS FIRST,
208
255
  s.defaclobjtype,
209
256
  s.grantee
210
257
  )
211
258
  FROM (
259
+ -- Explicit entries from pg_default_acl
212
260
  SELECT
213
261
  d.defaclnamespace,
214
262
  d.defaclobjtype,
@@ -219,12 +267,41 @@ export async function extractRoles(pool: Pool): Promise<Role[]> {
219
267
  'grantable', x.is_grantable
220
268
  )
221
269
  ORDER BY x.privilege_type, x.is_grantable
222
- ) AS privileges
270
+ ) AS privileges,
271
+ false AS is_implicit
223
272
  FROM pg_default_acl d
224
273
  CROSS JOIN LATERAL aclexplode(COALESCE(d.defaclacl, ARRAY[]::aclitem[]))
225
274
  AS x(grantor, grantee, privilege_type, is_grantable)
226
275
  WHERE d.defaclrole = r.oid
227
276
  GROUP BY d.defaclnamespace, d.defaclobjtype, x.grantee
277
+ UNION ALL
278
+ -- Implicit defaults from acldefault() for objtypes without a
279
+ -- global pg_default_acl entry. PostgreSQL applies these implicit
280
+ -- defaults (e.g. PUBLIC gets EXECUTE on functions) when no
281
+ -- explicit ALTER DEFAULT PRIVILEGES has been issued. Including
282
+ -- them lets the diff detect REVOKEs of implicit grants.
283
+ SELECT
284
+ 0 AS defaclnamespace,
285
+ v.t::"char" AS defaclobjtype,
286
+ x.grantee,
287
+ json_agg(
288
+ json_build_object(
289
+ 'privilege', x.privilege_type,
290
+ 'grantable', x.is_grantable
291
+ )
292
+ ORDER BY x.privilege_type, x.is_grantable
293
+ ) AS privileges,
294
+ true AS is_implicit
295
+ FROM (VALUES ('r'), ('S'), ('f'), ('T'), ('n')) AS v(t)
296
+ CROSS JOIN LATERAL aclexplode(acldefault(v.t::"char", r.oid))
297
+ AS x(grantor, grantee, privilege_type, is_grantable)
298
+ WHERE NOT EXISTS (
299
+ SELECT 1 FROM pg_default_acl d2
300
+ WHERE d2.defaclrole = r.oid
301
+ AND d2.defaclobjtype = v.t::"char"
302
+ AND d2.defaclnamespace = 0
303
+ )
304
+ GROUP BY v.t, x.grantee
228
305
  ) AS s
229
306
  ),
230
307
  '[]'
@@ -292,13 +369,15 @@ export async function extractRoles(pool: Pool): Promise<Role[]> {
292
369
  THEN 'PUBLIC'
293
370
  ELSE s.grantee::regrole::text
294
371
  END,
295
- 'privileges', s.privileges
372
+ 'privileges', s.privileges,
373
+ 'is_implicit', s.is_implicit
296
374
  )
297
375
  ORDER BY s.defaclnamespace NULLS FIRST,
298
376
  s.defaclobjtype,
299
377
  s.grantee
300
378
  )
301
379
  FROM (
380
+ -- Explicit entries from pg_default_acl
302
381
  SELECT
303
382
  d.defaclnamespace,
304
383
  d.defaclobjtype,
@@ -309,12 +388,41 @@ export async function extractRoles(pool: Pool): Promise<Role[]> {
309
388
  'grantable', x.is_grantable
310
389
  )
311
390
  ORDER BY x.privilege_type, x.is_grantable
312
- ) AS privileges
391
+ ) AS privileges,
392
+ false AS is_implicit
313
393
  FROM pg_default_acl d
314
394
  CROSS JOIN LATERAL aclexplode(COALESCE(d.defaclacl, ARRAY[]::aclitem[]))
315
395
  AS x(grantor, grantee, privilege_type, is_grantable)
316
396
  WHERE d.defaclrole = r.oid
317
397
  GROUP BY d.defaclnamespace, d.defaclobjtype, x.grantee
398
+ UNION ALL
399
+ -- Implicit defaults from acldefault() for objtypes without a
400
+ -- global pg_default_acl entry. PostgreSQL applies these implicit
401
+ -- defaults (e.g. PUBLIC gets EXECUTE on functions) when no
402
+ -- explicit ALTER DEFAULT PRIVILEGES has been issued. Including
403
+ -- them lets the diff detect REVOKEs of implicit grants.
404
+ SELECT
405
+ 0 AS defaclnamespace,
406
+ v.t::"char" AS defaclobjtype,
407
+ x.grantee,
408
+ json_agg(
409
+ json_build_object(
410
+ 'privilege', x.privilege_type,
411
+ 'grantable', x.is_grantable
412
+ )
413
+ ORDER BY x.privilege_type, x.is_grantable
414
+ ) AS privileges,
415
+ true AS is_implicit
416
+ FROM (VALUES ('r'), ('S'), ('f'), ('T'), ('n')) AS v(t)
417
+ CROSS JOIN LATERAL aclexplode(acldefault(v.t::"char", r.oid))
418
+ AS x(grantor, grantee, privilege_type, is_grantable)
419
+ WHERE NOT EXISTS (
420
+ SELECT 1 FROM pg_default_acl d2
421
+ WHERE d2.defaclrole = r.oid
422
+ AND d2.defaclobjtype = v.t::"char"
423
+ AND d2.defaclnamespace = 0
424
+ )
425
+ GROUP BY v.t, x.grantee
318
426
  ) AS s
319
427
  ),
320
428
  '[]'
@@ -1,4 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
+ import { assertValidSql } from "../../../test-utils/assert-valid-sql.ts";
2
3
  import { stableId } from "../../utils.ts";
3
4
  import { Rule } from "../rule.model.ts";
4
5
  import { ReplaceRule, SetRuleEnabledState } from "./rule.alter.ts";
@@ -28,7 +29,7 @@ const makeRule = (override: Partial<RuleProps> = {}) =>
28
29
  });
29
30
 
30
31
  describe("rule.alter", () => {
31
- test("replace rule serializes using create or replace and tracks dependencies", () => {
32
+ test("replace rule serializes using create or replace and tracks dependencies", async () => {
32
33
  const rule = makeRule({ columns: ["id", "amount"] });
33
34
  const change = new ReplaceRule({ rule });
34
35
 
@@ -39,12 +40,13 @@ describe("rule.alter", () => {
39
40
  stableId.column(rule.schema, rule.table_name, column),
40
41
  ),
41
42
  ]);
43
+ await assertValidSql(change.serialize());
42
44
  expect(change.serialize()).toBe(
43
45
  'CREATE OR REPLACE RULE "my_rule" AS ON INSERT TO public."my_table" DO INSTEAD NOTHING',
44
46
  );
45
47
  });
46
48
 
47
- test("set rule enabled state serializes appropriate clause", () => {
49
+ test("set rule enabled state serializes appropriate clause", async () => {
48
50
  const rule = makeRule({ columns: ["id", "amount"] });
49
51
  const change = new SetRuleEnabledState({ rule, enabled: "D" });
50
52
 
@@ -55,12 +57,13 @@ describe("rule.alter", () => {
55
57
  stableId.column(rule.schema, rule.table_name, column),
56
58
  ),
57
59
  ]);
60
+ await assertValidSql(change.serialize());
58
61
  expect(change.serialize()).toBe(
59
62
  'ALTER TABLE public."my_table" DISABLE RULE "my_rule"',
60
63
  );
61
64
  });
62
65
 
63
- test("set rule enabled state defaults to rule value and supports views", () => {
66
+ test("set rule enabled state defaults to rule value and supports views", async () => {
64
67
  const rule = makeRule({
65
68
  table_name: '"my_view"',
66
69
  relation_kind: "v",
@@ -71,6 +74,7 @@ describe("rule.alter", () => {
71
74
  const change = new SetRuleEnabledState({ rule });
72
75
 
73
76
  expect(change.requires).toEqual([rule.stableId, rule.relationStableId]);
77
+ await assertValidSql(change.serialize());
74
78
  expect(change.serialize()).toBe(
75
79
  'ALTER TABLE public."my_view" ENABLE REPLICA RULE "my_rule"',
76
80
  );
@@ -1,4 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
+ import { assertValidSql } from "../../../test-utils/assert-valid-sql.ts";
2
3
  import { stableId } from "../../utils.ts";
3
4
  import { Rule } from "../rule.model.ts";
4
5
  import { CreateCommentOnRule, DropCommentOnRule } from "./rule.comment.ts";
@@ -28,18 +29,19 @@ const makeRule = (override: Partial<RuleProps> = {}) =>
28
29
  });
29
30
 
30
31
  describe("rule.comment", () => {
31
- test("create comment serializes and tracks dependencies", () => {
32
+ test("create comment serializes and tracks dependencies", async () => {
32
33
  const rule = makeRule({ comment: "rule's description" });
33
34
  const change = new CreateCommentOnRule({ rule });
34
35
 
35
36
  expect(change.creates).toEqual([stableId.comment(rule.stableId)]);
36
37
  expect(change.requires).toEqual([rule.stableId]);
38
+ await assertValidSql(change.serialize());
37
39
  expect(change.serialize()).toBe(
38
40
  "COMMENT ON RULE \"my_rule\" ON public.\"my_table\" IS 'rule''s description'",
39
41
  );
40
42
  });
41
43
 
42
- test("drop comment serializes and tracks dependencies", () => {
44
+ test("drop comment serializes and tracks dependencies", async () => {
43
45
  const rule = makeRule({ comment: "temporary comment" });
44
46
  const change = new DropCommentOnRule({ rule });
45
47
 
@@ -48,6 +50,7 @@ describe("rule.comment", () => {
48
50
  stableId.comment(rule.stableId),
49
51
  rule.stableId,
50
52
  ]);
53
+ await assertValidSql(change.serialize());
51
54
  expect(change.serialize()).toBe(
52
55
  'COMMENT ON RULE "my_rule" ON public."my_table" IS NULL',
53
56
  );
@@ -1,4 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
+ import { assertValidSql } from "../../../test-utils/assert-valid-sql.ts";
2
3
  import { stableId } from "../../utils.ts";
3
4
  import { Rule } from "../rule.model.ts";
4
5
  import { CreateRule } from "./rule.create.ts";
@@ -28,7 +29,7 @@ const makeRule = (override: Partial<RuleProps> = {}) =>
28
29
  });
29
30
 
30
31
  describe("rule.create", () => {
31
- test("serialize rule definition and track dependencies", () => {
32
+ test("serialize rule definition and track dependencies", async () => {
32
33
  const rule = makeRule();
33
34
  const change = new CreateRule({ rule });
34
35
 
@@ -39,12 +40,13 @@ describe("rule.create", () => {
39
40
  stableId.column(rule.schema, rule.table_name, column),
40
41
  ),
41
42
  ]);
43
+ await assertValidSql(change.serialize());
42
44
  expect(change.serialize()).toBe(
43
45
  'CREATE RULE "my_rule" AS ON INSERT TO public."my_table" DO INSTEAD NOTHING',
44
46
  );
45
47
  });
46
48
 
47
- test("serialize rule definition with or replace override", () => {
49
+ test("serialize rule definition with or replace override", async () => {
48
50
  const rule = makeRule({
49
51
  definition:
50
52
  ' CREATE RULE "my_rule" AS ON INSERT TO public."my_table" DO INSTEAD NOTHING ',
@@ -52,6 +54,8 @@ describe("rule.create", () => {
52
54
 
53
55
  const change = new CreateRule({ rule, orReplace: true });
54
56
 
57
+ await assertValidSql(change.serialize());
58
+
55
59
  expect(change.serialize()).toBe(
56
60
  'CREATE OR REPLACE RULE "my_rule" AS ON INSERT TO public."my_table" DO INSTEAD NOTHING',
57
61
  );
@@ -1,4 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
+ import { assertValidSql } from "../../../test-utils/assert-valid-sql.ts";
2
3
  import { Rule } from "../rule.model.ts";
3
4
  import { DropRule } from "./rule.drop.ts";
4
5
 
@@ -27,12 +28,13 @@ const makeRule = (override: Partial<RuleProps> = {}) =>
27
28
  });
28
29
 
29
30
  describe("rule.drop", () => {
30
- test("serialize rule drop and track dependencies", () => {
31
+ test("serialize rule drop and track dependencies", async () => {
31
32
  const rule = makeRule();
32
33
  const change = new DropRule({ rule });
33
34
 
34
35
  expect(change.drops).toEqual([rule.stableId]);
35
36
  expect(change.requires).toEqual([rule.stableId, rule.relationStableId]);
37
+ await assertValidSql(change.serialize());
36
38
  expect(change.serialize()).toBe('DROP RULE "my_rule" ON public."my_table"');
37
39
  });
38
40
  });
@@ -1,10 +1,11 @@
1
1
  import { describe, expect, test } from "bun:test";
2
+ import { assertValidSql } from "../../../test-utils/assert-valid-sql.ts";
2
3
  import { Schema, type SchemaProps } from "../schema.model.ts";
3
4
  import { AlterSchemaChangeOwner } from "./schema.alter.ts";
4
5
 
5
6
  describe.concurrent("schema", () => {
6
7
  describe("alter", () => {
7
- test("change owner", () => {
8
+ test("change owner", async () => {
8
9
  const props: Omit<SchemaProps, "owner"> = {
9
10
  name: "test_schema",
10
11
  comment: null,
@@ -20,6 +21,8 @@ describe.concurrent("schema", () => {
20
21
  owner: "new_owner",
21
22
  });
22
23
 
24
+ await assertValidSql(change.serialize());
25
+
23
26
  expect(change.serialize()).toBe(
24
27
  "ALTER SCHEMA test_schema OWNER TO new_owner",
25
28
  );