@supabase/pg-delta 1.0.0-alpha.22 → 1.0.0-alpha.24

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 (228) hide show
  1. package/dist/core/catalog.model.js +1 -0
  2. package/dist/core/integrations/filter/flatten.js +13 -0
  3. package/dist/core/objects/aggregate/aggregate.diff.js +16 -0
  4. package/dist/core/objects/aggregate/aggregate.model.d.ts +10 -0
  5. package/dist/core/objects/aggregate/aggregate.model.js +19 -1
  6. package/dist/core/objects/aggregate/changes/aggregate.base.d.ts +1 -1
  7. package/dist/core/objects/aggregate/changes/aggregate.security-label.d.ts +28 -0
  8. package/dist/core/objects/aggregate/changes/aggregate.security-label.js +64 -0
  9. package/dist/core/objects/aggregate/changes/aggregate.types.d.ts +2 -1
  10. package/dist/core/objects/base.model.d.ts +8 -0
  11. package/dist/core/objects/base.model.js +2 -0
  12. package/dist/core/objects/domain/changes/domain.base.d.ts +1 -1
  13. package/dist/core/objects/domain/changes/domain.security-label.d.ts +28 -0
  14. package/dist/core/objects/domain/changes/domain.security-label.js +61 -0
  15. package/dist/core/objects/domain/changes/domain.types.d.ts +2 -1
  16. package/dist/core/objects/domain/domain.diff.js +16 -0
  17. package/dist/core/objects/domain/domain.model.d.ts +10 -0
  18. package/dist/core/objects/domain/domain.model.js +19 -1
  19. package/dist/core/objects/event-trigger/changes/event-trigger.base.d.ts +1 -1
  20. package/dist/core/objects/event-trigger/changes/event-trigger.security-label.d.ts +28 -0
  21. package/dist/core/objects/event-trigger/changes/event-trigger.security-label.js +61 -0
  22. package/dist/core/objects/event-trigger/changes/event-trigger.types.d.ts +2 -1
  23. package/dist/core/objects/event-trigger/event-trigger.diff.js +16 -0
  24. package/dist/core/objects/event-trigger/event-trigger.model.d.ts +10 -0
  25. package/dist/core/objects/event-trigger/event-trigger.model.js +19 -1
  26. package/dist/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.base.d.ts +1 -1
  27. package/dist/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.security-label.d.ts +28 -0
  28. package/dist/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.security-label.js +61 -0
  29. package/dist/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.types.d.ts +2 -1
  30. package/dist/core/objects/foreign-data-wrapper/foreign-table/foreign-table.diff.js +16 -0
  31. package/dist/core/objects/foreign-data-wrapper/foreign-table/foreign-table.model.d.ts +22 -0
  32. package/dist/core/objects/foreign-data-wrapper/foreign-table/foreign-table.model.js +20 -1
  33. package/dist/core/objects/materialized-view/changes/materialized-view.base.d.ts +1 -1
  34. package/dist/core/objects/materialized-view/changes/materialized-view.security-label.d.ts +28 -0
  35. package/dist/core/objects/materialized-view/changes/materialized-view.security-label.js +61 -0
  36. package/dist/core/objects/materialized-view/changes/materialized-view.types.d.ts +2 -1
  37. package/dist/core/objects/materialized-view/materialized-view.diff.js +18 -0
  38. package/dist/core/objects/materialized-view/materialized-view.model.d.ts +22 -0
  39. package/dist/core/objects/materialized-view/materialized-view.model.js +20 -1
  40. package/dist/core/objects/procedure/changes/procedure.base.d.ts +1 -1
  41. package/dist/core/objects/procedure/changes/procedure.security-label.d.ts +28 -0
  42. package/dist/core/objects/procedure/changes/procedure.security-label.js +69 -0
  43. package/dist/core/objects/procedure/changes/procedure.types.d.ts +2 -1
  44. package/dist/core/objects/procedure/procedure.diff.js +16 -0
  45. package/dist/core/objects/procedure/procedure.model.d.ts +10 -0
  46. package/dist/core/objects/procedure/procedure.model.js +19 -1
  47. package/dist/core/objects/publication/changes/publication.base.d.ts +1 -1
  48. package/dist/core/objects/publication/changes/publication.security-label.d.ts +28 -0
  49. package/dist/core/objects/publication/changes/publication.security-label.js +61 -0
  50. package/dist/core/objects/publication/changes/publication.types.d.ts +2 -1
  51. package/dist/core/objects/publication/publication.diff.js +16 -0
  52. package/dist/core/objects/publication/publication.model.d.ts +14 -0
  53. package/dist/core/objects/publication/publication.model.js +20 -1
  54. package/dist/core/objects/role/changes/role.base.d.ts +1 -1
  55. package/dist/core/objects/role/changes/role.security-label.d.ts +28 -0
  56. package/dist/core/objects/role/changes/role.security-label.js +61 -0
  57. package/dist/core/objects/role/changes/role.types.d.ts +2 -1
  58. package/dist/core/objects/role/role.diff.js +16 -0
  59. package/dist/core/objects/role/role.model.d.ts +10 -0
  60. package/dist/core/objects/role/role.model.js +29 -0
  61. package/dist/core/objects/schema/changes/schema.base.d.ts +1 -1
  62. package/dist/core/objects/schema/changes/schema.security-label.d.ts +28 -0
  63. package/dist/core/objects/schema/changes/schema.security-label.js +61 -0
  64. package/dist/core/objects/schema/changes/schema.types.d.ts +2 -1
  65. package/dist/core/objects/schema/schema.diff.js +24 -1
  66. package/dist/core/objects/schema/schema.model.d.ts +10 -0
  67. package/dist/core/objects/schema/schema.model.js +18 -1
  68. package/dist/core/objects/security-label.types.d.ts +20 -0
  69. package/dist/core/objects/security-label.types.js +46 -0
  70. package/dist/core/objects/sequence/changes/sequence.base.d.ts +1 -1
  71. package/dist/core/objects/sequence/changes/sequence.security-label.d.ts +28 -0
  72. package/dist/core/objects/sequence/changes/sequence.security-label.js +61 -0
  73. package/dist/core/objects/sequence/changes/sequence.types.d.ts +2 -1
  74. package/dist/core/objects/sequence/sequence.diff.js +16 -0
  75. package/dist/core/objects/sequence/sequence.model.d.ts +10 -0
  76. package/dist/core/objects/sequence/sequence.model.js +19 -1
  77. package/dist/core/objects/subscription/changes/subscription.base.d.ts +1 -1
  78. package/dist/core/objects/subscription/changes/subscription.security-label.d.ts +28 -0
  79. package/dist/core/objects/subscription/changes/subscription.security-label.js +61 -0
  80. package/dist/core/objects/subscription/changes/subscription.types.d.ts +2 -1
  81. package/dist/core/objects/subscription/subscription.diff.js +16 -0
  82. package/dist/core/objects/subscription/subscription.model.d.ts +10 -0
  83. package/dist/core/objects/subscription/subscription.model.js +19 -1
  84. package/dist/core/objects/table/changes/table.base.d.ts +1 -1
  85. package/dist/core/objects/table/changes/table.security-label.d.ts +63 -0
  86. package/dist/core/objects/table/changes/table.security-label.js +134 -0
  87. package/dist/core/objects/table/changes/table.types.d.ts +2 -1
  88. package/dist/core/objects/table/table.diff.js +49 -0
  89. package/dist/core/objects/table/table.model.d.ts +30 -0
  90. package/dist/core/objects/table/table.model.js +34 -2
  91. package/dist/core/objects/type/composite-type/changes/composite-type.base.d.ts +1 -1
  92. package/dist/core/objects/type/composite-type/changes/composite-type.security-label.d.ts +28 -0
  93. package/dist/core/objects/type/composite-type/changes/composite-type.security-label.js +61 -0
  94. package/dist/core/objects/type/composite-type/changes/composite-type.types.d.ts +2 -1
  95. package/dist/core/objects/type/composite-type/composite-type.diff.js +16 -0
  96. package/dist/core/objects/type/composite-type/composite-type.model.d.ts +22 -0
  97. package/dist/core/objects/type/composite-type/composite-type.model.js +22 -2
  98. package/dist/core/objects/type/enum/changes/enum.base.d.ts +1 -1
  99. package/dist/core/objects/type/enum/changes/enum.security-label.d.ts +28 -0
  100. package/dist/core/objects/type/enum/changes/enum.security-label.js +61 -0
  101. package/dist/core/objects/type/enum/changes/enum.types.d.ts +2 -1
  102. package/dist/core/objects/type/enum/enum.diff.js +16 -0
  103. package/dist/core/objects/type/enum/enum.model.d.ts +10 -0
  104. package/dist/core/objects/type/enum/enum.model.js +20 -1
  105. package/dist/core/objects/type/range/changes/range.base.d.ts +1 -1
  106. package/dist/core/objects/type/range/changes/range.security-label.d.ts +28 -0
  107. package/dist/core/objects/type/range/changes/range.security-label.js +61 -0
  108. package/dist/core/objects/type/range/changes/range.types.d.ts +2 -1
  109. package/dist/core/objects/type/range/range.diff.js +16 -0
  110. package/dist/core/objects/type/range/range.model.d.ts +10 -0
  111. package/dist/core/objects/type/range/range.model.js +19 -1
  112. package/dist/core/objects/utils.d.ts +1 -0
  113. package/dist/core/objects/utils.js +3 -0
  114. package/dist/core/objects/view/changes/view.base.d.ts +1 -1
  115. package/dist/core/objects/view/changes/view.security-label.d.ts +28 -0
  116. package/dist/core/objects/view/changes/view.security-label.js +61 -0
  117. package/dist/core/objects/view/changes/view.types.d.ts +2 -1
  118. package/dist/core/objects/view/view.diff.js +13 -0
  119. package/dist/core/objects/view/view.model.d.ts +26 -0
  120. package/dist/core/objects/view/view.model.js +20 -1
  121. package/dist/core/plan/sql-format/fixtures.js +1 -0
  122. package/dist/core/post-diff-normalization.d.ts +7 -0
  123. package/dist/core/post-diff-normalization.js +33 -4
  124. package/dist/core/sort/cycle-breakers.js +139 -17
  125. package/package.json +1 -1
  126. package/src/core/catalog.model.ts +1 -0
  127. package/src/core/integrations/filter/dsl.test.ts +27 -0
  128. package/src/core/integrations/filter/flatten.ts +16 -0
  129. package/src/core/objects/aggregate/aggregate.diff.ts +33 -0
  130. package/src/core/objects/aggregate/aggregate.model.ts +22 -1
  131. package/src/core/objects/aggregate/changes/aggregate.base.ts +5 -1
  132. package/src/core/objects/aggregate/changes/aggregate.security-label.ts +99 -0
  133. package/src/core/objects/aggregate/changes/aggregate.types.ts +3 -1
  134. package/src/core/objects/base.model.ts +2 -0
  135. package/src/core/objects/domain/changes/domain.base.ts +5 -1
  136. package/src/core/objects/domain/changes/domain.security-label.test.ts +56 -0
  137. package/src/core/objects/domain/changes/domain.security-label.ts +77 -0
  138. package/src/core/objects/domain/changes/domain.types.ts +3 -1
  139. package/src/core/objects/domain/domain.diff.ts +33 -0
  140. package/src/core/objects/domain/domain.model.ts +22 -1
  141. package/src/core/objects/event-trigger/changes/event-trigger.base.ts +1 -1
  142. package/src/core/objects/event-trigger/changes/event-trigger.security-label.ts +95 -0
  143. package/src/core/objects/event-trigger/changes/event-trigger.types.ts +3 -1
  144. package/src/core/objects/event-trigger/event-trigger.diff.ts +33 -0
  145. package/src/core/objects/event-trigger/event-trigger.model.ts +22 -1
  146. package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.base.ts +5 -1
  147. package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.security-label.ts +95 -0
  148. package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.types.ts +3 -1
  149. package/src/core/objects/foreign-data-wrapper/foreign-table/foreign-table.diff.ts +33 -0
  150. package/src/core/objects/foreign-data-wrapper/foreign-table/foreign-table.model.ts +24 -1
  151. package/src/core/objects/materialized-view/changes/materialized-view.base.ts +5 -1
  152. package/src/core/objects/materialized-view/changes/materialized-view.security-label.test.ts +63 -0
  153. package/src/core/objects/materialized-view/changes/materialized-view.security-label.ts +95 -0
  154. package/src/core/objects/materialized-view/changes/materialized-view.types.ts +3 -1
  155. package/src/core/objects/materialized-view/materialized-view.diff.ts +37 -0
  156. package/src/core/objects/materialized-view/materialized-view.model.ts +25 -4
  157. package/src/core/objects/procedure/changes/procedure.base.ts +5 -1
  158. package/src/core/objects/procedure/changes/procedure.security-label.ts +105 -0
  159. package/src/core/objects/procedure/changes/procedure.types.ts +3 -1
  160. package/src/core/objects/procedure/procedure.diff.ts +33 -0
  161. package/src/core/objects/procedure/procedure.model.ts +23 -2
  162. package/src/core/objects/publication/changes/publication.base.ts +1 -1
  163. package/src/core/objects/publication/changes/publication.security-label.ts +95 -0
  164. package/src/core/objects/publication/changes/publication.types.ts +3 -1
  165. package/src/core/objects/publication/publication.diff.ts +33 -0
  166. package/src/core/objects/publication/publication.model.ts +24 -1
  167. package/src/core/objects/role/changes/role.base.ts +2 -1
  168. package/src/core/objects/role/changes/role.security-label.ts +77 -0
  169. package/src/core/objects/role/changes/role.types.ts +3 -1
  170. package/src/core/objects/role/role.diff.ts +33 -0
  171. package/src/core/objects/role/role.model.ts +32 -0
  172. package/src/core/objects/schema/changes/schema.alter.test.ts +1 -0
  173. package/src/core/objects/schema/changes/schema.base.ts +5 -1
  174. package/src/core/objects/schema/changes/schema.create.test.ts +1 -0
  175. package/src/core/objects/schema/changes/schema.drop.test.ts +1 -0
  176. package/src/core/objects/schema/changes/schema.security-label.test.ts +76 -0
  177. package/src/core/objects/schema/changes/schema.security-label.ts +77 -0
  178. package/src/core/objects/schema/changes/schema.types.ts +3 -1
  179. package/src/core/objects/schema/schema.diff.test.ts +1 -0
  180. package/src/core/objects/schema/schema.diff.ts +43 -1
  181. package/src/core/objects/schema/schema.model.ts +21 -1
  182. package/src/core/objects/security-label.types.test.ts +106 -0
  183. package/src/core/objects/security-label.types.ts +61 -0
  184. package/src/core/objects/sequence/changes/sequence.base.ts +5 -1
  185. package/src/core/objects/sequence/changes/sequence.security-label.test.ts +58 -0
  186. package/src/core/objects/sequence/changes/sequence.security-label.ts +92 -0
  187. package/src/core/objects/sequence/changes/sequence.types.ts +3 -1
  188. package/src/core/objects/sequence/sequence.diff.ts +33 -0
  189. package/src/core/objects/sequence/sequence.model.ts +22 -1
  190. package/src/core/objects/subscription/changes/subscription.base.ts +1 -1
  191. package/src/core/objects/subscription/changes/subscription.security-label.ts +95 -0
  192. package/src/core/objects/subscription/changes/subscription.types.ts +3 -1
  193. package/src/core/objects/subscription/subscription.diff.ts +33 -0
  194. package/src/core/objects/subscription/subscription.model.ts +22 -1
  195. package/src/core/objects/table/changes/table.base.ts +5 -1
  196. package/src/core/objects/table/changes/table.security-label.test.ts +140 -0
  197. package/src/core/objects/table/changes/table.security-label.ts +183 -0
  198. package/src/core/objects/table/changes/table.types.ts +3 -1
  199. package/src/core/objects/table/table.diff.ts +87 -0
  200. package/src/core/objects/table/table.model.ts +42 -2
  201. package/src/core/objects/type/composite-type/changes/composite-type.base.ts +5 -1
  202. package/src/core/objects/type/composite-type/changes/composite-type.security-label.ts +95 -0
  203. package/src/core/objects/type/composite-type/changes/composite-type.types.ts +3 -1
  204. package/src/core/objects/type/composite-type/composite-type.diff.ts +33 -0
  205. package/src/core/objects/type/composite-type/composite-type.model.ts +26 -2
  206. package/src/core/objects/type/enum/changes/enum.base.ts +5 -1
  207. package/src/core/objects/type/enum/changes/enum.security-label.ts +77 -0
  208. package/src/core/objects/type/enum/changes/enum.types.ts +3 -1
  209. package/src/core/objects/type/enum/enum.diff.ts +33 -0
  210. package/src/core/objects/type/enum/enum.model.ts +25 -1
  211. package/src/core/objects/type/range/changes/range.base.ts +5 -1
  212. package/src/core/objects/type/range/changes/range.security-label.ts +77 -0
  213. package/src/core/objects/type/range/changes/range.types.ts +3 -1
  214. package/src/core/objects/type/range/range.diff.ts +33 -0
  215. package/src/core/objects/type/range/range.model.ts +22 -1
  216. package/src/core/objects/utils.ts +3 -0
  217. package/src/core/objects/view/changes/view.base.ts +5 -1
  218. package/src/core/objects/view/changes/view.security-label.test.ts +64 -0
  219. package/src/core/objects/view/changes/view.security-label.ts +77 -0
  220. package/src/core/objects/view/changes/view.types.ts +3 -1
  221. package/src/core/objects/view/view.diff.ts +31 -0
  222. package/src/core/objects/view/view.model.ts +25 -2
  223. package/src/core/plan/sql-format/fixtures.ts +1 -0
  224. package/src/core/post-diff-normalization.test.ts +123 -0
  225. package/src/core/post-diff-normalization.ts +40 -4
  226. package/src/core/sort/cycle-breakers.test.ts +236 -2
  227. package/src/core/sort/cycle-breakers.ts +184 -24
  228. package/src/core/sort/sort-changes.test.ts +317 -0
