@supabase/pg-delta 1.0.0-alpha.21 → 1.0.0-alpha.23

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 (271) hide show
  1. package/dist/core/catalog.diff.js +4 -3
  2. package/dist/core/catalog.model.d.ts +8 -1
  3. package/dist/core/catalog.model.js +10 -8
  4. package/dist/core/expand-replace-dependencies.js +23 -0
  5. package/dist/core/integrations/filter/flatten.js +13 -0
  6. package/dist/core/objects/aggregate/aggregate.diff.js +16 -0
  7. package/dist/core/objects/aggregate/aggregate.model.d.ts +10 -0
  8. package/dist/core/objects/aggregate/aggregate.model.js +19 -1
  9. package/dist/core/objects/aggregate/changes/aggregate.base.d.ts +1 -1
  10. package/dist/core/objects/aggregate/changes/aggregate.security-label.d.ts +28 -0
  11. package/dist/core/objects/aggregate/changes/aggregate.security-label.js +64 -0
  12. package/dist/core/objects/aggregate/changes/aggregate.types.d.ts +2 -1
  13. package/dist/core/objects/base.model.d.ts +8 -0
  14. package/dist/core/objects/base.model.js +2 -0
  15. package/dist/core/objects/domain/changes/domain.base.d.ts +1 -1
  16. package/dist/core/objects/domain/changes/domain.security-label.d.ts +28 -0
  17. package/dist/core/objects/domain/changes/domain.security-label.js +61 -0
  18. package/dist/core/objects/domain/changes/domain.types.d.ts +2 -1
  19. package/dist/core/objects/domain/domain.diff.js +16 -0
  20. package/dist/core/objects/domain/domain.model.d.ts +10 -0
  21. package/dist/core/objects/domain/domain.model.js +19 -1
  22. package/dist/core/objects/event-trigger/changes/event-trigger.base.d.ts +1 -1
  23. package/dist/core/objects/event-trigger/changes/event-trigger.security-label.d.ts +28 -0
  24. package/dist/core/objects/event-trigger/changes/event-trigger.security-label.js +61 -0
  25. package/dist/core/objects/event-trigger/changes/event-trigger.types.d.ts +2 -1
  26. package/dist/core/objects/event-trigger/event-trigger.diff.js +16 -0
  27. package/dist/core/objects/event-trigger/event-trigger.model.d.ts +10 -0
  28. package/dist/core/objects/event-trigger/event-trigger.model.js +19 -1
  29. package/dist/core/objects/extract-with-retry.d.ts +36 -0
  30. package/dist/core/objects/extract-with-retry.js +51 -0
  31. package/dist/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.base.d.ts +1 -1
  32. package/dist/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.security-label.d.ts +28 -0
  33. package/dist/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.security-label.js +61 -0
  34. package/dist/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.types.d.ts +2 -1
  35. package/dist/core/objects/foreign-data-wrapper/foreign-table/foreign-table.diff.js +16 -0
  36. package/dist/core/objects/foreign-data-wrapper/foreign-table/foreign-table.model.d.ts +22 -0
  37. package/dist/core/objects/foreign-data-wrapper/foreign-table/foreign-table.model.js +20 -1
  38. package/dist/core/objects/index/index.diff.js +0 -1
  39. package/dist/core/objects/index/index.model.d.ts +2 -3
  40. package/dist/core/objects/index/index.model.js +17 -6
  41. package/dist/core/objects/materialized-view/changes/materialized-view.base.d.ts +1 -1
  42. package/dist/core/objects/materialized-view/changes/materialized-view.security-label.d.ts +28 -0
  43. package/dist/core/objects/materialized-view/changes/materialized-view.security-label.js +61 -0
  44. package/dist/core/objects/materialized-view/changes/materialized-view.types.d.ts +2 -1
  45. package/dist/core/objects/materialized-view/materialized-view.diff.js +18 -0
  46. package/dist/core/objects/materialized-view/materialized-view.model.d.ts +24 -1
  47. package/dist/core/objects/materialized-view/materialized-view.model.js +40 -5
  48. package/dist/core/objects/procedure/changes/procedure.base.d.ts +1 -1
  49. package/dist/core/objects/procedure/changes/procedure.security-label.d.ts +28 -0
  50. package/dist/core/objects/procedure/changes/procedure.security-label.js +69 -0
  51. package/dist/core/objects/procedure/changes/procedure.types.d.ts +2 -1
  52. package/dist/core/objects/procedure/procedure.diff.js +16 -0
  53. package/dist/core/objects/procedure/procedure.model.d.ts +12 -1
  54. package/dist/core/objects/procedure/procedure.model.js +39 -5
  55. package/dist/core/objects/publication/changes/publication.base.d.ts +1 -1
  56. package/dist/core/objects/publication/changes/publication.security-label.d.ts +28 -0
  57. package/dist/core/objects/publication/changes/publication.security-label.js +61 -0
  58. package/dist/core/objects/publication/changes/publication.types.d.ts +2 -1
  59. package/dist/core/objects/publication/publication.diff.js +16 -0
  60. package/dist/core/objects/publication/publication.model.d.ts +14 -0
  61. package/dist/core/objects/publication/publication.model.js +20 -1
  62. package/dist/core/objects/rls-policy/rls-policy.diff.js +13 -1
  63. package/dist/core/objects/role/changes/role.base.d.ts +1 -1
  64. package/dist/core/objects/role/changes/role.security-label.d.ts +28 -0
  65. package/dist/core/objects/role/changes/role.security-label.js +61 -0
  66. package/dist/core/objects/role/changes/role.types.d.ts +2 -1
  67. package/dist/core/objects/role/role.diff.js +16 -0
  68. package/dist/core/objects/role/role.model.d.ts +10 -0
  69. package/dist/core/objects/role/role.model.js +29 -0
  70. package/dist/core/objects/rule/rule.model.d.ts +2 -1
  71. package/dist/core/objects/rule/rule.model.js +20 -3
  72. package/dist/core/objects/schema/changes/schema.base.d.ts +1 -1
  73. package/dist/core/objects/schema/changes/schema.security-label.d.ts +28 -0
  74. package/dist/core/objects/schema/changes/schema.security-label.js +61 -0
  75. package/dist/core/objects/schema/changes/schema.types.d.ts +2 -1
  76. package/dist/core/objects/schema/schema.diff.js +24 -1
  77. package/dist/core/objects/schema/schema.model.d.ts +10 -0
  78. package/dist/core/objects/schema/schema.model.js +18 -1
  79. package/dist/core/objects/security-label.types.d.ts +20 -0
  80. package/dist/core/objects/security-label.types.js +46 -0
  81. package/dist/core/objects/sequence/changes/sequence.base.d.ts +1 -1
  82. package/dist/core/objects/sequence/changes/sequence.security-label.d.ts +28 -0
  83. package/dist/core/objects/sequence/changes/sequence.security-label.js +61 -0
  84. package/dist/core/objects/sequence/changes/sequence.types.d.ts +2 -1
  85. package/dist/core/objects/sequence/sequence.diff.d.ts +2 -1
  86. package/dist/core/objects/sequence/sequence.diff.js +44 -4
  87. package/dist/core/objects/sequence/sequence.model.d.ts +10 -0
  88. package/dist/core/objects/sequence/sequence.model.js +19 -1
  89. package/dist/core/objects/subscription/changes/subscription.base.d.ts +1 -1
  90. package/dist/core/objects/subscription/changes/subscription.security-label.d.ts +28 -0
  91. package/dist/core/objects/subscription/changes/subscription.security-label.js +61 -0
  92. package/dist/core/objects/subscription/changes/subscription.types.d.ts +2 -1
  93. package/dist/core/objects/subscription/subscription.diff.js +16 -0
  94. package/dist/core/objects/subscription/subscription.model.d.ts +10 -0
  95. package/dist/core/objects/subscription/subscription.model.js +19 -1
  96. package/dist/core/objects/table/changes/table.alter.d.ts +12 -1
  97. package/dist/core/objects/table/changes/table.alter.js +20 -2
  98. package/dist/core/objects/table/changes/table.base.d.ts +1 -1
  99. package/dist/core/objects/table/changes/table.security-label.d.ts +63 -0
  100. package/dist/core/objects/table/changes/table.security-label.js +134 -0
  101. package/dist/core/objects/table/changes/table.types.d.ts +2 -1
  102. package/dist/core/objects/table/table.diff.js +68 -15
  103. package/dist/core/objects/table/table.model.d.ts +36 -1
  104. package/dist/core/objects/table/table.model.js +74 -7
  105. package/dist/core/objects/trigger/trigger.model.d.ts +2 -1
  106. package/dist/core/objects/trigger/trigger.model.js +20 -4
  107. package/dist/core/objects/type/composite-type/changes/composite-type.base.d.ts +1 -1
  108. package/dist/core/objects/type/composite-type/changes/composite-type.security-label.d.ts +28 -0
  109. package/dist/core/objects/type/composite-type/changes/composite-type.security-label.js +61 -0
  110. package/dist/core/objects/type/composite-type/changes/composite-type.types.d.ts +2 -1
  111. package/dist/core/objects/type/composite-type/composite-type.diff.js +16 -0
  112. package/dist/core/objects/type/composite-type/composite-type.model.d.ts +22 -0
  113. package/dist/core/objects/type/composite-type/composite-type.model.js +22 -2
  114. package/dist/core/objects/type/enum/changes/enum.base.d.ts +1 -1
  115. package/dist/core/objects/type/enum/changes/enum.security-label.d.ts +28 -0
  116. package/dist/core/objects/type/enum/changes/enum.security-label.js +61 -0
  117. package/dist/core/objects/type/enum/changes/enum.types.d.ts +2 -1
  118. package/dist/core/objects/type/enum/enum.diff.js +16 -0
  119. package/dist/core/objects/type/enum/enum.model.d.ts +10 -0
  120. package/dist/core/objects/type/enum/enum.model.js +20 -1
  121. package/dist/core/objects/type/range/changes/range.base.d.ts +1 -1
  122. package/dist/core/objects/type/range/changes/range.security-label.d.ts +28 -0
  123. package/dist/core/objects/type/range/changes/range.security-label.js +61 -0
  124. package/dist/core/objects/type/range/changes/range.types.d.ts +2 -1
  125. package/dist/core/objects/type/range/range.diff.js +16 -0
  126. package/dist/core/objects/type/range/range.model.d.ts +10 -0
  127. package/dist/core/objects/type/range/range.model.js +19 -1
  128. package/dist/core/objects/utils.d.ts +2 -0
  129. package/dist/core/objects/utils.js +6 -0
  130. package/dist/core/objects/view/changes/view.base.d.ts +1 -1
  131. package/dist/core/objects/view/changes/view.security-label.d.ts +28 -0
  132. package/dist/core/objects/view/changes/view.security-label.js +61 -0
  133. package/dist/core/objects/view/changes/view.types.d.ts +2 -1
  134. package/dist/core/objects/view/view.diff.js +13 -0
  135. package/dist/core/objects/view/view.model.d.ts +28 -1
  136. package/dist/core/objects/view/view.model.js +40 -5
  137. package/dist/core/plan/create.js +3 -1
  138. package/dist/core/plan/sql-format/fixtures.js +1 -0
  139. package/dist/core/plan/types.d.ts +8 -0
  140. package/dist/core/{post-diff-cycle-breaking.d.ts → post-diff-normalization.d.ts} +8 -1
  141. package/dist/core/post-diff-normalization.js +202 -0
  142. package/dist/core/sort/cycle-breakers.js +1 -1
  143. package/dist/core/sort/utils.d.ts +10 -0
  144. package/dist/core/sort/utils.js +28 -0
  145. package/package.json +1 -1
  146. package/src/core/catalog.diff.ts +4 -2
  147. package/src/core/catalog.model.ts +21 -8
  148. package/src/core/expand-replace-dependencies.test.ts +131 -0
  149. package/src/core/expand-replace-dependencies.ts +24 -0
  150. package/src/core/integrations/filter/dsl.test.ts +27 -0
  151. package/src/core/integrations/filter/flatten.ts +16 -0
  152. package/src/core/objects/aggregate/aggregate.diff.ts +33 -0
  153. package/src/core/objects/aggregate/aggregate.model.ts +22 -1
  154. package/src/core/objects/aggregate/changes/aggregate.base.ts +5 -1
  155. package/src/core/objects/aggregate/changes/aggregate.security-label.ts +99 -0
  156. package/src/core/objects/aggregate/changes/aggregate.types.ts +3 -1
  157. package/src/core/objects/base.model.ts +2 -0
  158. package/src/core/objects/domain/changes/domain.base.ts +5 -1
  159. package/src/core/objects/domain/changes/domain.security-label.test.ts +56 -0
  160. package/src/core/objects/domain/changes/domain.security-label.ts +77 -0
  161. package/src/core/objects/domain/changes/domain.types.ts +3 -1
  162. package/src/core/objects/domain/domain.diff.ts +33 -0
  163. package/src/core/objects/domain/domain.model.ts +22 -1
  164. package/src/core/objects/event-trigger/changes/event-trigger.base.ts +1 -1
  165. package/src/core/objects/event-trigger/changes/event-trigger.security-label.ts +95 -0
  166. package/src/core/objects/event-trigger/changes/event-trigger.types.ts +3 -1
  167. package/src/core/objects/event-trigger/event-trigger.diff.ts +33 -0
  168. package/src/core/objects/event-trigger/event-trigger.model.ts +22 -1
  169. package/src/core/objects/extract-with-retry.test.ts +143 -0
  170. package/src/core/objects/extract-with-retry.ts +87 -0
  171. package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.base.ts +5 -1
  172. package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.security-label.ts +95 -0
  173. package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.types.ts +3 -1
  174. package/src/core/objects/foreign-data-wrapper/foreign-table/foreign-table.diff.ts +33 -0
  175. package/src/core/objects/foreign-data-wrapper/foreign-table/foreign-table.model.ts +24 -1
  176. package/src/core/objects/index/index.diff.ts +0 -1
  177. package/src/core/objects/index/index.model.test.ts +37 -1
  178. package/src/core/objects/index/index.model.ts +25 -6
  179. package/src/core/objects/materialized-view/changes/materialized-view.base.ts +5 -1
  180. package/src/core/objects/materialized-view/changes/materialized-view.security-label.test.ts +63 -0
  181. package/src/core/objects/materialized-view/changes/materialized-view.security-label.ts +95 -0
  182. package/src/core/objects/materialized-view/changes/materialized-view.types.ts +3 -1
  183. package/src/core/objects/materialized-view/materialized-view.diff.ts +37 -0
  184. package/src/core/objects/materialized-view/materialized-view.model.test.ts +93 -0
  185. package/src/core/objects/materialized-view/materialized-view.model.ts +52 -8
  186. package/src/core/objects/procedure/changes/procedure.base.ts +5 -1
  187. package/src/core/objects/procedure/changes/procedure.security-label.ts +105 -0
  188. package/src/core/objects/procedure/changes/procedure.types.ts +3 -1
  189. package/src/core/objects/procedure/procedure.diff.ts +33 -0
  190. package/src/core/objects/procedure/procedure.model.test.ts +117 -0
  191. package/src/core/objects/procedure/procedure.model.ts +51 -7
  192. package/src/core/objects/publication/changes/publication.base.ts +1 -1
  193. package/src/core/objects/publication/changes/publication.security-label.ts +95 -0
  194. package/src/core/objects/publication/changes/publication.types.ts +3 -1
  195. package/src/core/objects/publication/publication.diff.ts +33 -0
  196. package/src/core/objects/publication/publication.model.ts +24 -1
  197. package/src/core/objects/rls-policy/rls-policy.diff.ts +19 -1
  198. package/src/core/objects/role/changes/role.base.ts +2 -1
  199. package/src/core/objects/role/changes/role.security-label.ts +77 -0
  200. package/src/core/objects/role/changes/role.types.ts +3 -1
  201. package/src/core/objects/role/role.diff.ts +33 -0
  202. package/src/core/objects/role/role.model.ts +32 -0
  203. package/src/core/objects/rule/rule.model.test.ts +99 -0
  204. package/src/core/objects/rule/rule.model.ts +28 -4
  205. package/src/core/objects/schema/changes/schema.alter.test.ts +1 -0
  206. package/src/core/objects/schema/changes/schema.base.ts +5 -1
  207. package/src/core/objects/schema/changes/schema.create.test.ts +1 -0
  208. package/src/core/objects/schema/changes/schema.drop.test.ts +1 -0
  209. package/src/core/objects/schema/changes/schema.security-label.test.ts +76 -0
  210. package/src/core/objects/schema/changes/schema.security-label.ts +77 -0
  211. package/src/core/objects/schema/changes/schema.types.ts +3 -1
  212. package/src/core/objects/schema/schema.diff.test.ts +1 -0
  213. package/src/core/objects/schema/schema.diff.ts +43 -1
  214. package/src/core/objects/schema/schema.model.ts +21 -1
  215. package/src/core/objects/security-label.types.test.ts +106 -0
  216. package/src/core/objects/security-label.types.ts +61 -0
  217. package/src/core/objects/sequence/changes/sequence.base.ts +5 -1
  218. package/src/core/objects/sequence/changes/sequence.security-label.test.ts +58 -0
  219. package/src/core/objects/sequence/changes/sequence.security-label.ts +92 -0
  220. package/src/core/objects/sequence/changes/sequence.types.ts +3 -1
  221. package/src/core/objects/sequence/sequence.diff.test.ts +87 -0
  222. package/src/core/objects/sequence/sequence.diff.ts +64 -6
  223. package/src/core/objects/sequence/sequence.model.ts +22 -1
  224. package/src/core/objects/subscription/changes/subscription.base.ts +1 -1
  225. package/src/core/objects/subscription/changes/subscription.security-label.ts +95 -0
  226. package/src/core/objects/subscription/changes/subscription.types.ts +3 -1
  227. package/src/core/objects/subscription/subscription.diff.ts +33 -0
  228. package/src/core/objects/subscription/subscription.model.ts +22 -1
  229. package/src/core/objects/table/changes/table.alter.test.ts +13 -21
  230. package/src/core/objects/table/changes/table.alter.ts +30 -3
  231. package/src/core/objects/table/changes/table.base.ts +5 -1
  232. package/src/core/objects/table/changes/table.security-label.test.ts +140 -0
  233. package/src/core/objects/table/changes/table.security-label.ts +183 -0
  234. package/src/core/objects/table/changes/table.types.ts +3 -1
  235. package/src/core/objects/table/table.diff.ts +111 -19
  236. package/src/core/objects/table/table.model.test.ts +209 -0
  237. package/src/core/objects/table/table.model.ts +94 -9
  238. package/src/core/objects/trigger/trigger.model.test.ts +113 -0
  239. package/src/core/objects/trigger/trigger.model.ts +28 -5
  240. package/src/core/objects/type/composite-type/changes/composite-type.base.ts +5 -1
  241. package/src/core/objects/type/composite-type/changes/composite-type.security-label.ts +95 -0
  242. package/src/core/objects/type/composite-type/changes/composite-type.types.ts +3 -1
  243. package/src/core/objects/type/composite-type/composite-type.diff.ts +33 -0
  244. package/src/core/objects/type/composite-type/composite-type.model.ts +26 -2
  245. package/src/core/objects/type/enum/changes/enum.base.ts +5 -1
  246. package/src/core/objects/type/enum/changes/enum.security-label.ts +77 -0
  247. package/src/core/objects/type/enum/changes/enum.types.ts +3 -1
  248. package/src/core/objects/type/enum/enum.diff.ts +33 -0
  249. package/src/core/objects/type/enum/enum.model.ts +25 -1
  250. package/src/core/objects/type/range/changes/range.base.ts +5 -1
  251. package/src/core/objects/type/range/changes/range.security-label.ts +77 -0
  252. package/src/core/objects/type/range/changes/range.types.ts +3 -1
  253. package/src/core/objects/type/range/range.diff.ts +33 -0
  254. package/src/core/objects/type/range/range.model.ts +22 -1
  255. package/src/core/objects/utils.ts +6 -0
  256. package/src/core/objects/view/changes/view.base.ts +5 -1
  257. package/src/core/objects/view/changes/view.security-label.test.ts +64 -0
  258. package/src/core/objects/view/changes/view.security-label.ts +77 -0
  259. package/src/core/objects/view/changes/view.types.ts +3 -1
  260. package/src/core/objects/view/view.diff.ts +31 -0
  261. package/src/core/objects/view/view.model.test.ts +90 -0
  262. package/src/core/objects/view/view.model.ts +53 -7
  263. package/src/core/plan/create.ts +3 -1
  264. package/src/core/plan/sql-format/fixtures.ts +1 -0
  265. package/src/core/plan/types.ts +8 -0
  266. package/src/core/{post-diff-cycle-breaking.test.ts → post-diff-normalization.test.ts} +168 -4
  267. package/src/core/post-diff-normalization.ts +260 -0
  268. package/src/core/sort/cycle-breakers.ts +1 -1
  269. package/src/core/sort/utils.ts +38 -0
  270. package/dist/core/post-diff-cycle-breaking.js +0 -100
  271. package/src/core/post-diff-cycle-breaking.ts +0 -138
