@supabase/pg-delta 1.0.0-alpha.22 → 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 (220) 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/package.json +1 -1
  123. package/src/core/catalog.model.ts +1 -0
  124. package/src/core/integrations/filter/dsl.test.ts +27 -0
  125. package/src/core/integrations/filter/flatten.ts +16 -0
  126. package/src/core/objects/aggregate/aggregate.diff.ts +33 -0
  127. package/src/core/objects/aggregate/aggregate.model.ts +22 -1
  128. package/src/core/objects/aggregate/changes/aggregate.base.ts +5 -1
  129. package/src/core/objects/aggregate/changes/aggregate.security-label.ts +99 -0
  130. package/src/core/objects/aggregate/changes/aggregate.types.ts +3 -1
  131. package/src/core/objects/base.model.ts +2 -0
  132. package/src/core/objects/domain/changes/domain.base.ts +5 -1
  133. package/src/core/objects/domain/changes/domain.security-label.test.ts +56 -0
  134. package/src/core/objects/domain/changes/domain.security-label.ts +77 -0
  135. package/src/core/objects/domain/changes/domain.types.ts +3 -1
  136. package/src/core/objects/domain/domain.diff.ts +33 -0
  137. package/src/core/objects/domain/domain.model.ts +22 -1
  138. package/src/core/objects/event-trigger/changes/event-trigger.base.ts +1 -1
  139. package/src/core/objects/event-trigger/changes/event-trigger.security-label.ts +95 -0
  140. package/src/core/objects/event-trigger/changes/event-trigger.types.ts +3 -1
  141. package/src/core/objects/event-trigger/event-trigger.diff.ts +33 -0
  142. package/src/core/objects/event-trigger/event-trigger.model.ts +22 -1
  143. package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.base.ts +5 -1
  144. package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.security-label.ts +95 -0
  145. package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.types.ts +3 -1
  146. package/src/core/objects/foreign-data-wrapper/foreign-table/foreign-table.diff.ts +33 -0
  147. package/src/core/objects/foreign-data-wrapper/foreign-table/foreign-table.model.ts +24 -1
  148. package/src/core/objects/materialized-view/changes/materialized-view.base.ts +5 -1
  149. package/src/core/objects/materialized-view/changes/materialized-view.security-label.test.ts +63 -0
  150. package/src/core/objects/materialized-view/changes/materialized-view.security-label.ts +95 -0
  151. package/src/core/objects/materialized-view/changes/materialized-view.types.ts +3 -1
  152. package/src/core/objects/materialized-view/materialized-view.diff.ts +37 -0
  153. package/src/core/objects/materialized-view/materialized-view.model.ts +25 -4
  154. package/src/core/objects/procedure/changes/procedure.base.ts +5 -1
  155. package/src/core/objects/procedure/changes/procedure.security-label.ts +105 -0
  156. package/src/core/objects/procedure/changes/procedure.types.ts +3 -1
  157. package/src/core/objects/procedure/procedure.diff.ts +33 -0
  158. package/src/core/objects/procedure/procedure.model.ts +23 -2
  159. package/src/core/objects/publication/changes/publication.base.ts +1 -1
  160. package/src/core/objects/publication/changes/publication.security-label.ts +95 -0
  161. package/src/core/objects/publication/changes/publication.types.ts +3 -1
  162. package/src/core/objects/publication/publication.diff.ts +33 -0
  163. package/src/core/objects/publication/publication.model.ts +24 -1
  164. package/src/core/objects/role/changes/role.base.ts +2 -1
  165. package/src/core/objects/role/changes/role.security-label.ts +77 -0
  166. package/src/core/objects/role/changes/role.types.ts +3 -1
  167. package/src/core/objects/role/role.diff.ts +33 -0
  168. package/src/core/objects/role/role.model.ts +32 -0
  169. package/src/core/objects/schema/changes/schema.alter.test.ts +1 -0
  170. package/src/core/objects/schema/changes/schema.base.ts +5 -1
  171. package/src/core/objects/schema/changes/schema.create.test.ts +1 -0
  172. package/src/core/objects/schema/changes/schema.drop.test.ts +1 -0
  173. package/src/core/objects/schema/changes/schema.security-label.test.ts +76 -0
  174. package/src/core/objects/schema/changes/schema.security-label.ts +77 -0
  175. package/src/core/objects/schema/changes/schema.types.ts +3 -1
  176. package/src/core/objects/schema/schema.diff.test.ts +1 -0
  177. package/src/core/objects/schema/schema.diff.ts +43 -1
  178. package/src/core/objects/schema/schema.model.ts +21 -1
  179. package/src/core/objects/security-label.types.test.ts +106 -0
  180. package/src/core/objects/security-label.types.ts +61 -0
  181. package/src/core/objects/sequence/changes/sequence.base.ts +5 -1
  182. package/src/core/objects/sequence/changes/sequence.security-label.test.ts +58 -0
  183. package/src/core/objects/sequence/changes/sequence.security-label.ts +92 -0
  184. package/src/core/objects/sequence/changes/sequence.types.ts +3 -1
  185. package/src/core/objects/sequence/sequence.diff.ts +33 -0
  186. package/src/core/objects/sequence/sequence.model.ts +22 -1
  187. package/src/core/objects/subscription/changes/subscription.base.ts +1 -1
  188. package/src/core/objects/subscription/changes/subscription.security-label.ts +95 -0
  189. package/src/core/objects/subscription/changes/subscription.types.ts +3 -1
  190. package/src/core/objects/subscription/subscription.diff.ts +33 -0
  191. package/src/core/objects/subscription/subscription.model.ts +22 -1
  192. package/src/core/objects/table/changes/table.base.ts +5 -1
  193. package/src/core/objects/table/changes/table.security-label.test.ts +140 -0
  194. package/src/core/objects/table/changes/table.security-label.ts +183 -0
  195. package/src/core/objects/table/changes/table.types.ts +3 -1
  196. package/src/core/objects/table/table.diff.ts +87 -0
  197. package/src/core/objects/table/table.model.ts +42 -2
  198. package/src/core/objects/type/composite-type/changes/composite-type.base.ts +5 -1
  199. package/src/core/objects/type/composite-type/changes/composite-type.security-label.ts +95 -0
  200. package/src/core/objects/type/composite-type/changes/composite-type.types.ts +3 -1
  201. package/src/core/objects/type/composite-type/composite-type.diff.ts +33 -0
  202. package/src/core/objects/type/composite-type/composite-type.model.ts +26 -2
  203. package/src/core/objects/type/enum/changes/enum.base.ts +5 -1
  204. package/src/core/objects/type/enum/changes/enum.security-label.ts +77 -0
  205. package/src/core/objects/type/enum/changes/enum.types.ts +3 -1
  206. package/src/core/objects/type/enum/enum.diff.ts +33 -0
  207. package/src/core/objects/type/enum/enum.model.ts +25 -1
  208. package/src/core/objects/type/range/changes/range.base.ts +5 -1
  209. package/src/core/objects/type/range/changes/range.security-label.ts +77 -0
  210. package/src/core/objects/type/range/changes/range.types.ts +3 -1
  211. package/src/core/objects/type/range/range.diff.ts +33 -0
  212. package/src/core/objects/type/range/range.model.ts +22 -1
  213. package/src/core/objects/utils.ts +3 -0
  214. package/src/core/objects/view/changes/view.base.ts +5 -1
  215. package/src/core/objects/view/changes/view.security-label.test.ts +64 -0
  216. package/src/core/objects/view/changes/view.security-label.ts +77 -0
  217. package/src/core/objects/view/changes/view.types.ts +3 -1
  218. package/src/core/objects/view/view.diff.ts +31 -0
  219. package/src/core/objects/view/view.model.ts +25 -2
  220. package/src/core/plan/sql-format/fixtures.ts +1 -0