@@ -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 {
9
10
  AlterForeignTableAddColumn,
10
11
  AlterForeignTableAlterColumnDropDefault,
@@ -27,6 +28,10 @@ import {
27
28
  RevokeForeignTablePrivileges,
28
29
  RevokeGrantOptionForeignTablePrivileges,
29
30
  } from "./changes/foreign-table.privilege.ts";
31
+ import {
32
+ CreateSecurityLabelOnForeignTable,
33
+ DropSecurityLabelOnForeignTable,
34
+ } from "./changes/foreign-table.security-label.ts";
30
35
  import type { ForeignTableChange } from "./changes/foreign-table.types.ts";
31
36
  import type { ForeignTable } from "./foreign-table.model.ts";
32
37
 
@@ -70,6 +75,14 @@ export function diffForeignTables(
70
75
  new CreateCommentOnForeignTable({ foreignTable: createdTable }),
71
76
  );
72
77
  }
78
+ for (const label of createdTable.security_labels) {
79
+ changes.push(
80
+ new CreateSecurityLabelOnForeignTable({
81
+ foreignTable: createdTable,
82
+ securityLabel: label,
83
+ }),
84
+ );
85
+ }
73
86
 
74
87
  // PRIVILEGES: For created objects, compare against default privileges state
75
88
  const effectiveDefaults = ctx.defaultPrivilegeState.getEffectiveDefaults(
@@ -249,6 +262,26 @@ export function diffForeignTables(
249
262
  }
250
263
  }
