@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
@@ -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 enumLabelSchema = z.object({
11
15
  sort_order: z.number(),
@@ -33,6 +37,7 @@ const enumPropsSchema = z.object({
33
37
  labels: z.array(enumLabelSchema),
34
38
  comment: z.string().nullable(),
35
39
  privileges: z.array(privilegePropsSchema),
40
+ security_labels: z.array(securityLabelPropsSchema).default([]).optional(),
36
41
  });
37
42
 
38
43
  type EnumPrivilegeProps = PrivilegeProps;
@@ -45,6 +50,7 @@ export class Enum extends BasePgModel {
45
50
  public readonly labels: EnumProps["labels"];
46
51
  public readonly comment: EnumProps["comment"];
47
52
  public readonly privileges: EnumPrivilegeProps[];
53
+ public readonly security_labels: SecurityLabelProps[];
48
54
 
49
55
  constructor(props: EnumProps) {
50
56
  super();
@@ -58,6 +64,7 @@ export class Enum extends BasePgModel {
58
64
  this.labels = props.labels;
59
65
  this.comment = props.comment;
60
66
  this.privileges = props.privileges;
67
+ this.security_labels = props.security_labels ?? [];
61
68
  }
62
69
 
63
70
  get stableId(): `type:${string}` {
@@ -105,6 +112,7 @@ export class Enum extends BasePgModel {
105
112
  labels,
106
113
  comment: this.comment,
107
114
  privileges,
115
+ security_labels: this.security_labels,
108
116
  };
109
117
  }
110
118
  }
@@ -118,6 +126,7 @@ export async function extractEnums(pool: Pool): Promise<Enum[]> {
118
126
  owner: string;
119
127
  comment: string | null;
120
128
  privileges: { grantee: string; privilege: string; grantable: boolean }[];
129
+ security_labels: { provider: string; label: string }[];
121
130
  }>(sql`
122
131
  with extension_oids as (
123
132
  select
@@ -147,7 +156,20 @@ select
147
156
  )
148
157
  from lateral aclexplode(COALESCE(t.typacl, acldefault('T', t.typowner))) as x(grantor, grantee, privilege_type, is_grantable)
149
158
  ), '[]'
150
- ) as privileges
159
+ ) as privileges,
160
+ coalesce(
161
+ (
162
+ select json_agg(
163
+ json_build_object('provider', sl.provider, 'label', sl.label)
164
+ order by sl.provider
165
+ )
166
+ from pg_catalog.pg_seclabel sl
167
+ where sl.objoid = t.oid
168
+ and sl.classoid = 'pg_type'::regclass
169
+ and sl.objsubid = 0
170
+ ),
171
+ '[]'::json
172
+ ) as security_labels
151
173
  from
152
174
  pg_catalog.pg_enum e
153
175
  inner join pg_catalog.pg_type t on t.oid = e.enumtypid
@@ -170,6 +192,7 @@ order by
170
192
  privilege: string;
171
193
  grantable: boolean;
172
194
  }[];
195
+ security_labels: { provider: string; label: string }[];
173
196
  }
174
197
  > = {};
175
198
  for (const e of enumRows) {
@@ -182,6 +205,7 @@ order by
182
205
  labels: [],
183
206
  comment: e.comment,
184
207
  privileges: e.privileges,
208
+ security_labels: e.security_labels,
185
209
  };
186
210
  }
187
211
  grouped[key].labels.push({ sort_order: e.sort_order, label: e.label });
@@ -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
@@ -77,6 +77,9 @@ export const stableId = {
77
77
  comment(objectStableId: string) {
78
78
  return `comment:${objectStableId}` as const;
79
79
  },
80
+ securityLabel(objectStableId: string, provider: string) {
81
+ return `securityLabel:${objectStableId}::provider:${provider}` as const;
82
+ },
80
83
  role(role: string) {
81
84
  return `role:${role}` as const;
82
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()
@@ -15,6 +15,11 @@ import {
15
15
  type ExtractRetryOptions,
16
16
  extractWithDefinitionRetry,
17
17
  } from "../extract-with-retry.ts";
18
+ import {
19
+ normalizeSecurityLabels,
20
+ type SecurityLabelProps,
21
+ securityLabelPropsSchema,
22
+ } from "../security-label.types.ts";
18
23
  import { ReplicaIdentitySchema } from "../table/table.model.ts";
19
24
 
20
25
  const viewPropsSchema = z.object({
@@ -36,6 +41,7 @@ const viewPropsSchema = z.object({
36
41
  comment: z.string().nullable(),
37
42
  columns: z.array(columnPropsSchema),
38
43
  privileges: z.array(privilegePropsSchema),
44
+ security_labels: z.array(securityLabelPropsSchema).default([]).optional(),
39
45
  });
40
46
 
41
47
  // pg_get_viewdef(oid) can return NULL when the underlying view (or its
@@ -69,6 +75,7 @@ export class View extends BasePgModel implements TableLikeObject {
69
75
  public readonly comment: ViewProps["comment"];
70
76
  public readonly columns: ViewProps["columns"];
71
77
  public readonly privileges: ViewPrivilegeProps[];
78
+ public readonly security_labels: SecurityLabelProps[];
72
79
 
73
80
  constructor(props: ViewProps) {
74
81
  super();
@@ -94,6 +101,7 @@ export class View extends BasePgModel implements TableLikeObject {
94
101
  this.comment = props.comment;
95
102
  this.columns = props.columns;
96
103
  this.privileges = props.privileges;
104
+ this.security_labels = props.security_labels ?? [];
97
105
  }
98
106
 
99
107
  get stableId(): `view:${string}` {
@@ -125,6 +133,7 @@ export class View extends BasePgModel implements TableLikeObject {
125
133
  comment: this.comment,
126
134
  columns: this.columns,
127
135
  privileges: this.privileges,
136
+ security_labels: this.security_labels,
128
137
  };
129
138
  }
130
139
 
@@ -134,6 +143,7 @@ export class View extends BasePgModel implements TableLikeObject {
134
143
  data: {
135
144
  ...this.dataFields,
136
145
  columns: normalizeColumns(this.columns),
146
+ security_labels: normalizeSecurityLabels(this.security_labels),
137
147
  },
138
148
  };
139
149
  }
@@ -264,7 +274,20 @@ select
264
274
  join lateral aclexplode(src.acl) as x(grantor, grantee, privilege_type, is_grantable) on true
265
275
  group by x.grantee, x.privilege_type
266
276
  ) as grp
267
- ), '[]') 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
268
291
  from
269
292
  views v
270
293
  left join pg_attribute a on a.attrelid = v.oid and a.attnum > 0 and not a.attisdropped
@@ -281,5 +304,5 @@ order by
281
304
  const validatedRows = viewRows.filter(
282
305
  (row): row is ViewProps => row.definition !== null,
283
306
  );
284
- return validatedRows.map((row: ViewProps) => new View(row));
307
+ return validatedRows.map((row) => new View(row));
285
308
  }
@@ -1119,6 +1119,7 @@ const schema = new Schema({
1119
1119
  owner: "admin",
1120
1120
  comment: "application schema",
1121
1121
  privileges: [],
1122
+ security_labels: [],
1122
1123
  });
1123
1124
 
1124
1125
  const extension = new Extension({