@@ -3,7 +3,11 @@ import type { Range } from "../range.model.ts";
3
3
 
4
4
  abstract class BaseRangeChange extends BaseChange {
5
5
  abstract readonly range: Range;
6
- abstract readonly scope: "object" | "comment" | "privilege";
6
+ abstract readonly scope:
7
+ | "object"
8
+ | "comment"
9
+ | "privilege"
10
+ | "security_label";
7
11
  readonly objectType: "range" = "range";
8
12
  }
9
13
 
@@ -0,0 +1,77 @@
1
+ import { quoteLiteral } from "../../../base.change.ts";
2
+ import type { SecurityLabelProps } from "../../../security-label.types.ts";
3
+ import { stableId } from "../../../utils.ts";
4
+ import type { Range } from "../range.model.ts";
5
+ import { CreateRangeChange, DropRangeChange } from "./range.base.ts";
6
+
7
+ export type SecurityLabelRange =
8
+ | CreateSecurityLabelOnRange
9
+ | DropSecurityLabelOnRange;
10
+
11
+ export class CreateSecurityLabelOnRange extends CreateRangeChange {
12
+ public readonly range: Range;
13
+ public readonly securityLabel: SecurityLabelProps;
14
+ public readonly scope = "security_label" as const;
15
+
16
+ constructor(props: { range: Range; securityLabel: SecurityLabelProps }) {
17
+ super();
18
+ this.range = props.range;
19
+ this.securityLabel = props.securityLabel;
20
+ }
21
+
22
+ get creates() {
23
+ return [
24
+ stableId.securityLabel(this.range.stableId, this.securityLabel.provider),
25
+ ];
26
+ }
27
+
28
+ get requires() {
29
+ return [this.range.stableId];
30
+ }
31
+
32
+ serialize(): string {
33
+ return [
34
+ "SECURITY LABEL FOR",
35
+ this.securityLabel.provider,
36
+ "ON TYPE",
37
+ `${this.range.schema}.${this.range.name}`,
38
+ "IS",
39
+ quoteLiteral(this.securityLabel.label),
40
+ ].join(" ");
41
+ }
42
+ }
43
+
44
+ export class DropSecurityLabelOnRange extends DropRangeChange {
45
+ public readonly range: Range;
46
+ public readonly securityLabel: SecurityLabelProps;
47
+ public readonly scope = "security_label" as const;
48
+
49
+ constructor(props: { range: Range; securityLabel: SecurityLabelProps }) {
50
+ super();
51
+ this.range = props.range;
52
+ this.securityLabel = props.securityLabel;
53
+ }
54
+
55
+ get drops() {
56
+ return [
57
+ stableId.securityLabel(this.range.stableId, this.securityLabel.provider),
58
+ ];
59
+ }
60
+
61
+ get requires() {
62
+ return [
63
+ stableId.securityLabel(this.range.stableId, this.securityLabel.provider),
64
+ this.range.stableId,
65
+ ];
66
+ }
67
+
68
+ serialize(): string {
69
+ return [
70
+ "SECURITY LABEL FOR",
71
+ this.securityLabel.provider,
72
+ "ON TYPE",
73
+ `${this.range.schema}.${this.range.name}`,
74
+ "IS NULL",
75
+ ].join(" ");
76
+ }
77
+ }
@@ -3,6 +3,7 @@ import type { CommentRange } from "./range.comment.ts";
3
3
  import type { CreateRange } from "./range.create.ts";