251
264
 
265
+ // SECURITY LABELS
266
+ changes.push(
267
+ ...diffSecurityLabels<
268
+ CreateSecurityLabelOnForeignTable | DropSecurityLabelOnForeignTable
269
+ >(
270
+ mainTable.security_labels,
271
+ branchTable.security_labels,
272
+ (securityLabel) =>
273
+ new CreateSecurityLabelOnForeignTable({
274
+ foreignTable: branchTable,
275
+ securityLabel,
276
+ }),
277
+ (securityLabel) =>
278
+ new DropSecurityLabelOnForeignTable({
279
+ foreignTable: mainTable,
280
+ securityLabel,
281
+ }),
282
+ ),
283
+ );
284
+
252
285
  // PRIVILEGES
253
286
  const mainPrivilegesFiltered = filterPublicBuiltInDefaults(
254
287
  "foreign_table",
@@ -10,6 +10,11 @@ import {
10
10
  type PrivilegeProps,
11
11
  privilegePropsSchema,
12
12
  } from "../../base.privilege-diff.ts";
13
+ import {
14
+ normalizeSecurityLabels,
15
+ type SecurityLabelProps,
16
+ securityLabelPropsSchema,
17
+ } from "../../security-label.types.ts";
13
18
 
14
19
  /**
15
20
  * All properties exposed by CREATE FOREIGN TABLE statement are included in diff output.
@@ -30,6 +35,7 @@ const foreignTablePropsSchema = z.object({
30
35
  comment: z.string().nullable(),
31
36
  columns: z.array(columnPropsSchema),
32
37
  privileges: z.array(privilegePropsSchema),
38
+ security_labels: z.array(securityLabelPropsSchema).default([]).optional(),
33
39
  });
34
40
 
35
41
  type ForeignTablePrivilegeProps = PrivilegeProps;
@@ -44,6 +50,7 @@ export class ForeignTable extends BasePgModel implements TableLikeObject {
44
50
  public readonly comment: ForeignTableProps["comment"];
45
51
  public readonly columns: ForeignTableProps["columns"];
46
52
  public readonly privileges: ForeignTablePrivilegeProps[];
53
+ public readonly security_labels: SecurityLabelProps[];
47
54
 
48
55
  constructor(props: ForeignTableProps) {
49
56
  super();
@@ -59,6 +66,7 @@ export class ForeignTable extends BasePgModel implements TableLikeObject {
59
66
  this.comment = props.comment;
60
67
  this.columns = props.columns;
61
68
  this.privileges = props.privileges;
69
+ this.security_labels = props.security_labels ?? [];
62
70
  }
63
71
 
64
72
  get stableId(): `foreignTable:${string}` {
@@ -80,6 +88,7 @@ export class ForeignTable extends BasePgModel implements TableLikeObject {
80
88
  comment: this.comment,
81
89
  columns: this.columns,
82
90
  privileges: this.privileges,
91
+ security_labels: this.security_labels,
83
92
  };
84
93
  }
85
94
 
@@ -104,6 +113,7 @@ export class ForeignTable extends BasePgModel implements TableLikeObject {
104
113
  data: {
105
114
  ...this.dataFields,
106
115
  columns: normalizeColumns(),
116
+ security_labels: normalizeSecurityLabels(this.security_labels),
107
117
  },
108
118
  };
109
119
  }
@@ -209,7 +219,20 @@ export async function extractForeignTables(
209
219
  join lateral aclexplode(src.acl) as x(grantor, grantee, privilege_type, is_grantable) on true
210
220
  group by x.grantee, x.privilege_type
211
221
  ) as grp
212
- ), '[]') as privileges
222
+ ), '[]') as privileges,
223
+ coalesce(
224
+ (
225
+ select json_agg(
226
+ json_build_object('provider', sl.provider, 'label', sl.label)
227
+ order by sl.provider
228
+ )
229
+ from pg_catalog.pg_seclabel sl
230
+ where sl.objoid = ft.oid
231
+ and sl.classoid = 'pg_class'::regclass
232
+ and sl.objsubid = 0
233
+ ),
234
+ '[]'::json
235
+ ) as security_labels
213
236
  from
214
237
  foreign_tables ft
215
238
  left join pg_attribute a on a.attrelid = ft.oid and a.attnum > 0 and not a.attisdropped
@@ -3,7 +3,11 @@ import type { MaterializedView } from "../materialized-view.model.ts";
3
3
 
4
4
  abstract class BaseMaterializedViewChange extends BaseChange {
5
5
  abstract readonly materializedView: MaterializedView;
6
- abstract readonly scope: "object" | "comment" | "privilege";
6
+ abstract readonly scope:
7
+ | "object"
8
+ | "comment"
9
+ | "privilege"
10
+ | "security_label";
7
11
  readonly objectType: "materialized_view" = "materialized_view";
8
12
  }
9
13
 
@@ -0,0 +1,63 @@
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 {
5
+ MaterializedView,
6
+ type MaterializedViewProps,
7
+ } from "../materialized-view.model.ts";
8
+ import {
9
+ CreateSecurityLabelOnMaterializedView,
10
+ DropSecurityLabelOnMaterializedView,
11
+ } from "./materialized-view.security-label.ts";
12
+
13
+ const makeMV = (): MaterializedView =>
14
+ new MaterializedView({
15
+ schema: "public",
16
+ name: "mv",
17
+ definition: "SELECT 1",
18
+ row_security: false,
19
+ force_row_security: false,
20
+ has_indexes: false,
21
+ has_rules: false,
22
+ has_triggers: false,
23
+ has_subclasses: false,
24
+ is_populated: true,
25
+ replica_identity: "d",
26
+ is_partition: false,
27
+ options: null,
28
+ partition_bound: null,
29
+ owner: "postgres",
30
+ comment: null,
31
+ columns: [],
32
+ privileges: [],
33
+ } as MaterializedViewProps);
34
+
35
+ describe("materialized-view.security-label", () => {
36
+ test("create serializes", async () => {
37
+ const mv = makeMV();
38
+ const change = new CreateSecurityLabelOnMaterializedView({
39
+ materializedView: mv,
40
+ securityLabel: { provider: "dummy", label: "classified" },
41
+ });
42
+ expect(change.scope).toBe("security_label");
43
+ expect(change.creates).toEqual([
44
+ stableId.securityLabel(mv.stableId, "dummy"),
45
+ ]);
46
+ await assertValidSql(change.serialize());
47
+ expect(change.serialize()).toBe(
48
+ "SECURITY LABEL FOR dummy ON MATERIALIZED VIEW public.mv IS 'classified'",
49
+ );
50
+ });
51
+
52
+ test("drop serializes to IS NULL", async () => {
53
+ const mv = makeMV();
54
+ const change = new DropSecurityLabelOnMaterializedView({
55
+ materializedView: mv,
56
+ securityLabel: { provider: "dummy", label: "x" },
57
+ });
58
+ await assertValidSql(change.serialize());
59
+ expect(change.serialize()).toBe(
60
+ "SECURITY LABEL FOR dummy ON MATERIALIZED VIEW public.mv IS NULL",
61
+ );
62
+ });
63
+ });
@@ -0,0 +1,95 @@
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 { MaterializedView } from "../materialized-view.model.ts";
5
+ import {
6
+ CreateMaterializedViewChange,
7
+ DropMaterializedViewChange,
8
+ } from "./materialized-view.base.ts";
9
+
10
+ export type SecurityLabelMaterializedView =
11
+ | CreateSecurityLabelOnMaterializedView
12
+ | DropSecurityLabelOnMaterializedView;
13
+
14
+ export class CreateSecurityLabelOnMaterializedView extends CreateMaterializedViewChange {
15
+ public readonly materializedView: MaterializedView;
16
+ public readonly securityLabel: SecurityLabelProps;
17
+ public readonly scope = "security_label" as const;
18
+
19
+ constructor(props: {
20
+ materializedView: MaterializedView;
21
+ securityLabel: SecurityLabelProps;
22
+ }) {
23
+ super();
24
+ this.materializedView = props.materializedView;
25
+ this.securityLabel = props.securityLabel;
26
+ }
27
+
28
+ get creates() {
29
+ return [
30
+ stableId.securityLabel(
31
+ this.materializedView.stableId,
32
+ this.securityLabel.provider,
33
+ ),
34
+ ];
35
+ }
36
+
37
+ get requires() {
38
+ return [this.materializedView.stableId];
39
+ }
40
+
41
+ serialize(): string {
42
+ return [
43
+ "SECURITY LABEL FOR",
44
+ this.securityLabel.provider,
45
+ "ON MATERIALIZED VIEW",
46
+ `${this.materializedView.schema}.${this.materializedView.name}`,
47
+ "IS",
48
+ quoteLiteral(this.securityLabel.label),
49
+ ].join(" ");
50
+ }
51
+ }
52
+
53
+ export class DropSecurityLabelOnMaterializedView extends DropMaterializedViewChange {
54
+ public readonly materializedView: MaterializedView;
55
+ public readonly securityLabel: SecurityLabelProps;
56
+ public readonly scope = "security_label" as const;
57
+
58
+ constructor(props: {
59
+ materializedView: MaterializedView;
60
+ securityLabel: SecurityLabelProps;
61
+ }) {
62
+ super();
63
+ this.materializedView = props.materializedView;
64
+ this.securityLabel = props.securityLabel;
65
+ }
66
+
67
+ get drops() {
68
+ return [
69
+ stableId.securityLabel(
70
+ this.materializedView.stableId,
71
+ this.securityLabel.provider,
72
+ ),
73
+ ];
74
+ }
75
+
76
+ get requires() {
77
+ return [
78
+ stableId.securityLabel(
79
+ this.materializedView.stableId,
80
+ this.securityLabel.provider,
81
+ ),
82
+ this.materializedView.stableId,
83
+ ];
84
+ }
85
+
86
+ serialize(): string {
87
+ return [
88
+ "SECURITY LABEL FOR",
89
+ this.securityLabel.provider,
90
+ "ON MATERIALIZED VIEW",
91
+ `${this.materializedView.schema}.${this.materializedView.name}`,
92
+ "IS NULL",
93
+ ].join(" ");
94
+ }
95
+ }
@@ -3,6 +3,7 @@ import type { CommentMaterializedView } from "./materialized-view.comment.ts";
3
3
  import type { CreateMaterializedView } from "./materialized-view.create.ts";
4
4
  import type { DropMaterializedView } from "./materialized-view.drop.ts";
5
5
  import type { MaterializedViewPrivilege } from "./materialized-view.privilege.ts";
6
+ import type { SecurityLabelMaterializedView } from "./materialized-view.security-label.ts";
6
7
 
7
8
  /** Union of all materialized-view-related change variants (`objectType: "materialized_view"`). @category Change Types */
8
9
  export type MaterializedViewChange =
@@ -10,4 +11,5 @@ export type MaterializedViewChange =
10
11
  | CommentMaterializedView
11
12
  | CreateMaterializedView
12
13
  | DropMaterializedView
13
- | MaterializedViewPrivilege;
14
+ | MaterializedViewPrivilege
15
+ | SecurityLabelMaterializedView;
@@ -4,6 +4,7 @@ import {
4
4
  emitColumnPrivilegeChanges,
5
5
  } from "../base.privilege-diff.ts";
6
6
  import type { ObjectDiffContext } from "../diff-context.ts";
7
+ import { diffSecurityLabels } from "../security-label.types.ts";
7
8
  import { deepEqual, hasNonAlterableChanges } from "../utils.ts";
8
9
  import {
9
10
  AlterMaterializedViewChangeOwner,
@@ -22,6 +23,10 @@ import {
22
23
  RevokeGrantOptionMaterializedViewPrivileges,
23
24
  RevokeMaterializedViewPrivileges,
24
25
  } from "./changes/materialized-view.privilege.ts";
26
+ import {
27
+ CreateSecurityLabelOnMaterializedView,
28
+ DropSecurityLabelOnMaterializedView,
29
+ } from "./changes/materialized-view.security-label.ts";
25
30
  import type { MaterializedViewChange } from "./changes/materialized-view.types.ts";
26
31
  import type { MaterializedView } from "./materialized-view.model.ts";
27
32
 
@@ -88,6 +93,17 @@ export function diffMaterializedViews(
88
93
  }
89
94
  }
90
95
 
96
+ // Security labels on the matview itself (columns of matviews are not
97
+ // supported targets of SECURITY LABEL, so we only label the relation).
98
+ for (const label of mv.security_labels) {
99
+ changes.push(
100
+ new CreateSecurityLabelOnMaterializedView({
101
+ materializedView: mv,
102
+ securityLabel: label,
103
+ }),
104
+ );
105
+ }
106
+
91
107
  // PRIVILEGES: For created objects, compare against default privileges state
92
108
  // The migration script will run ALTER DEFAULT PRIVILEGES before CREATE (via constraint spec),
93
109
  // so objects are created with the default privileges state in effect.
@@ -241,6 +257,27 @@ export function diffMaterializedViews(
241
257
  );
242
258
  }