@@ -3,6 +3,7 @@ import z from "zod";
3
3
  import { BasePgModel, columnPropsSchema, normalizeColumns, } from "../base.model.js";
4
4
  import { privilegePropsSchema, } from "../base.privilege-diff.js";
5
5
  import { extractWithDefinitionRetry, } from "../extract-with-retry.js";
6
+ import { normalizeSecurityLabels, securityLabelPropsSchema, } from "../security-label.types.js";
6
7
  import { ReplicaIdentitySchema } from "../table/table.model.js";
7
8
  const viewPropsSchema = z.object({
8
9
  schema: z.string(),
@@ -23,6 +24,7 @@ const viewPropsSchema = z.object({
23
24
  comment: z.string().nullable(),
24
25
  columns: z.array(columnPropsSchema),
25
26
  privileges: z.array(privilegePropsSchema),
27
+ security_labels: z.array(securityLabelPropsSchema).default([]).optional(),
26
28
  });
27
29
  // pg_get_viewdef(oid) can return NULL when the underlying view (or its
28
30
  // pg_rewrite row) is dropped between catalog scan and resolution, or under
@@ -51,6 +53,7 @@ export class View extends BasePgModel {
51
53
  comment;
52
54
  columns;
53
55
  privileges;
56
+ security_labels;
54
57
  constructor(props) {
55
58
  super();
56
59
  // Identity fields
@@ -73,6 +76,7 @@ export class View extends BasePgModel {
73
76
  this.comment = props.comment;
74
77
  this.columns = props.columns;
75
78
  this.privileges = props.privileges;
79
+ this.security_labels = props.security_labels ?? [];
76
80
  }
77
81
  get stableId() {
78
82
  return `view:${this.schema}.${this.name}`;
@@ -101,6 +105,7 @@ export class View extends BasePgModel {
101
105
  comment: this.comment,
102
106
  columns: this.columns,
103
107
  privileges: this.privileges,
108
+ security_labels: this.security_labels,
104
109
  };
105
110
  }
106
111
  stableSnapshot() {
@@ -109,6 +114,7 @@ export class View extends BasePgModel {
109
114
  data: {
110
115
  ...this.dataFields,
111
116
  columns: normalizeColumns(this.columns),
117
+ security_labels: normalizeSecurityLabels(this.security_labels),
112
118
  },
113
119
  };
114
120
  }
@@ -235,7 +241,20 @@ select
235
241
  join lateral aclexplode(src.acl) as x(grantor, grantee, privilege_type, is_grantable) on true
236
242
  group by x.grantee, x.privilege_type
237
243
  ) as grp
238
- ), '[]') as privileges
244
+ ), '[]') as privileges,
245
+ coalesce(
246
+ (
247
+ select json_agg(
248
+ json_build_object('provider', sl.provider, 'label', sl.label)
249
+ order by sl.provider
250
+ )
251
+ from pg_catalog.pg_seclabel sl
252
+ where sl.objoid = v.oid
253
+ and sl.classoid = 'pg_class'::regclass
254
+ and sl.objsubid = 0
255
+ ),
256
+ '[]'::json
257
+ ) as security_labels
239
258
  from