4
4
  import type { DropRange } from "./range.drop.ts";
5
5
  import type { RangePrivilege } from "./range.privilege.ts";
6
+ import type { SecurityLabelRange } from "./range.security-label.ts";
6
7
 
7
8
  /** Union of all range-related change variants (`objectType: "range"`). @category Change Types */
8
9
  export type RangeChange =
@@ -10,4 +11,5 @@ export type RangeChange =
10
11
  | CommentRange
11
12
  | CreateRange
12
13
  | DropRange
13
- | RangePrivilege;
14
+ | RangePrivilege
15
+ | SecurityLabelRange;
@@ -5,6 +5,7 @@ import {
5
5
  filterPublicBuiltInDefaults,
6
6
  } from "../../base.privilege-diff.ts";
7
7
  import type { ObjectDiffContext } from "../../diff-context.ts";
8
+ import { diffSecurityLabels } from "../../security-label.types.ts";
8
9
  import { hasNonAlterableChanges } from "../../utils.ts";
9
10
  import { AlterRangeChangeOwner } from "./changes/range.alter.ts";
10
11
  import {
@@ -18,6 +19,10 @@ import {
18
19
  RevokeGrantOptionRangePrivileges,
19
20
  RevokeRangePrivileges,
20
21
  } from "./changes/range.privilege.ts";
22
+ import {
23
+ CreateSecurityLabelOnRange,
24
+ DropSecurityLabelOnRange,
25
+ } from "./changes/range.security-label.ts";
21
26
  import type { RangeChange } from "./changes/range.types.ts";
22
27
  import type { Range } from "./range.model.ts";
23
28
 
@@ -59,6 +64,14 @@ export function diffRanges(
59
64
  if (createdRange.comment !== null) {
60
65
  changes.push(new CreateCommentOnRange({ range: createdRange }));
61
66
  }
67
+ for (const label of createdRange.security_labels) {
68
+ changes.push(
69
+ new CreateSecurityLabelOnRange({
70
+ range: createdRange,
71
+ securityLabel: label,
72
+ }),
73
+ );
74
+ }
62
75
 
63
76
  // PRIVILEGES: For created objects, compare against default privileges state
64
77
  // The migration script will run ALTER DEFAULT PRIVILEGES before CREATE (via constraint spec),
@@ -156,6 +169,26 @@ export function diffRanges(
156
169
  }
157
170
  }
158
171
 
172
+ // SECURITY LABELS
173
+ changes.push(
174
+ ...diffSecurityLabels<
175
+ CreateSecurityLabelOnRange | DropSecurityLabelOnRange
176
+ >(
177
+ mainRange.security_labels,
178
+ branchRange.security_labels,
179
+ (securityLabel) =>
180
+ new CreateSecurityLabelOnRange({
181
+ range: branchRange,
182
+ securityLabel,
183
+ }),
184
+ (securityLabel) =>
185
+ new DropSecurityLabelOnRange({
186
+ range: mainRange,
187
+ securityLabel,
188
+ }),
189
+ ),
190
+ );
191
+
159
192
  // PRIVILEGES
160
193
  // Filter out PUBLIC's built-in default USAGE privilege from main catalog
161
194
  // (PostgreSQL grants it automatically, so we shouldn't compare it)
@@ -6,6 +6,10 @@ import {
6
6
  type PrivilegeProps,
7
7
  privilegePropsSchema,
8
8
  } from "../../base.privilege-diff.ts";
9
+ import {
10
+ type SecurityLabelProps,
11
+ securityLabelPropsSchema,
12
+ } from "../../security-label.types.ts";
9
13
 
10
14
  const rangePropsSchema = z.object({
11
15
  schema: z.string(),
@@ -30,6 +34,7 @@ const rangePropsSchema = z.object({
30
34
  subtype_opclass_schema: z.string().nullable(),
31
35
  subtype_opclass_name: z.string().nullable(),
32
36
  privileges: z.array(privilegePropsSchema),
37
+ security_labels: z.array(securityLabelPropsSchema).default([]).optional(),
33
38
  });
34
39
 
35
40
  type RangePrivilegeProps = PrivilegeProps;
@@ -54,6 +59,7 @@ export class Range extends BasePgModel {
54
59
  public readonly subtype_opclass_schema: RangeProps["subtype_opclass_schema"];
55
60
  public readonly subtype_opclass_name: RangeProps["subtype_opclass_name"];
56
61
  public readonly privileges: RangePrivilegeProps[];
62
+ public readonly security_labels: SecurityLabelProps[];
57
63
 
58
64
  constructor(props: RangeProps) {
59
65
  super();
@@ -75,6 +81,7 @@ export class Range extends BasePgModel {
75
81
  this.subtype_opclass_schema = props.subtype_opclass_schema;
76
82
  this.subtype_opclass_name = props.subtype_opclass_name;
77
83
  this.privileges = props.privileges;
84
+ this.security_labels = props.security_labels ?? [];
78
85
  }
79
86
 
80
87
  get stableId(): `type:${string}` {
@@ -102,6 +109,7 @@ export class Range extends BasePgModel {
102
109
  subtype_opclass_name: this.subtype_opclass_name,
103
110
  comment: this.comment,
104
111
  privileges: this.privileges,
112
+ security_labels: this.security_labels,
105
113
  };
106
114
  }
107
115
  }
@@ -166,7 +174,20 @@ select
166
174
  )
167
175
  from lateral aclexplode(COALESCE(t.typacl, acldefault('T', t.typowner))) as x(grantor, grantee, privilege_type, is_grantable)
168
176
  ), '[]'
169
- ) as privileges
177
+ ) as privileges,
178
+ coalesce(
179
+ (
180
+ select json_agg(
181
+ json_build_object('provider', sl.provider, 'label', sl.label)
182
+ order by sl.provider
183
+ )
184
+ from pg_catalog.pg_seclabel sl
185
+ where sl.objoid = t.oid
186
+ and sl.classoid = 'pg_type'::regclass
187
+ and sl.objsubid = 0
188
+ ),
189
+ '[]'::json
190
+ ) as security_labels
170
191
  from pg_catalog.pg_range r