243
259
  }
260
+
261
+ // SECURITY LABELS
262
+ changes.push(
263
+ ...diffSecurityLabels<
264
+ | CreateSecurityLabelOnMaterializedView
265
+ | DropSecurityLabelOnMaterializedView
266
+ >(
267
+ mainMaterializedView.security_labels,
268
+ branchMaterializedView.security_labels,
269
+ (securityLabel) =>
270
+ new CreateSecurityLabelOnMaterializedView({
271
+ materializedView: branchMaterializedView,
272
+ securityLabel,
273
+ }),
274
+ (securityLabel) =>
275
+ new DropSecurityLabelOnMaterializedView({
276
+ materializedView: mainMaterializedView,
277
+ securityLabel,
278
+ }),
279
+ ),
280
+ );
244
281
  // COMMENT changes on columns
245
282
  const mainCols = new Map(
246
283
  mainMaterializedView.columns.map((c) => [c.name, c]),
@@ -14,6 +14,11 @@ import {
14
14
  type ExtractRetryOptions,
15
15
  extractWithDefinitionRetry,
16
16
  } from "../extract-with-retry.ts";
17
+ import {
18
+ normalizeSecurityLabels,
19
+ type SecurityLabelProps,
20
+ securityLabelPropsSchema,
21
+ } from "../security-label.types.ts";
17
22
  import { ReplicaIdentitySchema } from "../table/table.model.ts";
18
23
 
19
24
  const materializedViewPropsSchema = z.object({
@@ -35,6 +40,7 @@ const materializedViewPropsSchema = z.object({
35
40
  comment: z.string().nullable(),
36
41
  columns: z.array(columnPropsSchema),
37
42
  privileges: z.array(privilegePropsSchema),
43
+ security_labels: z.array(securityLabelPropsSchema).default([]).optional(),
38
44
  });
39
45
 
40
46
  // pg_get_viewdef(oid) can return NULL when the underlying matview (or its
@@ -68,6 +74,7 @@ export class MaterializedView extends BasePgModel implements TableLikeObject {
68
74
  public readonly comment: MaterializedViewProps["comment"];
69
75
  public readonly columns: MaterializedViewProps["columns"];
70
76
  public readonly privileges: MaterializedViewPrivilegeProps[];
77
+ public readonly security_labels: SecurityLabelProps[];
71
78
 
72
79
  constructor(props: MaterializedViewProps) {
73
80
  super();
@@ -93,6 +100,7 @@ export class MaterializedView extends BasePgModel implements TableLikeObject {
93
100
  this.comment = props.comment;
94
101
  this.columns = props.columns;
95
102
  this.privileges = props.privileges;
103
+ this.security_labels = props.security_labels ?? [];
96
104
  }
97
105
 
98
106
  get stableId(): `materializedView:${string}` {
@@ -124,6 +132,7 @@ export class MaterializedView extends BasePgModel implements TableLikeObject {
124
132
  comment: this.comment,
125
133
  columns: this.columns,
126
134
  privileges: this.privileges,
135
+ security_labels: this.security_labels,
127
136
  };
128
137
  }
129
138
 
@@ -148,6 +157,7 @@ export class MaterializedView extends BasePgModel implements TableLikeObject {
148
157
  data: {
149
158
  ...this.dataFields,
150
159
  columns: normalizeColumns(),
160
+ security_labels: normalizeSecurityLabels(this.security_labels),
151
161
  },
152
162
  };
153
163
  }
@@ -252,7 +262,20 @@ select
252
262
  join lateral aclexplode(src.acl) as x(grantor, grantee, privilege_type, is_grantable) on true
253
263
  group by x.grantee, x.privilege_type
254
264
  ) as grp
255
- ), '[]') as privileges
265
+ ), '[]') as privileges,
266
+ coalesce(
267
+ (
268
+ select json_agg(
269
+ json_build_object('provider', sl.provider, 'label', sl.label)
270
+ order by sl.provider
271
+ )
272
+ from pg_catalog.pg_seclabel sl
273
+ where sl.objoid = c.oid
274
+ and sl.classoid = 'pg_class'::regclass
275
+ and sl.objsubid = 0
276
+ ),
277
+ '[]'::json
278
+ ) as security_labels
256
279
  from