240
259
  views v
241
260
  left join pg_attribute a on a.attrelid = v.oid and a.attnum > 0 and not a.attisdropped
@@ -806,6 +806,7 @@ const schema = new Schema({
806
806
  owner: "admin",
807
807
  comment: "application schema",
808
808
  privileges: [],
809
+ security_labels: [],
809
810
  });
810
811
  const extension = new Extension({
811
812
  name: "pgcrypto",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supabase/pg-delta",
3
- "version": "1.0.0-alpha.22",
3
+ "version": "1.0.0-alpha.23",
4
4
  "description": "PostgreSQL migrations made easy",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -267,6 +267,7 @@ export async function createEmptyCatalog(
267
267
  owner: currentUser,
268
268
  comment: "standard public schema",
269
269
  privileges: [],
270
+ security_labels: [],
270
271
  });
271
272
 
272
273
  return new Catalog({
@@ -41,6 +41,16 @@ const membershipChange = {
41
41
  requires: [],
42
42
  } as unknown as Change;
43
43
 
44
+ const securityLabelChange = {
45
+ objectType: "schema",
46
+ operation: "create",
47
+ scope: "security_label",
48
+ schema: { name: "labeled" },
49
+ securityLabel: { provider: "dummy", label: "classified" },
50
+ requires: ["schema:labeled"],
51
+ creates: ["security_label:schema:labeled:dummy"],
52
+ } as unknown as Change;
53
+
44
54
  describe("evaluatePattern", () => {
45
55
  describe("bare key matching (top-level properties)", () => {
46
56
  test("objectType match", () => {
@@ -242,6 +252,23 @@ describe("evaluatePattern", () => {
242
252
  });
243
253
  });
244
254
 
255
+ describe("security label matching", () => {
256
+ test("provider matches security label changes", () => {
257
+ expect(
258
+ evaluatePattern(
259
+ { scope: "security_label", provider: "dummy" },
260
+ securityLabelChange,
261
+ ),
262
+ ).toBe(true);
263
+ expect(
264
+ evaluatePattern(
265
+ { scope: "security_label", provider: "other" },
266
+ securityLabelChange,
267
+ ),
268
+ ).toBe(false);
269
+ });
270
+ });
271
+
245
272
  describe("composition patterns", () => {
246
273
  test("not negates a pattern", () => {
247
274
  expect(
@@ -105,6 +105,22 @@ export function flattenChange(change: Change): Record<string, FlatValue> {
105
105
  flat[`${prefix}/${subKey}`] = flatVal;
106
106
  }
107
107
  }
108
+ } else if (
109
+ key === "securityLabel" &&
110
+ value &&
111
+ typeof value === "object" &&
112
+ !Array.isArray(value)
113
+ ) {
114
+ // Security labels are change-level metadata, so expose provider/label as
115
+ // bare keys for filters like { scope: "security_label", provider: "..." }.
116
+ for (const [subKey, subValue] of Object.entries(
117
+ value as Record<string, unknown>,
118
+ )) {
119
+ const flatVal = toFlatValue(subValue);
120
+ if (flatVal !== undefined) {
121
+ flat[subKey] = flatVal;
122
+ }
123
+ }
108
124
  } else {
109
125
  const flatVal = toFlatValue(value);
110
126
  if (flatVal !== undefined) {
@@ -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 type { Aggregate } from "./aggregate.model.ts";
10
11
  import { AlterAggregateChangeOwner } from "./changes/aggregate.alter.ts";
@@ -19,6 +20,10 @@ import {
19
20
  RevokeAggregatePrivileges,
20
21
  RevokeGrantOptionAggregatePrivileges,
21
22
  } from "./changes/aggregate.privilege.ts";
23
+ import {
24
+ CreateSecurityLabelOnAggregate,
25
+ DropSecurityLabelOnAggregate,
26
+ } from "./changes/aggregate.security-label.ts";
22
27
  import type { AggregateChange } from "./changes/aggregate.types.ts";
23
28
 
24
29
  export function diffAggregates(
@@ -51,6 +56,14 @@ export function diffAggregates(
51
56
  if (aggregate.comment !== null) {
52
57
  changes.push(new CreateCommentOnAggregate({ aggregate }));
53
58
  }
59
+ for (const label of aggregate.security_labels) {
60
+ changes.push(
61
+ new CreateSecurityLabelOnAggregate({
62
+ aggregate,
63
+ securityLabel: label,
64
+ }),
65
+ );
66
+ }
54
67
 
55
68
  // PRIVILEGES: For created objects, compare against default privileges state
56
69
  // The migration script will run ALTER DEFAULT PRIVILEGES before CREATE (via constraint spec),
@@ -182,6 +195,26 @@ export function diffAggregates(
182
195
  }
183
196
  }
184
197
 
198
+ // SECURITY LABELS
199
+ changes.push(
200
+ ...diffSecurityLabels<
201
+ CreateSecurityLabelOnAggregate | DropSecurityLabelOnAggregate
202
+ >(
203
+ mainAggregate.security_labels,
204
+ branchAggregate.security_labels,
205
+ (securityLabel) =>
206
+ new CreateSecurityLabelOnAggregate({
207
+ aggregate: branchAggregate,
208
+ securityLabel,
209
+ }),
210
+ (securityLabel) =>
211
+ new DropSecurityLabelOnAggregate({
212
+ aggregate: mainAggregate,
213
+ securityLabel,
214
+ }),
215
+ ),
216
+ );
217
+
185
218
  // PRIVILEGES
186
219
  // Filter out PUBLIC's built-in default EXECUTE privilege from main catalog
187
220
  // (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 AggregateKindSchema = z.enum([
11
15
  "n", // normal aggregate
@@ -75,6 +79,7 @@ const aggregatePropsSchema = z.object({
75
79
  owner: z.string(),
76
80
  comment: z.string().nullable(),
77
81
  privileges: z.array(privilegePropsSchema),
82
+ security_labels: z.array(securityLabelPropsSchema).default([]).optional(),
78
83
  });
79
84
 
80
85
  type AggregatePrivilegeProps = PrivilegeProps;
@@ -122,6 +127,7 @@ export class Aggregate extends BasePgModel {
122
127
  public readonly owner: AggregateProps["owner"];
123
128
  public readonly comment: AggregateProps["comment"];
124
129
  public readonly privileges: AggregatePrivilegeProps[];
130
+ public readonly security_labels: SecurityLabelProps[];
125
131
 
126
132
  constructor(props: AggregateProps) {
127
133
  super();
@@ -168,6 +174,7 @@ export class Aggregate extends BasePgModel {
168
174
  this.owner = props.owner;
169
175
  this.comment = props.comment;
170
176
  this.privileges = props.privileges;
177
+ this.security_labels = props.security_labels ?? [];
171
178
  }
172
179
 
173
180
  get stableId(): `aggregate:${string}` {
@@ -224,6 +231,7 @@ export class Aggregate extends BasePgModel {
224
231
  owner: this.owner,
225
232
  comment: this.comment,
226
233
  privileges: this.privileges,
234
+ security_labels: this.security_labels,
227
235
  };
228
236
  }
229
237
  }
@@ -294,7 +302,20 @@ select
294
302
  )
295
303
  from lateral aclexplode(COALESCE(p.proacl, acldefault('f', p.proowner))) as x(grantor, grantee, privilege_type, is_grantable)
296
304
  ), '[]'
297
- ) as privileges
305
+ ) as privileges,
306
+ coalesce(
307
+ (
308
+ select json_agg(
309
+ json_build_object('provider', sl.provider, 'label', sl.label)
310
+ order by sl.provider
311
+ )
312
+ from pg_catalog.pg_seclabel sl
313
+ where sl.objoid = p.oid
314
+ and sl.classoid = 'pg_proc'::regclass
315
+ and sl.objsubid = 0
316
+ ),
317
+ '[]'::json
318
+ ) as security_labels
298
319
  from
299
320
  pg_catalog.pg_proc p
300
321
  inner join pg_catalog.pg_aggregate a on a.aggfnoid = p.oid
@@ -3,7 +3,11 @@ import type { Aggregate } from "../aggregate.model.ts";
3
3
 
4
4
  abstract class BaseAggregateChange extends BaseChange {
5
5
  abstract readonly aggregate: Aggregate;
6
- abstract readonly scope: "object" | "comment" | "privilege";
6
+ abstract readonly scope:
7
+ | "object"
8
+ | "comment"
9
+ | "privilege"
10
+ | "security_label";
7
11
  readonly objectType: "aggregate" = "aggregate";
8
12
  }
9
13
 
@@ -0,0 +1,99 @@
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 { Aggregate } from "../aggregate.model.ts";
5
+ import {
6
+ CreateAggregateChange,
7
+ DropAggregateChange,
8
+ } from "./aggregate.base.ts";
9
+
10
+ export type SecurityLabelAggregate =
11
+ | CreateSecurityLabelOnAggregate
12
+ | DropSecurityLabelOnAggregate;
13
+
14
+ function aggregateIdentity(a: Aggregate): string {
15
+ return `${a.schema}.${a.name}(${a.identityArguments})`;
16
+ }
17
+
18
+ export class CreateSecurityLabelOnAggregate extends CreateAggregateChange {
19
+ public readonly aggregate: Aggregate;
20
+ public readonly securityLabel: SecurityLabelProps;
21
+ public readonly scope = "security_label" as const;
22
+
23
+ constructor(props: {
24
+ aggregate: Aggregate;
25
+ securityLabel: SecurityLabelProps;
26
+ }) {
27
+ super();
28
+ this.aggregate = props.aggregate;
29
+ this.securityLabel = props.securityLabel;
30
+ }
31
+
32
+ get creates() {
33
+ return [
34
+ stableId.securityLabel(
35
+ this.aggregate.stableId,
36
+ this.securityLabel.provider,
37
+ ),
38
+ ];
39
+ }
40
+
41
+ get requires() {
42
+ return [this.aggregate.stableId];
43
+ }
44
+
45
+ serialize(): string {
46
+ return [
47
+ "SECURITY LABEL FOR",
48
+ this.securityLabel.provider,
49
+ "ON AGGREGATE",
50
+ aggregateIdentity(this.aggregate),
51
+ "IS",
52
+ quoteLiteral(this.securityLabel.label),
53
+ ].join(" ");
54
+ }
55
+ }
56
+
57
+ export class DropSecurityLabelOnAggregate extends DropAggregateChange {
58
+ public readonly aggregate: Aggregate;
59
+ public readonly securityLabel: SecurityLabelProps;
60
+ public readonly scope = "security_label" as const;
61
+
62
+ constructor(props: {
63
+ aggregate: Aggregate;
64
+ securityLabel: SecurityLabelProps;
65
+ }) {
66
+ super();
67
+ this.aggregate = props.aggregate;
68
+ this.securityLabel = props.securityLabel;
69
+ }
70
+
71
+ get drops() {
72
+ return [
73
+ stableId.securityLabel(
74
+ this.aggregate.stableId,
75
+ this.securityLabel.provider,
76
+ ),
77
+ ];
78
+ }
79
+
80
+ get requires() {
81
+ return [
82
+ stableId.securityLabel(
83
+ this.aggregate.stableId,
84
+ this.securityLabel.provider,
85
+ ),
86
+ this.aggregate.stableId,
87
+ ];
88
+ }
89
+
90
+ serialize(): string {
91
+ return [
92
+ "SECURITY LABEL FOR",
93
+ this.securityLabel.provider,
94
+ "ON AGGREGATE",
95
+ aggregateIdentity(this.aggregate),
96
+ "IS NULL",
97
+ ].join(" ");
98
+ }
99
+ }
@@ -3,6 +3,7 @@ import type { CommentAggregate } from "./aggregate.comment.ts";
3
3
  import type { CreateAggregate } from "./aggregate.create.ts";
4
4
  import type { DropAggregate } from "./aggregate.drop.ts";
5
5
  import type { AggregatePrivilege } from "./aggregate.privilege.ts";
6
+ import type { SecurityLabelAggregate } from "./aggregate.security-label.ts";
6
7
 
7
8
  /** Union of all aggregate-related change variants (`objectType: "aggregate"`). @category Change Types */
8
9
  export type AggregateChange =
@@ -10,4 +11,5 @@ export type AggregateChange =
10
11
  | CommentAggregate
11
12
  | CreateAggregate
12
13
  | DropAggregate
13
- | AggregatePrivilege;
14
+ | AggregatePrivilege
15
+ | SecurityLabelAggregate;
@@ -1,4 +1,5 @@
1
1
  import z from "zod";
2
+ import { securityLabelPropsSchema } from "./security-label.types.ts";
2
3
  import { deepEqual } from "./utils.ts";
3
4
 
4
5
  export const columnPropsSchema = z.object({
@@ -18,6 +19,7 @@ export const columnPropsSchema = z.object({
18
19
  collation: z.string().nullable(),
19
20
  default: z.string().nullable(),
20
21
  comment: z.string().nullable(),
22
+ security_labels: z.array(securityLabelPropsSchema).optional(),
21
23
  });
22
24
 
23
25
  export type ColumnProps = z.infer<typeof columnPropsSchema>;
@@ -3,7 +3,11 @@ import type { Domain } from "../domain.model.ts";
3
3
 
4
4
  abstract class BaseDomainChange extends BaseChange {
5
5
  abstract readonly domain: Domain;
6
- abstract readonly scope: "object" | "comment" | "privilege";
6
+ abstract readonly scope:
7
+ | "object"
8
+ | "comment"
9
+ | "privilege"
10
+ | "security_label";
7
11
  readonly objectType: "domain" = "domain";
8
12
  }
9
13
 
@@ -0,0 +1,56 @@
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 { Domain, type DomainProps } from "../domain.model.ts";
5
+ import {
6
+ CreateSecurityLabelOnDomain,
7
+ DropSecurityLabelOnDomain,
8
+ } from "./domain.security-label.ts";
9
+
10
+ const makeDomain = (): Domain =>
11
+ new Domain({
12
+ schema: "public",
13
+ name: "email_t",
14
+ base_type: "text",
15
+ base_type_schema: "pg_catalog",
16
+ not_null: false,
17
+ type_modifier: -1,
18
+ array_dimensions: 0,
19
+ collation: null,
20
+ default_bin: null,
21
+ default_value: null,
22
+ owner: "postgres",
23
+ comment: null,
24
+ constraints: [],
25
+ privileges: [],
26
+ } as DomainProps);
27
+
28
+ describe("domain.security-label", () => {
29
+ test("create serializes", async () => {
30
+ const domain = makeDomain();
31
+ const change = new CreateSecurityLabelOnDomain({
32
+ domain,
33
+ securityLabel: { provider: "dummy", label: "classified" },
34
+ });
35
+ expect(change.scope).toBe("security_label");
36
+ expect(change.creates).toEqual([
37
+ stableId.securityLabel(domain.stableId, "dummy"),
38
+ ]);
39
+ await assertValidSql(change.serialize());
40
+ expect(change.serialize()).toBe(
41
+ "SECURITY LABEL FOR dummy ON DOMAIN public.email_t IS 'classified'",
42
+ );
43
+ });
44
+
45
+ test("drop serializes to IS NULL", async () => {
46
+ const domain = makeDomain();
47
+ const change = new DropSecurityLabelOnDomain({
48
+ domain,
49
+ securityLabel: { provider: "dummy", label: "x" },
50
+ });
51
+ await assertValidSql(change.serialize());
52
+ expect(change.serialize()).toBe(
53
+ "SECURITY LABEL FOR dummy ON DOMAIN public.email_t IS NULL",
54
+ );
55
+ });
56
+ });
@@ -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 { Domain } from "../domain.model.ts";
5
+ import { CreateDomainChange, DropDomainChange } from "./domain.base.ts";
6
+
7
+ export type SecurityLabelDomain =
8
+ | CreateSecurityLabelOnDomain
9
+ | DropSecurityLabelOnDomain;
10
+
11
+ export class CreateSecurityLabelOnDomain extends CreateDomainChange {
12
+ public readonly domain: Domain;
13
+ public readonly securityLabel: SecurityLabelProps;
14
+ public readonly scope = "security_label" as const;
15
+
16
+ constructor(props: { domain: Domain; securityLabel: SecurityLabelProps }) {
17
+ super();
18
+ this.domain = props.domain;
19
+ this.securityLabel = props.securityLabel;
20
+ }
21
+
22
+ get creates() {
23
+ return [
24
+ stableId.securityLabel(this.domain.stableId, this.securityLabel.provider),
25
+ ];
26
+ }
27
+
28
+ get requires() {
29
+ return [this.domain.stableId];
30
+ }
31
+
32
+ serialize(): string {
33
+ return [
34
+ "SECURITY LABEL FOR",
35
+ this.securityLabel.provider,
36
+ "ON DOMAIN",
37
+ `${this.domain.schema}.${this.domain.name}`,
38
+ "IS",
39
+ quoteLiteral(this.securityLabel.label),
40
+ ].join(" ");
41
+ }
42
+ }
43
+
44
+ export class DropSecurityLabelOnDomain extends DropDomainChange {
45
+ public readonly domain: Domain;
46
+ public readonly securityLabel: SecurityLabelProps;
47
+ public readonly scope = "security_label" as const;
48
+
49
+ constructor(props: { domain: Domain; securityLabel: SecurityLabelProps }) {
50
+ super();
51
+ this.domain = props.domain;
52
+ this.securityLabel = props.securityLabel;
53
+ }
54
+
55
+ get drops() {
56
+ return [
57
+ stableId.securityLabel(this.domain.stableId, this.securityLabel.provider),
58
+ ];
59
+ }
60
+
61
+ get requires() {
62
+ return [
63
+ stableId.securityLabel(this.domain.stableId, this.securityLabel.provider),
64
+ this.domain.stableId,
65
+ ];
66
+ }
67
+
68
+ serialize(): string {
69
+ return [
70
+ "SECURITY LABEL FOR",
71
+ this.securityLabel.provider,
72
+ "ON DOMAIN",
73
+ `${this.domain.schema}.${this.domain.name}`,
74
+ "IS NULL",
75
+ ].join(" ");
76
+ }
77
+ }
@@ -3,6 +3,7 @@ import type { CommentDomain } from "./domain.comment.ts";
3
3
  import type { CreateDomain } from "./domain.create.ts";
4
4
  import type { DropDomain } from "./domain.drop.ts";
5
5
  import type { DomainPrivilege } from "./domain.privilege.ts";
6
+ import type { SecurityLabelDomain } from "./domain.security-label.ts";
6
7
 
7
8
  /** Union of all domain-related change variants (`objectType: "domain"`). @category Change Types */
8
9
  export type DomainChange =
@@ -10,4 +11,5 @@ export type DomainChange =
10
11
  | CommentDomain
11
12
  | CreateDomain
12
13
  | DropDomain
13
- | DomainPrivilege;
14
+ | DomainPrivilege
15
+ | SecurityLabelDomain;
@@ -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
  AlterDomainAddConstraint,
10
11
  AlterDomainChangeOwner,
@@ -26,6 +27,10 @@ import {
26
27
  RevokeDomainPrivileges,
27
28
  RevokeGrantOptionDomainPrivileges,
28
29
  } from "./changes/domain.privilege.ts";
30
+ import {
31
+ CreateSecurityLabelOnDomain,
32
+ DropSecurityLabelOnDomain,
33
+ } from "./changes/domain.security-label.ts";
29
34
  import type { DomainChange } from "./changes/domain.types.ts";
30
35
  import type { Domain } from "./domain.model.ts";
31
36
 
@@ -67,6 +72,14 @@ export function diffDomains(
67
72
  if (newDomain.comment !== null) {
68
73
  changes.push(new CreateCommentOnDomain({ domain: newDomain }));
69
74
  }
75
+ for (const label of newDomain.security_labels) {
76
+ changes.push(
77
+ new CreateSecurityLabelOnDomain({
78
+ domain: newDomain,
79
+ securityLabel: label,
80
+ }),
81
+ );
82
+ }
70
83
  // For unvalidated constraints, CREATE DOMAIN cannot specify NOT VALID.
71
84
  // Add them after creation and validate to match branch state semantics.
72
85
  // For already validated constraints, they are emitted inline in CREATE DOMAIN.
@@ -255,6 +268,26 @@ export function diffDomains(
255
268
  }
256
269
  }
257
270
 
271
+ // SECURITY LABELS
272
+ changes.push(
273
+ ...diffSecurityLabels<
274
+ CreateSecurityLabelOnDomain | DropSecurityLabelOnDomain
275
+ >(
276
+ mainDomain.security_labels,
277
+ branchDomain.security_labels,
278
+ (securityLabel) =>
279
+ new CreateSecurityLabelOnDomain({
280
+ domain: branchDomain,
281
+ securityLabel,
282
+ }),
283
+ (securityLabel) =>
284
+ new DropSecurityLabelOnDomain({
285
+ domain: mainDomain,
286
+ securityLabel,
287
+ }),
288
+ ),
289
+ );
290
+
258
291
  // PRIVILEGES
259
292
  // Filter out PUBLIC's built-in default USAGE privilege from main catalog
260
293
  // (PostgreSQL grants it automatically, so we shouldn't compare it)