171
192
  join pg_catalog.pg_type t on t.oid = r.rngtypid
172
193
  join pg_catalog.pg_type subt on subt.oid = r.rngsubtype
@@ -71,9 +71,15 @@ export const stableId = {
71
71
  constraint(schema: string, table: string, constraint: string) {
72
72
  return `constraint:${schema}.${table}.${constraint}` as const;
73
73
  },
74
+ index(schema: string, table: string, indexName: string) {
75
+ return `index:${schema}.${table}.${indexName}` as const;
76
+ },
74
77
  comment(objectStableId: string) {
75
78
  return `comment:${objectStableId}` as const;
76
79
  },
80
+ securityLabel(objectStableId: string, provider: string) {
81
+ return `securityLabel:${objectStableId}::provider:${provider}` as const;
82
+ },
77
83
  role(role: string) {
78
84
  return `role:${role}` as const;
79
85
  },
@@ -3,7 +3,11 @@ import type { View } from "../view.model.ts";
3
3
 
4
4
  abstract class BaseViewChange extends BaseChange {
5
5
  abstract readonly view: View;
6
- abstract readonly scope: "object" | "comment" | "privilege";
6
+ abstract readonly scope:
7
+ | "object"
8
+ | "comment"
9
+ | "privilege"
10
+ | "security_label";
7
11
  readonly objectType: "view" = "view";
8
12
  }
9
13
 
@@ -0,0 +1,64 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { assertValidSql } from "../../../test-utils/assert-valid-sql.ts";
3
+ import { stableId } from "../../utils.ts";
4
+ import { View, type ViewProps } from "../view.model.ts";
5
+ import {
6
+ CreateSecurityLabelOnView,
7
+ DropSecurityLabelOnView,
8
+ } from "./view.security-label.ts";
9
+
10
+ const makeView = (): View =>
11
+ new View({
12
+ schema: "public",
13
+ name: "v",
14
+ definition: "SELECT 1",
15
+ row_security: false,
16
+ force_row_security: false,
17
+ has_indexes: false,
18
+ has_rules: false,
19
+ has_triggers: false,
20
+ has_subclasses: false,
21
+ is_populated: true,
22
+ replica_identity: "d",
23
+ is_partition: false,
24
+ options: null,
25
+ partition_bound: null,
26
+ owner: "postgres",
27
+ comment: null,
28
+ columns: [],
29
+ privileges: [],
30
+ } as ViewProps);
31
+
32
+ describe("view.security-label", () => {
33
+ test("create serializes and tracks dependencies", async () => {
34
+ const view = makeView();
35
+ const change = new CreateSecurityLabelOnView({
36
+ view,
37
+ securityLabel: { provider: "dummy", label: "classified" },
38
+ });
39
+ expect(change.scope).toBe("security_label");
40
+ expect(change.creates).toEqual([
41
+ stableId.securityLabel(view.stableId, "dummy"),
42
+ ]);
43
+ expect(change.requires).toEqual([view.stableId]);
44
+ await assertValidSql(change.serialize());
45
+ expect(change.serialize()).toBe(
46
+ "SECURITY LABEL FOR dummy ON VIEW public.v IS 'classified'",
47
+ );
48
+ });
49
+
50
+ test("drop serializes to IS NULL", async () => {
51
+ const view = makeView();
52
+ const change = new DropSecurityLabelOnView({
53
+ view,
54
+ securityLabel: { provider: "dummy", label: "classified" },
55
+ });
56
+ expect(change.drops).toEqual([
57
+ stableId.securityLabel(view.stableId, "dummy"),
58
+ ]);
59
+ await assertValidSql(change.serialize());
60
+ expect(change.serialize()).toBe(
61
+ "SECURITY LABEL FOR dummy ON VIEW public.v IS NULL",
62
+ );
63
+ });
64
+ });
@@ -0,0 +1,77 @@
1
+ import { quoteLiteral } from "../../base.change.ts";
2
+ import type { SecurityLabelProps } from "../../security-label.types.ts";
3
+ import { stableId } from "../../utils.ts";
4
+ import type { View } from "../view.model.ts";
5
+ import { CreateViewChange, DropViewChange } from "./view.base.ts";
6
+
7
+ export type SecurityLabelView =
8
+ | CreateSecurityLabelOnView
9
+ | DropSecurityLabelOnView;
10
+
11
+ export class CreateSecurityLabelOnView extends CreateViewChange {
12
+ public readonly view: View;
13
+ public readonly securityLabel: SecurityLabelProps;
14
+ public readonly scope = "security_label" as const;
15
+
16
+ constructor(props: { view: View; securityLabel: SecurityLabelProps }) {
17
+ super();
18
+ this.view = props.view;
19
+ this.securityLabel = props.securityLabel;
20
+ }
21
+
22
+ get creates() {
23
+ return [
24
+ stableId.securityLabel(this.view.stableId, this.securityLabel.provider),
25
+ ];
26
+ }
27
+
28
+ get requires() {
29
+ return [this.view.stableId];
30
+ }
31
+
32
+ serialize(): string {
33
+ return [
34
+ "SECURITY LABEL FOR",
35
+ this.securityLabel.provider,
36
+ "ON VIEW",
37
+ `${this.view.schema}.${this.view.name}`,
38
+ "IS",
39
+ quoteLiteral(this.securityLabel.label),
40
+ ].join(" ");
41
+ }
42
+ }
43
+
44
+ export class DropSecurityLabelOnView extends DropViewChange {
45
+ public readonly view: View;
46
+ public readonly securityLabel: SecurityLabelProps;
47
+ public readonly scope = "security_label" as const;
48
+
49
+ constructor(props: { view: View; securityLabel: SecurityLabelProps }) {
50
+ super();
51
+ this.view = props.view;
52
+ this.securityLabel = props.securityLabel;
53
+ }
54
+
55
+ get drops() {
56
+ return [
57
+ stableId.securityLabel(this.view.stableId, this.securityLabel.provider),
58
+ ];
59
+ }
60
+
61
+ get requires() {
62
+ return [
63
+ stableId.securityLabel(this.view.stableId, this.securityLabel.provider),
64
+ this.view.stableId,
65
+ ];
66
+ }
67
+
68
+ serialize(): string {
69
+ return [
70
+ "SECURITY LABEL FOR",
71
+ this.securityLabel.provider,
72
+ "ON VIEW",
73
+ `${this.view.schema}.${this.view.name}`,
74
+ "IS NULL",
75
+ ].join(" ");
76
+ }
77
+ }
@@ -3,6 +3,7 @@ import type { CommentView } from "./view.comment.ts";
3
3
  import type { CreateView } from "./view.create.ts";
4
4
  import type { DropView } from "./view.drop.ts";
5
5
  import type { ViewPrivilege } from "./view.privilege.ts";
6
+ import type { SecurityLabelView } from "./view.security-label.ts";
6
7
 
7
8
  /** Union of all view-related change variants (`objectType: "view"`). @category Change Types */
8
9
  export type ViewChange =
@@ -10,4 +11,5 @@ export type ViewChange =
10
11
  | CommentView
11
12
  | CreateView
12
13
  | DropView
13
- | ViewPrivilege;
14
+ | ViewPrivilege
15
+ | SecurityLabelView;
@@ -5,6 +5,7 @@ import {
5
5
  emitColumnPrivilegeChanges,
6
6
  } from "../base.privilege-diff.ts";
7
7
  import type { ObjectDiffContext } from "../diff-context.ts";
8
+ import { diffSecurityLabels } from "../security-label.types.ts";
8
9
  import { deepEqual, hasNonAlterableChanges } from "../utils.ts";
9
10
  import {
10
11
  AlterViewChangeOwner,
@@ -22,6 +23,10 @@ import {
22
23
  RevokeGrantOptionViewPrivileges,
23
24
  RevokeViewPrivileges,
24
25
  } from "./changes/view.privilege.ts";
26
+ import {
27
+ CreateSecurityLabelOnView,
28
+ DropSecurityLabelOnView,
29
+ } from "./changes/view.security-label.ts";
25
30
  import type { ViewChange } from "./changes/view.types.ts";
26
31
  import type { View } from "./view.model.ts";
27
32
 
@@ -57,6 +62,12 @@ export function diffViews(
57
62
  changes.push(new CreateCommentOnView({ view }));
58
63
  }
59
64
 
65
+ for (const label of view.security_labels) {
66
+ changes.push(
67
+ new CreateSecurityLabelOnView({ view, securityLabel: label }),
68
+ );
69
+ }
70
+
60
71
  // PRIVILEGES: For created objects, compare against default privileges state
61
72
  // The migration script will run ALTER DEFAULT PRIVILEGES before CREATE (via constraint spec),
62
73
  // so objects are created with the default privileges state in effect.
@@ -195,6 +206,26 @@ export function diffViews(
195
206
  }
196
207
  }
197
208
 
209
+ // SECURITY LABELS
210
+ changes.push(
211
+ ...diffSecurityLabels<
212
+ CreateSecurityLabelOnView | DropSecurityLabelOnView
213
+ >(
214
+ mainView.security_labels,
215
+ branchView.security_labels,
216
+ (securityLabel) =>
217
+ new CreateSecurityLabelOnView({
218
+ view: branchView,
219
+ securityLabel,
220
+ }),
221
+ (securityLabel) =>
222
+ new DropSecurityLabelOnView({
223
+ view: mainView,
224
+ securityLabel,
225
+ }),
226
+ ),
227
+ );
228
+
198
229
  // Note: View renaming would also use ALTER VIEW ... RENAME TO ...
199
230
  // But since our View model uses 'name' as the identity field,
200
231
  // a name change would be handled as drop + create by diffObjects()
@@ -0,0 +1,90 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import type { Pool } from "pg";
3
+ import { extractViews, View } from "./view.model.ts";
4
+
5
+ const baseRow = {
6
+ schema: "public",
7
+ row_security: false,
8
+ force_row_security: false,
9
+ has_indexes: false,
10
+ has_rules: false,
11
+ has_triggers: false,
12
+ has_subclasses: false,
13
+ is_populated: true,
14
+ replica_identity: "d" as const,
15
+ is_partition: false,
16
+ options: null,
17
+ partition_bound: null,
18
+ owner: "postgres",
19
+ comment: null,
20
+ columns: [],
21
+ privileges: [],
22
+ };
23
+
24
+ const mockPool = (rows: unknown[]): Pool =>
25
+ ({ query: async () => ({ rows }) }) as unknown as Pool;
26
+
27
+ const mockPoolSequence = (...attempts: unknown[][]): Pool => {
28
+ let i = 0;
29
+ return {
30
+ query: async () => ({
31
+ rows: attempts[Math.min(i++, attempts.length - 1)],
32
+ }),
33
+ } as unknown as Pool;
34
+ };
35
+
36
+ const NO_BACKOFF = { backoffMs: 0 } as const;
37
+
38
+ describe("extractViews", () => {
39
+ test("skips rows where pg_get_viewdef returned NULL after exhausting retries", async () => {
40
+ const views = await extractViews(
41
+ mockPool([
42
+ {
43
+ ...baseRow,
44
+ name: '"good_view"',
45
+ definition: "SELECT 1",
46
+ },
47
+ { ...baseRow, name: '"orphan_view"', definition: null },
48
+ ]),
49
+ NO_BACKOFF,
50
+ );
51
+
52
+ expect(views).toHaveLength(1);
53
+ expect(views[0]).toBeInstanceOf(View);
54
+ expect(views[0]?.name).toBe('"good_view"');
55
+ expect(views[0]?.definition).toBe("SELECT 1");
56
+ });
57
+
58
+ test("does not throw ZodError when the only row has a null definition", async () => {
59
+ await expect(
60
+ extractViews(
61
+ mockPool([{ ...baseRow, name: '"orphan"', definition: null }]),
62
+ NO_BACKOFF,
63
+ ),
64
+ ).resolves.toEqual([]);
65
+ });
66
+
67
+ test("returns all views when every row has a valid definition", async () => {
68
+ const views = await extractViews(
69
+ mockPool([
70
+ { ...baseRow, name: '"a"', definition: "SELECT 1" },
71
+ { ...baseRow, name: '"b"', definition: "SELECT 2" },
72
+ ]),
73
+ NO_BACKOFF,
74
+ );
75
+ expect(views.map((v) => v.name)).toEqual(['"a"', '"b"']);
76
+ });
77
+
78
+ test("recovers when pg_get_viewdef is NULL on first attempt but resolved on retry", async () => {
79
+ const views = await extractViews(
80
+ mockPoolSequence(
81
+ [{ ...baseRow, name: '"racy_view"', definition: null }],
82
+ [{ ...baseRow, name: '"racy_view"', definition: "SELECT 42" }],
83
+ ),
84
+ { retries: 2, backoffMs: 0 },
85
+ );
86
+ expect(views).toHaveLength(1);
87
+ expect(views[0]?.name).toBe('"racy_view"');
88
+ expect(views[0]?.definition).toBe("SELECT 42");
89
+ });
90
+ });
@@ -11,6 +11,15 @@ import {
11
11
  type PrivilegeProps,
12
12
  privilegePropsSchema,
13
13
  } from "../base.privilege-diff.ts";
14
+ import {
15
+ type ExtractRetryOptions,
16
+ extractWithDefinitionRetry,
17
+ } from "../extract-with-retry.ts";
18
+ import {
19
+ normalizeSecurityLabels,
20
+ type SecurityLabelProps,
21
+ securityLabelPropsSchema,
22
+ } from "../security-label.types.ts";
14
23
  import { ReplicaIdentitySchema } from "../table/table.model.ts";
15
24
 
16
25
  const viewPropsSchema = z.object({
@@ -32,6 +41,16 @@ const viewPropsSchema = z.object({
32
41
  comment: z.string().nullable(),
33
42
  columns: z.array(columnPropsSchema),
34
43
  privileges: z.array(privilegePropsSchema),
44
+ security_labels: z.array(securityLabelPropsSchema).default([]).optional(),
45
+ });
46
+
47
+ // pg_get_viewdef(oid) can return NULL when the underlying view (or its
48
+ // pg_rewrite row) is dropped between catalog scan and resolution, or under
49
+ // transient catalog state during recovery. An unreadable view cannot be
50
+ // diffed, so we accept NULL here and filter the row out at extraction time
51
+ // rather than crashing the whole catalog parse with a ZodError.
52
+ const viewRowSchema = viewPropsSchema.extend({
53
+ definition: z.string().nullable(),
35
54
  });
36
55
 
37
56
  type ViewPrivilegeProps = PrivilegeProps;
@@ -56,6 +75,7 @@ export class View extends BasePgModel implements TableLikeObject {
56
75
  public readonly comment: ViewProps["comment"];
57
76
  public readonly columns: ViewProps["columns"];
58
77
  public readonly privileges: ViewPrivilegeProps[];
78
+ public readonly security_labels: SecurityLabelProps[];
59
79
 
60
80
  constructor(props: ViewProps) {
61
81
  super();
@@ -81,6 +101,7 @@ export class View extends BasePgModel implements TableLikeObject {
81
101
  this.comment = props.comment;
82
102
  this.columns = props.columns;
83
103
  this.privileges = props.privileges;
104
+ this.security_labels = props.security_labels ?? [];
84
105
  }
85
106
 
86
107
  get stableId(): `view:${string}` {
@@ -112,6 +133,7 @@ export class View extends BasePgModel implements TableLikeObject {
112
133
  comment: this.comment,
113
134
  columns: this.columns,
114
135
  privileges: this.privileges,
136
+ security_labels: this.security_labels,
115
137
  };
116
138
  }
117
139
 
@@ -121,13 +143,22 @@ export class View extends BasePgModel implements TableLikeObject {
121
143
  data: {
122
144
  ...this.dataFields,
123
145
  columns: normalizeColumns(this.columns),
146
+ security_labels: normalizeSecurityLabels(this.security_labels),
124
147
  },
125
148
  };
126
149
  }
127
150
  }
128
151
 
129
- export async function extractViews(pool: Pool): Promise<View[]> {
130
- const { rows: viewRows } = await pool.query<ViewProps>(sql`
152
+ export async function extractViews(
153
+ pool: Pool,
154
+ options?: ExtractRetryOptions,
155
+ ): Promise<View[]> {
156
+ const viewRows = await extractWithDefinitionRetry({
157
+ label: "views",
158
+ options,
159
+ hasNullDefinition: (row) => row.definition === null,
160
+ query: async () => {
161
+ const result = await pool.query<ViewProps>(sql`
131
162
  with extension_oids as (
132
163
  select
133
164
  objid
@@ -243,7 +274,20 @@ select
243
274
  join lateral aclexplode(src.acl) as x(grantor, grantee, privilege_type, is_grantable) on true
244
275
  group by x.grantee, x.privilege_type
245
276
  ) as grp
246
- ), '[]') as privileges
277
+ ), '[]') as privileges,
278
+ coalesce(
279
+ (
280
+ select json_agg(
281
+ json_build_object('provider', sl.provider, 'label', sl.label)
282
+ order by sl.provider
283
+ )
284
+ from pg_catalog.pg_seclabel sl
285
+ where sl.objoid = v.oid
286
+ and sl.classoid = 'pg_class'::regclass
287
+ and sl.objsubid = 0
288
+ ),
289
+ '[]'::json
290
+ ) as security_labels
247
291
  from
248
292
  views v
249
293
  left join pg_attribute a on a.attrelid = v.oid and a.attnum > 0 and not a.attisdropped
@@ -254,9 +298,11 @@ group by
254
298
  order by
255
299
  v.schema, v.name
256
300
  `);
257
- // Validate and parse each row using the Zod schema
258
- const validatedRows = viewRows.map((row: unknown) =>
259
- viewPropsSchema.parse(row),
301
+ return result.rows.map((row: unknown) => viewRowSchema.parse(row));
302
+ },
303
+ });
304
+ const validatedRows = viewRows.filter(
305
+ (row): row is ViewProps => row.definition !== null,
260
306
  );
261
- return validatedRows.map((row: ViewProps) => new View(row));
307
+ return validatedRows.map((row) => new View(row));
262
308
  }