257
280
  pg_catalog.pg_class c
258
281
  left outer join extension_oids e on c.oid = e.objid
@@ -275,7 +298,5 @@ order by
275
298
  const validatedRows = mvRows.filter(
276
299
  (row): row is MaterializedViewProps => row.definition !== null,
277
300
  );
278
- return validatedRows.map(
279
- (row: MaterializedViewProps) => new MaterializedView(row),
280
- );
301
+ return validatedRows.map((row) => new MaterializedView(row));
281
302
  }
@@ -3,7 +3,11 @@ import type { Procedure } from "../procedure.model.ts";
3
3
 
4
4
  abstract class BaseProcedureChange extends BaseChange {
5
5
  abstract readonly procedure: Procedure;
6
- abstract readonly scope: "object" | "comment" | "privilege";
6
+ abstract readonly scope:
7
+ | "object"
8
+ | "comment"
9
+ | "privilege"
10
+ | "security_label";
7
11
  readonly objectType: "procedure" = "procedure";
8
12
  }
9
13
 
@@ -0,0 +1,105 @@
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 { Procedure } from "../procedure.model.ts";
5
+ import {
6
+ CreateProcedureChange,
7
+ DropProcedureChange,
8
+ } from "./procedure.base.ts";
9
+
10
+ export type SecurityLabelProcedure =
11
+ | CreateSecurityLabelOnProcedure
12
+ | DropSecurityLabelOnProcedure;
13
+
14
+ function targetKeyword(p: Procedure): "FUNCTION" | "PROCEDURE" {
15
+ return p.kind === "p" ? "PROCEDURE" : "FUNCTION";
16
+ }
17
+
18
+ function procedureIdentity(p: Procedure): string {
19
+ return `${p.schema}.${p.name}(${(p.argument_types ?? []).join(",")})`;
20
+ }
21
+
22
+ export class CreateSecurityLabelOnProcedure extends CreateProcedureChange {
23
+ public readonly procedure: Procedure;
24
+ public readonly securityLabel: SecurityLabelProps;
25
+ public readonly scope = "security_label" as const;
26
+
27
+ constructor(props: {
28
+ procedure: Procedure;
29
+ securityLabel: SecurityLabelProps;
30
+ }) {
31
+ super();
32
+ this.procedure = props.procedure;
33
+ this.securityLabel = props.securityLabel;
34
+ }
35
+
36
+ get creates() {
37
+ return [
38
+ stableId.securityLabel(
39
+ this.procedure.stableId,
40
+ this.securityLabel.provider,
41
+ ),
42
+ ];
43
+ }
44
+
45
+ get requires() {
46
+ return [this.procedure.stableId];
47
+ }
48
+
49
+ serialize(): string {
50
+ return [
51
+ "SECURITY LABEL FOR",
52
+ this.securityLabel.provider,
53
+ "ON",
54
+ targetKeyword(this.procedure),
55
+ procedureIdentity(this.procedure),
56
+ "IS",
57
+ quoteLiteral(this.securityLabel.label),
58
+ ].join(" ");
59
+ }
60
+ }
61
+
62
+ export class DropSecurityLabelOnProcedure extends DropProcedureChange {
63
+ public readonly procedure: Procedure;
64
+ public readonly securityLabel: SecurityLabelProps;
65
+ public readonly scope = "security_label" as const;
66
+
67
+ constructor(props: {
68
+ procedure: Procedure;
69
+ securityLabel: SecurityLabelProps;
70
+ }) {
71
+ super();
72
+ this.procedure = props.procedure;
73
+ this.securityLabel = props.securityLabel;
74
+ }
75
+
76
+ get drops() {
77
+ return [
78
+ stableId.securityLabel(
79
+ this.procedure.stableId,
80
+ this.securityLabel.provider,
81
+ ),
82
+ ];
83
+ }
84
+
85
+ get requires() {
86
+ return [
87
+ stableId.securityLabel(
88
+ this.procedure.stableId,
89
+ this.securityLabel.provider,
90
+ ),
91
+ this.procedure.stableId,
92
+ ];
93
+ }
94
+
95
+ serialize(): string {
96
+ return [
97
+ "SECURITY LABEL FOR",
98
+ this.securityLabel.provider,
99
+ "ON",
100
+ targetKeyword(this.procedure),
101
+ procedureIdentity(this.procedure),
102
+ "IS NULL",
103
+ ].join(" ");
104
+ }
105
+ }
@@ -3,6 +3,7 @@ import type { CommentProcedure } from "./procedure.comment.ts";
3
3
  import type { CreateProcedure } from "./procedure.create.ts";
4
4
  import type { DropProcedure } from "./procedure.drop.ts";
5
5
  import type { ProcedurePrivilege } from "./procedure.privilege.ts";
6
+ import type { SecurityLabelProcedure } from "./procedure.security-label.ts";
6
7
 
7
8
  /** Union of all procedure-related change variants (`objectType: "procedure"`). @category Change Types */
8
9
  export type ProcedureChange =
@@ -10,4 +11,5 @@ export type ProcedureChange =
10
11
  | CommentProcedure
11
12
  | CreateProcedure
12
13
  | DropProcedure
13
- | ProcedurePrivilege;
14
+ | ProcedurePrivilege
15
+ | SecurityLabelProcedure;
@@ -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 { deepEqual, hasNonAlterableChanges } from "../utils.ts";
9
10
  import {
10
11
  AlterProcedureChangeOwner,
@@ -26,6 +27,10 @@ import {
26
27
  RevokeGrantOptionProcedurePrivileges,
27
28
  RevokeProcedurePrivileges,
28
29
  } from "./changes/procedure.privilege.ts";
30
+ import {
31
+ CreateSecurityLabelOnProcedure,
32
+ DropSecurityLabelOnProcedure,
33
+ } from "./changes/procedure.security-label.ts";
29
34
  import type { ProcedureChange } from "./changes/procedure.types.ts";
30
35
  import type { Procedure } from "./procedure.model.ts";
31
36
 
@@ -66,6 +71,14 @@ export function diffProcedures(
66
71
  if (proc.comment !== null) {
67
72
  changes.push(new CreateCommentOnProcedure({ procedure: proc }));
68
73
  }
74
+ for (const label of proc.security_labels) {
75
+ changes.push(
76
+ new CreateSecurityLabelOnProcedure({
77
+ procedure: proc,
78
+ securityLabel: label,
79
+ }),
80
+ );
81
+ }
69
82
 
70
83
  // PRIVILEGES: For created objects, compare against default privileges state
71
84
  // The migration script will run ALTER DEFAULT PRIVILEGES before CREATE (via constraint spec),
@@ -225,6 +238,26 @@ export function diffProcedures(
225
238
  }
226
239
  }
227
240
 
241
+ // SECURITY LABELS
242
+ changes.push(
243
+ ...diffSecurityLabels<
244
+ CreateSecurityLabelOnProcedure | DropSecurityLabelOnProcedure
245
+ >(
246
+ mainProcedure.security_labels,
247
+ branchProcedure.security_labels,
248
+ (securityLabel) =>
249
+ new CreateSecurityLabelOnProcedure({
250
+ procedure: branchProcedure,
251
+ securityLabel,
252
+ }),
253
+ (securityLabel) =>
254
+ new DropSecurityLabelOnProcedure({
255
+ procedure: mainProcedure,
256
+ securityLabel,
257
+ }),
258
+ ),
259
+ );
260
+
228
261
  // SECURITY DEFINER/INVOKER
229
262
  if (mainProcedure.security_definer !== branchProcedure.security_definer) {
230
263
  changes.push(