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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (271) hide show
  1. package/dist/core/catalog.diff.js +4 -3
  2. package/dist/core/catalog.model.d.ts +8 -1
  3. package/dist/core/catalog.model.js +10 -8
  4. package/dist/core/expand-replace-dependencies.js +23 -0
  5. package/dist/core/integrations/filter/flatten.js +13 -0
  6. package/dist/core/objects/aggregate/aggregate.diff.js +16 -0
  7. package/dist/core/objects/aggregate/aggregate.model.d.ts +10 -0
  8. package/dist/core/objects/aggregate/aggregate.model.js +19 -1
  9. package/dist/core/objects/aggregate/changes/aggregate.base.d.ts +1 -1
  10. package/dist/core/objects/aggregate/changes/aggregate.security-label.d.ts +28 -0
  11. package/dist/core/objects/aggregate/changes/aggregate.security-label.js +64 -0
  12. package/dist/core/objects/aggregate/changes/aggregate.types.d.ts +2 -1
  13. package/dist/core/objects/base.model.d.ts +8 -0
  14. package/dist/core/objects/base.model.js +2 -0
  15. package/dist/core/objects/domain/changes/domain.base.d.ts +1 -1
  16. package/dist/core/objects/domain/changes/domain.security-label.d.ts +28 -0
  17. package/dist/core/objects/domain/changes/domain.security-label.js +61 -0
  18. package/dist/core/objects/domain/changes/domain.types.d.ts +2 -1
  19. package/dist/core/objects/domain/domain.diff.js +16 -0
  20. package/dist/core/objects/domain/domain.model.d.ts +10 -0
  21. package/dist/core/objects/domain/domain.model.js +19 -1
  22. package/dist/core/objects/event-trigger/changes/event-trigger.base.d.ts +1 -1
  23. package/dist/core/objects/event-trigger/changes/event-trigger.security-label.d.ts +28 -0
  24. package/dist/core/objects/event-trigger/changes/event-trigger.security-label.js +61 -0
  25. package/dist/core/objects/event-trigger/changes/event-trigger.types.d.ts +2 -1
  26. package/dist/core/objects/event-trigger/event-trigger.diff.js +16 -0
  27. package/dist/core/objects/event-trigger/event-trigger.model.d.ts +10 -0
  28. package/dist/core/objects/event-trigger/event-trigger.model.js +19 -1
  29. package/dist/core/objects/extract-with-retry.d.ts +36 -0
  30. package/dist/core/objects/extract-with-retry.js +51 -0
  31. package/dist/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.base.d.ts +1 -1
  32. package/dist/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.security-label.d.ts +28 -0
  33. package/dist/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.security-label.js +61 -0
  34. package/dist/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.types.d.ts +2 -1
  35. package/dist/core/objects/foreign-data-wrapper/foreign-table/foreign-table.diff.js +16 -0
  36. package/dist/core/objects/foreign-data-wrapper/foreign-table/foreign-table.model.d.ts +22 -0
  37. package/dist/core/objects/foreign-data-wrapper/foreign-table/foreign-table.model.js +20 -1
  38. package/dist/core/objects/index/index.diff.js +0 -1
  39. package/dist/core/objects/index/index.model.d.ts +2 -3
  40. package/dist/core/objects/index/index.model.js +17 -6
  41. package/dist/core/objects/materialized-view/changes/materialized-view.base.d.ts +1 -1
  42. package/dist/core/objects/materialized-view/changes/materialized-view.security-label.d.ts +28 -0
  43. package/dist/core/objects/materialized-view/changes/materialized-view.security-label.js +61 -0
  44. package/dist/core/objects/materialized-view/changes/materialized-view.types.d.ts +2 -1
  45. package/dist/core/objects/materialized-view/materialized-view.diff.js +18 -0
  46. package/dist/core/objects/materialized-view/materialized-view.model.d.ts +24 -1
  47. package/dist/core/objects/materialized-view/materialized-view.model.js +40 -5
  48. package/dist/core/objects/procedure/changes/procedure.base.d.ts +1 -1
  49. package/dist/core/objects/procedure/changes/procedure.security-label.d.ts +28 -0
  50. package/dist/core/objects/procedure/changes/procedure.security-label.js +69 -0
  51. package/dist/core/objects/procedure/changes/procedure.types.d.ts +2 -1
  52. package/dist/core/objects/procedure/procedure.diff.js +16 -0
  53. package/dist/core/objects/procedure/procedure.model.d.ts +12 -1
  54. package/dist/core/objects/procedure/procedure.model.js +39 -5
  55. package/dist/core/objects/publication/changes/publication.base.d.ts +1 -1
  56. package/dist/core/objects/publication/changes/publication.security-label.d.ts +28 -0
  57. package/dist/core/objects/publication/changes/publication.security-label.js +61 -0
  58. package/dist/core/objects/publication/changes/publication.types.d.ts +2 -1
  59. package/dist/core/objects/publication/publication.diff.js +16 -0
  60. package/dist/core/objects/publication/publication.model.d.ts +14 -0
  61. package/dist/core/objects/publication/publication.model.js +20 -1
  62. package/dist/core/objects/rls-policy/rls-policy.diff.js +13 -1
  63. package/dist/core/objects/role/changes/role.base.d.ts +1 -1
  64. package/dist/core/objects/role/changes/role.security-label.d.ts +28 -0
  65. package/dist/core/objects/role/changes/role.security-label.js +61 -0
  66. package/dist/core/objects/role/changes/role.types.d.ts +2 -1
  67. package/dist/core/objects/role/role.diff.js +16 -0
  68. package/dist/core/objects/role/role.model.d.ts +10 -0
  69. package/dist/core/objects/role/role.model.js +29 -0
  70. package/dist/core/objects/rule/rule.model.d.ts +2 -1
  71. package/dist/core/objects/rule/rule.model.js +20 -3
  72. package/dist/core/objects/schema/changes/schema.base.d.ts +1 -1
  73. package/dist/core/objects/schema/changes/schema.security-label.d.ts +28 -0
  74. package/dist/core/objects/schema/changes/schema.security-label.js +61 -0
  75. package/dist/core/objects/schema/changes/schema.types.d.ts +2 -1
  76. package/dist/core/objects/schema/schema.diff.js +24 -1
  77. package/dist/core/objects/schema/schema.model.d.ts +10 -0
  78. package/dist/core/objects/schema/schema.model.js +18 -1
  79. package/dist/core/objects/security-label.types.d.ts +20 -0
  80. package/dist/core/objects/security-label.types.js +46 -0
  81. package/dist/core/objects/sequence/changes/sequence.base.d.ts +1 -1
  82. package/dist/core/objects/sequence/changes/sequence.security-label.d.ts +28 -0
  83. package/dist/core/objects/sequence/changes/sequence.security-label.js +61 -0
  84. package/dist/core/objects/sequence/changes/sequence.types.d.ts +2 -1
  85. package/dist/core/objects/sequence/sequence.diff.d.ts +2 -1
  86. package/dist/core/objects/sequence/sequence.diff.js +44 -4
  87. package/dist/core/objects/sequence/sequence.model.d.ts +10 -0
  88. package/dist/core/objects/sequence/sequence.model.js +19 -1
  89. package/dist/core/objects/subscription/changes/subscription.base.d.ts +1 -1
  90. package/dist/core/objects/subscription/changes/subscription.security-label.d.ts +28 -0
  91. package/dist/core/objects/subscription/changes/subscription.security-label.js +61 -0
  92. package/dist/core/objects/subscription/changes/subscription.types.d.ts +2 -1
  93. package/dist/core/objects/subscription/subscription.diff.js +16 -0
  94. package/dist/core/objects/subscription/subscription.model.d.ts +10 -0
  95. package/dist/core/objects/subscription/subscription.model.js +19 -1
  96. package/dist/core/objects/table/changes/table.alter.d.ts +12 -1
  97. package/dist/core/objects/table/changes/table.alter.js +20 -2
  98. package/dist/core/objects/table/changes/table.base.d.ts +1 -1
  99. package/dist/core/objects/table/changes/table.security-label.d.ts +63 -0
  100. package/dist/core/objects/table/changes/table.security-label.js +134 -0
  101. package/dist/core/objects/table/changes/table.types.d.ts +2 -1
  102. package/dist/core/objects/table/table.diff.js +68 -15
  103. package/dist/core/objects/table/table.model.d.ts +36 -1
  104. package/dist/core/objects/table/table.model.js +74 -7
  105. package/dist/core/objects/trigger/trigger.model.d.ts +2 -1
  106. package/dist/core/objects/trigger/trigger.model.js +20 -4
  107. package/dist/core/objects/type/composite-type/changes/composite-type.base.d.ts +1 -1
  108. package/dist/core/objects/type/composite-type/changes/composite-type.security-label.d.ts +28 -0
  109. package/dist/core/objects/type/composite-type/changes/composite-type.security-label.js +61 -0
  110. package/dist/core/objects/type/composite-type/changes/composite-type.types.d.ts +2 -1
  111. package/dist/core/objects/type/composite-type/composite-type.diff.js +16 -0
  112. package/dist/core/objects/type/composite-type/composite-type.model.d.ts +22 -0
  113. package/dist/core/objects/type/composite-type/composite-type.model.js +22 -2
  114. package/dist/core/objects/type/enum/changes/enum.base.d.ts +1 -1
  115. package/dist/core/objects/type/enum/changes/enum.security-label.d.ts +28 -0
  116. package/dist/core/objects/type/enum/changes/enum.security-label.js +61 -0
  117. package/dist/core/objects/type/enum/changes/enum.types.d.ts +2 -1
  118. package/dist/core/objects/type/enum/enum.diff.js +16 -0
  119. package/dist/core/objects/type/enum/enum.model.d.ts +10 -0
  120. package/dist/core/objects/type/enum/enum.model.js +20 -1
  121. package/dist/core/objects/type/range/changes/range.base.d.ts +1 -1
  122. package/dist/core/objects/type/range/changes/range.security-label.d.ts +28 -0
  123. package/dist/core/objects/type/range/changes/range.security-label.js +61 -0
  124. package/dist/core/objects/type/range/changes/range.types.d.ts +2 -1
  125. package/dist/core/objects/type/range/range.diff.js +16 -0
  126. package/dist/core/objects/type/range/range.model.d.ts +10 -0
  127. package/dist/core/objects/type/range/range.model.js +19 -1
  128. package/dist/core/objects/utils.d.ts +2 -0
  129. package/dist/core/objects/utils.js +6 -0
  130. package/dist/core/objects/view/changes/view.base.d.ts +1 -1
  131. package/dist/core/objects/view/changes/view.security-label.d.ts +28 -0
  132. package/dist/core/objects/view/changes/view.security-label.js +61 -0
  133. package/dist/core/objects/view/changes/view.types.d.ts +2 -1
  134. package/dist/core/objects/view/view.diff.js +13 -0
  135. package/dist/core/objects/view/view.model.d.ts +28 -1
  136. package/dist/core/objects/view/view.model.js +40 -5
  137. package/dist/core/plan/create.js +3 -1
  138. package/dist/core/plan/sql-format/fixtures.js +1 -0
  139. package/dist/core/plan/types.d.ts +8 -0
  140. package/dist/core/{post-diff-cycle-breaking.d.ts → post-diff-normalization.d.ts} +8 -1
  141. package/dist/core/post-diff-normalization.js +202 -0
  142. package/dist/core/sort/cycle-breakers.js +1 -1
  143. package/dist/core/sort/utils.d.ts +10 -0
  144. package/dist/core/sort/utils.js +28 -0
  145. package/package.json +1 -1
  146. package/src/core/catalog.diff.ts +4 -2
  147. package/src/core/catalog.model.ts +21 -8
  148. package/src/core/expand-replace-dependencies.test.ts +131 -0
  149. package/src/core/expand-replace-dependencies.ts +24 -0
  150. package/src/core/integrations/filter/dsl.test.ts +27 -0
  151. package/src/core/integrations/filter/flatten.ts +16 -0
  152. package/src/core/objects/aggregate/aggregate.diff.ts +33 -0
  153. package/src/core/objects/aggregate/aggregate.model.ts +22 -1
  154. package/src/core/objects/aggregate/changes/aggregate.base.ts +5 -1
  155. package/src/core/objects/aggregate/changes/aggregate.security-label.ts +99 -0
  156. package/src/core/objects/aggregate/changes/aggregate.types.ts +3 -1
  157. package/src/core/objects/base.model.ts +2 -0
  158. package/src/core/objects/domain/changes/domain.base.ts +5 -1
  159. package/src/core/objects/domain/changes/domain.security-label.test.ts +56 -0
  160. package/src/core/objects/domain/changes/domain.security-label.ts +77 -0
  161. package/src/core/objects/domain/changes/domain.types.ts +3 -1
  162. package/src/core/objects/domain/domain.diff.ts +33 -0
  163. package/src/core/objects/domain/domain.model.ts +22 -1
  164. package/src/core/objects/event-trigger/changes/event-trigger.base.ts +1 -1
  165. package/src/core/objects/event-trigger/changes/event-trigger.security-label.ts +95 -0
  166. package/src/core/objects/event-trigger/changes/event-trigger.types.ts +3 -1
  167. package/src/core/objects/event-trigger/event-trigger.diff.ts +33 -0
  168. package/src/core/objects/event-trigger/event-trigger.model.ts +22 -1
  169. package/src/core/objects/extract-with-retry.test.ts +143 -0
  170. package/src/core/objects/extract-with-retry.ts +87 -0
  171. package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.base.ts +5 -1
  172. package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.security-label.ts +95 -0
  173. package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.types.ts +3 -1
  174. package/src/core/objects/foreign-data-wrapper/foreign-table/foreign-table.diff.ts +33 -0
  175. package/src/core/objects/foreign-data-wrapper/foreign-table/foreign-table.model.ts +24 -1
  176. package/src/core/objects/index/index.diff.ts +0 -1
  177. package/src/core/objects/index/index.model.test.ts +37 -1
  178. package/src/core/objects/index/index.model.ts +25 -6
  179. package/src/core/objects/materialized-view/changes/materialized-view.base.ts +5 -1
  180. package/src/core/objects/materialized-view/changes/materialized-view.security-label.test.ts +63 -0
  181. package/src/core/objects/materialized-view/changes/materialized-view.security-label.ts +95 -0
  182. package/src/core/objects/materialized-view/changes/materialized-view.types.ts +3 -1
  183. package/src/core/objects/materialized-view/materialized-view.diff.ts +37 -0
  184. package/src/core/objects/materialized-view/materialized-view.model.test.ts +93 -0
  185. package/src/core/objects/materialized-view/materialized-view.model.ts +52 -8
  186. package/src/core/objects/procedure/changes/procedure.base.ts +5 -1
  187. package/src/core/objects/procedure/changes/procedure.security-label.ts +105 -0
  188. package/src/core/objects/procedure/changes/procedure.types.ts +3 -1
  189. package/src/core/objects/procedure/procedure.diff.ts +33 -0
  190. package/src/core/objects/procedure/procedure.model.test.ts +117 -0
  191. package/src/core/objects/procedure/procedure.model.ts +51 -7
  192. package/src/core/objects/publication/changes/publication.base.ts +1 -1
  193. package/src/core/objects/publication/changes/publication.security-label.ts +95 -0
  194. package/src/core/objects/publication/changes/publication.types.ts +3 -1
  195. package/src/core/objects/publication/publication.diff.ts +33 -0
  196. package/src/core/objects/publication/publication.model.ts +24 -1
  197. package/src/core/objects/rls-policy/rls-policy.diff.ts +19 -1
  198. package/src/core/objects/role/changes/role.base.ts +2 -1
  199. package/src/core/objects/role/changes/role.security-label.ts +77 -0
  200. package/src/core/objects/role/changes/role.types.ts +3 -1
  201. package/src/core/objects/role/role.diff.ts +33 -0
  202. package/src/core/objects/role/role.model.ts +32 -0
  203. package/src/core/objects/rule/rule.model.test.ts +99 -0
  204. package/src/core/objects/rule/rule.model.ts +28 -4
  205. package/src/core/objects/schema/changes/schema.alter.test.ts +1 -0
  206. package/src/core/objects/schema/changes/schema.base.ts +5 -1
  207. package/src/core/objects/schema/changes/schema.create.test.ts +1 -0
  208. package/src/core/objects/schema/changes/schema.drop.test.ts +1 -0
  209. package/src/core/objects/schema/changes/schema.security-label.test.ts +76 -0
  210. package/src/core/objects/schema/changes/schema.security-label.ts +77 -0
  211. package/src/core/objects/schema/changes/schema.types.ts +3 -1
  212. package/src/core/objects/schema/schema.diff.test.ts +1 -0
  213. package/src/core/objects/schema/schema.diff.ts +43 -1
  214. package/src/core/objects/schema/schema.model.ts +21 -1
  215. package/src/core/objects/security-label.types.test.ts +106 -0
  216. package/src/core/objects/security-label.types.ts +61 -0
  217. package/src/core/objects/sequence/changes/sequence.base.ts +5 -1
  218. package/src/core/objects/sequence/changes/sequence.security-label.test.ts +58 -0
  219. package/src/core/objects/sequence/changes/sequence.security-label.ts +92 -0
  220. package/src/core/objects/sequence/changes/sequence.types.ts +3 -1
  221. package/src/core/objects/sequence/sequence.diff.test.ts +87 -0
  222. package/src/core/objects/sequence/sequence.diff.ts +64 -6
  223. package/src/core/objects/sequence/sequence.model.ts +22 -1
  224. package/src/core/objects/subscription/changes/subscription.base.ts +1 -1
  225. package/src/core/objects/subscription/changes/subscription.security-label.ts +95 -0
  226. package/src/core/objects/subscription/changes/subscription.types.ts +3 -1
  227. package/src/core/objects/subscription/subscription.diff.ts +33 -0
  228. package/src/core/objects/subscription/subscription.model.ts +22 -1
  229. package/src/core/objects/table/changes/table.alter.test.ts +13 -21
  230. package/src/core/objects/table/changes/table.alter.ts +30 -3
  231. package/src/core/objects/table/changes/table.base.ts +5 -1
  232. package/src/core/objects/table/changes/table.security-label.test.ts +140 -0
  233. package/src/core/objects/table/changes/table.security-label.ts +183 -0
  234. package/src/core/objects/table/changes/table.types.ts +3 -1
  235. package/src/core/objects/table/table.diff.ts +111 -19
  236. package/src/core/objects/table/table.model.test.ts +209 -0
  237. package/src/core/objects/table/table.model.ts +94 -9
  238. package/src/core/objects/trigger/trigger.model.test.ts +113 -0
  239. package/src/core/objects/trigger/trigger.model.ts +28 -5
  240. package/src/core/objects/type/composite-type/changes/composite-type.base.ts +5 -1
  241. package/src/core/objects/type/composite-type/changes/composite-type.security-label.ts +95 -0
  242. package/src/core/objects/type/composite-type/changes/composite-type.types.ts +3 -1
  243. package/src/core/objects/type/composite-type/composite-type.diff.ts +33 -0
  244. package/src/core/objects/type/composite-type/composite-type.model.ts +26 -2
  245. package/src/core/objects/type/enum/changes/enum.base.ts +5 -1
  246. package/src/core/objects/type/enum/changes/enum.security-label.ts +77 -0
  247. package/src/core/objects/type/enum/changes/enum.types.ts +3 -1
  248. package/src/core/objects/type/enum/enum.diff.ts +33 -0
  249. package/src/core/objects/type/enum/enum.model.ts +25 -1
  250. package/src/core/objects/type/range/changes/range.base.ts +5 -1
  251. package/src/core/objects/type/range/changes/range.security-label.ts +77 -0
  252. package/src/core/objects/type/range/changes/range.types.ts +3 -1
  253. package/src/core/objects/type/range/range.diff.ts +33 -0
  254. package/src/core/objects/type/range/range.model.ts +22 -1
  255. package/src/core/objects/utils.ts +6 -0
  256. package/src/core/objects/view/changes/view.base.ts +5 -1
  257. package/src/core/objects/view/changes/view.security-label.test.ts +64 -0
  258. package/src/core/objects/view/changes/view.security-label.ts +77 -0
  259. package/src/core/objects/view/changes/view.types.ts +3 -1
  260. package/src/core/objects/view/view.diff.ts +31 -0
  261. package/src/core/objects/view/view.model.test.ts +90 -0
  262. package/src/core/objects/view/view.model.ts +53 -7
  263. package/src/core/plan/create.ts +3 -1
  264. package/src/core/plan/sql-format/fixtures.ts +1 -0
  265. package/src/core/plan/types.ts +8 -0
  266. package/src/core/{post-diff-cycle-breaking.test.ts → post-diff-normalization.test.ts} +168 -4
  267. package/src/core/post-diff-normalization.ts +260 -0
  268. package/src/core/sort/cycle-breakers.ts +1 -1
  269. package/src/core/sort/utils.ts +38 -0
  270. package/dist/core/post-diff-cycle-breaking.js +0 -100
  271. package/src/core/post-diff-cycle-breaking.ts +0 -138
@@ -2,6 +2,10 @@ import { sql } from "@ts-safeql/sql-tag";
2
2
  import type { Pool } from "pg";
3
3
  import z from "zod";
4
4
  import { BasePgModel } from "../base.model.ts";
5
+ import {
6
+ type SecurityLabelProps,
7
+ securityLabelPropsSchema,
8
+ } from "../security-label.types.ts";
5
9
 
6
10
  const EventTriggerEnabledSchema = z.enum([
7
11
  "O", // ORIGIN - trigger fires in origin mode
@@ -19,6 +23,7 @@ const eventTriggerPropsSchema = z.object({
19
23
  tags: z.array(z.string()).nullable(),
20
24
  owner: z.string(),
21
25
  comment: z.string().nullable(),
26
+ security_labels: z.array(securityLabelPropsSchema).default([]).optional(),
22
27
  });
23
28
 
24
29
  export type EventTriggerProps = z.infer<typeof eventTriggerPropsSchema>;
@@ -32,6 +37,7 @@ export class EventTrigger extends BasePgModel {
32
37
  public readonly tags: EventTriggerProps["tags"];
33
38
  public readonly owner: EventTriggerProps["owner"];
34
39
  public readonly comment: EventTriggerProps["comment"];
40
+ public readonly security_labels: SecurityLabelProps[];
35
41
 
36
42
  constructor(props: EventTriggerProps) {
37
43
  super();
@@ -47,6 +53,7 @@ export class EventTrigger extends BasePgModel {
47
53
  this.tags = props.tags;
48
54
  this.owner = props.owner;
49
55
  this.comment = props.comment;
56
+ this.security_labels = props.security_labels ?? [];
50
57
  }
51
58
 
52
59
  get stableId(): `eventTrigger:${string}` {
@@ -68,6 +75,7 @@ export class EventTrigger extends BasePgModel {
68
75
  tags: this.tags,
69
76
  owner: this.owner,
70
77
  comment: this.comment,
78
+ security_labels: this.security_labels,
71
79
  };
72
80
  }
73
81
  }
@@ -90,7 +98,20 @@ select
90
98
  et.evtenabled as enabled,
91
99
  et.evttags as tags,
92
100
  et.evtowner::regrole::text as owner,
93
- obj_description(et.oid, 'pg_event_trigger') as comment
101
+ obj_description(et.oid, 'pg_event_trigger') as comment,
102
+ coalesce(
103
+ (
104
+ select json_agg(
105
+ json_build_object('provider', sl.provider, 'label', sl.label)
106
+ order by sl.provider
107
+ )
108
+ from pg_catalog.pg_seclabel sl
109
+ where sl.objoid = et.oid
110
+ and sl.classoid = 'pg_event_trigger'::regclass
111
+ and sl.objsubid = 0
112
+ ),
113
+ '[]'::json
114
+ ) as security_labels
94
115
  from pg_catalog.pg_event_trigger et
95
116
  join pg_catalog.pg_proc p on p.oid = et.evtfoid
96
117
  left join extension_oids e on e.objid = et.oid
@@ -0,0 +1,143 @@
1
+ import { afterEach, describe, expect, test } from "bun:test";
2
+ import {
3
+ extractWithDefinitionRetry,
4
+ resolveExtractRetries,
5
+ } from "./extract-with-retry.ts";
6
+
7
+ type Row = { id: string; definition: string | null };
8
+
9
+ const hasNullDefinition = (r: Row) => r.definition === null;
10
+
11
+ describe("resolveExtractRetries", () => {
12
+ const originalEnv = process.env.PGDELTA_EXTRACT_RETRIES;
13
+ afterEach(() => {
14
+ if (originalEnv === undefined) {
15
+ process.env.PGDELTA_EXTRACT_RETRIES = undefined;
16
+ delete process.env.PGDELTA_EXTRACT_RETRIES;
17
+ } else {
18
+ process.env.PGDELTA_EXTRACT_RETRIES = originalEnv;
19
+ }
20
+ });
21
+
22
+ test("defaults to 1 when option and env are unset", () => {
23
+ delete process.env.PGDELTA_EXTRACT_RETRIES;
24
+ expect(resolveExtractRetries()).toBe(1);
25
+ });
26
+
27
+ test("uses option when provided", () => {
28
+ process.env.PGDELTA_EXTRACT_RETRIES = "5";
29
+ expect(resolveExtractRetries(0)).toBe(0);
30
+ expect(resolveExtractRetries(1)).toBe(1);
31
+ expect(resolveExtractRetries(7)).toBe(7);
32
+ });
33
+
34
+ test("falls back to env when option is undefined", () => {
35
+ process.env.PGDELTA_EXTRACT_RETRIES = "4";
36
+ expect(resolveExtractRetries()).toBe(4);
37
+ });
38
+
39
+ test("clamps negative values to 0", () => {
40
+ delete process.env.PGDELTA_EXTRACT_RETRIES;
41
+ expect(resolveExtractRetries(-3)).toBe(0);
42
+ process.env.PGDELTA_EXTRACT_RETRIES = "-9";
43
+ expect(resolveExtractRetries()).toBe(0);
44
+ });
45
+
46
+ test("ignores non-numeric env values", () => {
47
+ process.env.PGDELTA_EXTRACT_RETRIES = "not-a-number";
48
+ expect(resolveExtractRetries()).toBe(1);
49
+ });
50
+
51
+ test("ignores empty env string", () => {
52
+ process.env.PGDELTA_EXTRACT_RETRIES = "";
53
+ expect(resolveExtractRetries()).toBe(1);
54
+ });
55
+ });
56
+
57
+ describe("extractWithDefinitionRetry", () => {
58
+ test("returns first attempt when no row has null definition", async () => {
59
+ let attempts = 0;
60
+ const rows = await extractWithDefinitionRetry<Row>({
61
+ label: "test",
62
+ query: async () => {
63
+ attempts++;
64
+ return [{ id: "a", definition: "OK" }];
65
+ },
66
+ hasNullDefinition,
67
+ options: { retries: 2, backoffMs: 0 },
68
+ });
69
+ expect(attempts).toBe(1);
70
+ expect(rows).toEqual([{ id: "a", definition: "OK" }]);
71
+ });
72
+
73
+ test("retries when definition is null and succeeds on attempt 2", async () => {
74
+ let attempts = 0;
75
+ const rows = await extractWithDefinitionRetry<Row>({
76
+ label: "test",
77
+ query: async () => {
78
+ attempts++;
79
+ if (attempts === 1) {
80
+ return [
81
+ { id: "a", definition: "OK" },
82
+ { id: "b", definition: null },
83
+ ];
84
+ }
85
+ return [{ id: "a", definition: "OK" }];
86
+ },
87
+ hasNullDefinition,
88
+ options: { retries: 2, backoffMs: 0 },
89
+ });
90
+ expect(attempts).toBe(2);
91
+ expect(rows).toEqual([{ id: "a", definition: "OK" }]);
92
+ });
93
+
94
+ test("returns last-attempt rows (with offenders) once retries are exhausted", async () => {
95
+ let attempts = 0;
96
+ const rows = await extractWithDefinitionRetry<Row>({
97
+ label: "test",
98
+ query: async () => {
99
+ attempts++;
100
+ return [
101
+ { id: "a", definition: "OK" },
102
+ { id: "b", definition: null },
103
+ ];
104
+ },
105
+ hasNullDefinition,
106
+ options: { retries: 2, backoffMs: 0 },
107
+ });
108
+ expect(attempts).toBe(3);
109
+ expect(rows).toEqual([
110
+ { id: "a", definition: "OK" },
111
+ { id: "b", definition: null },
112
+ ]);
113
+ });
114
+
115
+ test("retries: 0 disables retrying entirely", async () => {
116
+ let attempts = 0;
117
+ const rows = await extractWithDefinitionRetry<Row>({
118
+ label: "test",
119
+ query: async () => {
120
+ attempts++;
121
+ return [{ id: "b", definition: null }];
122
+ },
123
+ hasNullDefinition,
124
+ options: { retries: 0, backoffMs: 0 },
125
+ });
126
+ expect(attempts).toBe(1);
127
+ expect(rows).toEqual([{ id: "b", definition: null }]);
128
+ });
129
+
130
+ test("retries: 5 attempts up to 6 times before giving up", async () => {
131
+ let attempts = 0;
132
+ await extractWithDefinitionRetry<Row>({
133
+ label: "test",
134
+ query: async () => {
135
+ attempts++;
136
+ return [{ id: "b", definition: null }];
137
+ },
138
+ hasNullDefinition,
139
+ options: { retries: 5, backoffMs: 0 },
140
+ });
141
+ expect(attempts).toBe(6);
142
+ });
143
+ });
@@ -0,0 +1,87 @@
1
+ import debug from "debug";
2
+
3
+ const log = debug("pg-delta:extract");
4
+
5
+ const DEFAULT_RETRIES = 1;
6
+ const DEFAULT_BACKOFF_MS = 50;
7
+
8
+ export interface ExtractRetryOptions {
9
+ /**
10
+ * Number of retry attempts to make when a `pg_get_*def()` call returns NULL
11
+ * for at least one row. Total attempts is `retries + 1`. Negative values are
12
+ * clamped to 0. When this option is undefined the value is read from the
13
+ * `PGDELTA_EXTRACT_RETRIES` environment variable, falling back to a default
14
+ * of 1 (i.e. the first attempt plus one retry, 2 attempts total).
15
+ */
16
+ retries?: number;
17
+ /**
18
+ * Delay between retry attempts in milliseconds; the actual wait is
19
+ * `backoffMs * attemptNumber` (linear). Defaults to 50. Set to 0 in tests.
20
+ */
21
+ backoffMs?: number;
22
+ }
23
+
24
+ export function resolveExtractRetries(option?: number): number {
25
+ if (typeof option === "number" && Number.isFinite(option)) {
26
+ return Math.max(0, Math.floor(option));
27
+ }
28
+ const envVal = process.env.PGDELTA_EXTRACT_RETRIES;
29
+ if (envVal !== undefined && envVal !== "") {
30
+ const n = Number(envVal);
31
+ if (Number.isFinite(n)) return Math.max(0, Math.floor(n));
32
+ }
33
+ return DEFAULT_RETRIES;
34
+ }
35
+
36
+ const sleep = (ms: number) =>
37
+ ms > 0 ? new Promise<void>((r) => setTimeout(r, ms)) : Promise.resolve();
38
+
39
+ /**
40
+ * Runs `query()` up to `retries + 1` times, retrying as long as at least one
41
+ * row in the result satisfies `hasNullDefinition`. The retry exists because
42
+ * `pg_get_<x>def()` can return NULL transiently when the underlying catalog
43
+ * row is dropped concurrently or the catalog state is in flux; in practice a
44
+ * second attempt either no longer sees the dropped row or succeeds in
45
+ * resolving the definition.
46
+ *
47
+ * Returns the rows from the first attempt with no offenders, or — once
48
+ * retries are exhausted — the rows from the final attempt (still containing
49
+ * offenders). The caller is responsible for the final filter so this helper
50
+ * works for both flat schemas (definition on the row) and nested schemas
51
+ * (definition on a child collection, e.g. table constraints).
52
+ */
53
+ export async function extractWithDefinitionRetry<TRow>(params: {
54
+ label: string;
55
+ query: () => Promise<TRow[]>;
56
+ hasNullDefinition: (row: TRow) => boolean;
57
+ options?: ExtractRetryOptions;
58
+ }): Promise<TRow[]> {
59
+ const retries = resolveExtractRetries(params.options?.retries);
60
+ const backoffMs = params.options?.backoffMs ?? DEFAULT_BACKOFF_MS;
61
+ const maxAttempts = retries + 1;
62
+
63
+ let rows: TRow[] = [];
64
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
65
+ rows = await params.query();
66
+ const offenders = rows.filter(params.hasNullDefinition).length;
67
+ if (offenders === 0) return rows;
68
+ if (attempt < maxAttempts) {
69
+ log(
70
+ "%s: pg_get_*def() returned NULL for %d row(s) on attempt %d/%d; retrying",
71
+ params.label,
72
+ offenders,
73
+ attempt,
74
+ maxAttempts,
75
+ );
76
+ await sleep(backoffMs * attempt);
77
+ } else {
78
+ log(
79
+ "%s: pg_get_*def() returned NULL for %d row(s) after %d attempt(s); skipping",
80
+ params.label,
81
+ offenders,
82
+ maxAttempts,
83
+ );
84
+ }
85
+ }
86
+ return rows;
87
+ }
@@ -3,7 +3,11 @@ import type { ForeignTable } from "../foreign-table.model.ts";
3
3
 
4
4
  abstract class BaseForeignTableChange extends BaseChange {
5
5
  abstract readonly foreignTable: ForeignTable;
6
- abstract readonly scope: "object" | "comment" | "privilege";
6
+ abstract readonly scope:
7
+ | "object"
8
+ | "comment"
9
+ | "privilege"
10
+ | "security_label";
7
11
  readonly objectType: "foreign_table" = "foreign_table";
8
12
  }
9
13
 
@@ -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 { ForeignTable } from "../foreign-table.model.ts";
5
+ import {
6
+ CreateForeignTableChange,
7
+ DropForeignTableChange,
8
+ } from "./foreign-table.base.ts";
9
+
10
+ export type SecurityLabelForeignTable =
11
+ | CreateSecurityLabelOnForeignTable
12
+ | DropSecurityLabelOnForeignTable;
13
+
14
+ export class CreateSecurityLabelOnForeignTable extends CreateForeignTableChange {
15
+ public readonly foreignTable: ForeignTable;
16
+ public readonly securityLabel: SecurityLabelProps;
17
+ public readonly scope = "security_label" as const;
18
+
19
+ constructor(props: {
20
+ foreignTable: ForeignTable;
21
+ securityLabel: SecurityLabelProps;
22
+ }) {
23
+ super();
24
+ this.foreignTable = props.foreignTable;
25
+ this.securityLabel = props.securityLabel;
26
+ }
27
+
28
+ get creates() {
29
+ return [
30
+ stableId.securityLabel(
31
+ this.foreignTable.stableId,
32
+ this.securityLabel.provider,
33
+ ),
34
+ ];
35
+ }
36
+
37
+ get requires() {
38
+ return [this.foreignTable.stableId];
39
+ }
40
+
41
+ serialize(): string {
42
+ return [
43
+ "SECURITY LABEL FOR",
44
+ this.securityLabel.provider,
45
+ "ON FOREIGN TABLE",
46
+ `${this.foreignTable.schema}.${this.foreignTable.name}`,
47
+ "IS",
48
+ quoteLiteral(this.securityLabel.label),
49
+ ].join(" ");
50
+ }
51
+ }
52
+
53
+ export class DropSecurityLabelOnForeignTable extends DropForeignTableChange {
54
+ public readonly foreignTable: ForeignTable;
55
+ public readonly securityLabel: SecurityLabelProps;
56
+ public readonly scope = "security_label" as const;
57
+
58
+ constructor(props: {
59
+ foreignTable: ForeignTable;
60
+ securityLabel: SecurityLabelProps;
61
+ }) {
62
+ super();
63
+ this.foreignTable = props.foreignTable;
64
+ this.securityLabel = props.securityLabel;
65
+ }
66
+
67
+ get drops() {
68
+ return [
69
+ stableId.securityLabel(
70
+ this.foreignTable.stableId,
71
+ this.securityLabel.provider,
72
+ ),
73
+ ];
74
+ }
75
+
76
+ get requires() {
77
+ return [
78
+ stableId.securityLabel(
79
+ this.foreignTable.stableId,
80
+ this.securityLabel.provider,
81
+ ),
82
+ this.foreignTable.stableId,
83
+ ];
84
+ }
85
+
86
+ serialize(): string {
87
+ return [
88
+ "SECURITY LABEL FOR",
89
+ this.securityLabel.provider,
90
+ "ON FOREIGN TABLE",
91
+ `${this.foreignTable.schema}.${this.foreignTable.name}`,
92
+ "IS NULL",
93
+ ].join(" ");
94
+ }
95
+ }
@@ -3,6 +3,7 @@ import type { CommentForeignTable } from "./foreign-table.comment.ts";
3
3
  import type { CreateForeignTable } from "./foreign-table.create.ts";
4
4
  import type { DropForeignTable } from "./foreign-table.drop.ts";
5
5
  import type { ForeignTablePrivilege } from "./foreign-table.privilege.ts";
6
+ import type { SecurityLabelForeignTable } from "./foreign-table.security-label.ts";
6
7
 
7
8
  /** Union of all foreign-table-related change variants (`objectType: "foreign_table"`). @category Change Types */
8
9
  export type ForeignTableChange =
@@ -10,4 +11,5 @@ export type ForeignTableChange =
10
11
  | CommentForeignTable
11
12
  | CreateForeignTable
12
13
  | DropForeignTable
13
- | ForeignTablePrivilege;
14
+ | ForeignTablePrivilege
15
+ | SecurityLabelForeignTable;
@@ -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
@@ -104,7 +104,6 @@ export function diffIndexes(
104
104
  "nulls_not_distinct",
105
105
  "immediate",
106
106
  "is_clustered",
107
- "is_replica_identity",
108
107
  "column_collations",
109
108
  "operator_classes",
110
109
  "column_options",
@@ -36,8 +36,19 @@ const baseRow = {
36
36
  const mockPool = (rows: unknown[]): Pool =>
37
37
  ({ query: async () => ({ rows }) }) as unknown as Pool;
38
38
 
39
+ const mockPoolSequence = (...attempts: unknown[][]): Pool => {
40
+ let i = 0;
41
+ return {
42
+ query: async () => ({
43
+ rows: attempts[Math.min(i++, attempts.length - 1)],
44
+ }),
45
+ } as unknown as Pool;
46
+ };
47
+
48
+ const NO_BACKOFF = { backoffMs: 0 } as const;
49
+
39
50
  describe("extractIndexes", () => {
40
- test("skips rows where pg_get_indexdef returned NULL", async () => {
51
+ test("skips rows where pg_get_indexdef returned NULL after exhausting retries", async () => {
41
52
  const indexes = await extractIndexes(
42
53
  mockPool([
43
54
  {
@@ -47,6 +58,7 @@ describe("extractIndexes", () => {
47
58
  },
48
59
  { ...baseRow, name: '"orphan_idx"', definition: null },
49
60
  ]),
61
+ NO_BACKOFF,
50
62
  );
51
63
 
52
64
  expect(indexes).toHaveLength(1);
@@ -59,6 +71,7 @@ describe("extractIndexes", () => {
59
71
  await expect(
60
72
  extractIndexes(
61
73
  mockPool([{ ...baseRow, name: '"orphan"', definition: null }]),
74
+ NO_BACKOFF,
62
75
  ),
63
76
  ).resolves.toEqual([]);
64
77
  });
@@ -77,7 +90,30 @@ describe("extractIndexes", () => {
77
90
  definition: "CREATE INDEX b ON users (id)",
78
91
  },
79
92
  ]),
93
+ NO_BACKOFF,
80
94
  );
81
95
  expect(indexes.map((i) => i.name)).toEqual(['"a"', '"b"']);
82
96
  });
97
+
98
+ test("recovers when pg_get_indexdef is NULL on first attempt but resolved on retry", async () => {
99
+ const indexes = await extractIndexes(
100
+ mockPoolSequence(
101
+ // attempt 1: definition is NULL (transient race)
102
+ [{ ...baseRow, name: '"racy_idx"', definition: null }],
103
+ // attempt 2: catalog scan no longer sees the dropped row, or
104
+ // pg_get_indexdef successfully resolves the definition
105
+ [
106
+ {
107
+ ...baseRow,
108
+ name: '"racy_idx"',
109
+ definition: "CREATE INDEX racy_idx ON users (id)",
110
+ },
111
+ ],
112
+ ),
113
+ { retries: 2, backoffMs: 0 },
114
+ );
115
+ expect(indexes).toHaveLength(1);
116
+ expect(indexes[0]?.name).toBe('"racy_idx"');
117
+ expect(indexes[0]?.definition).toBe("CREATE INDEX racy_idx ON users (id)");
118
+ });
83
119
  });
@@ -2,6 +2,10 @@ import { sql } from "@ts-safeql/sql-tag";
2
2
  import type { Pool } from "pg";
3
3
  import z from "zod";
4
4
  import { BasePgModel } from "../base.model.ts";
5
+ import {
6
+ type ExtractRetryOptions,
7
+ extractWithDefinitionRetry,
8
+ } from "../extract-with-retry.ts";
5
9
 
6
10
  const TableRelkindSchema = z.enum([
7
11
  "r", // table (regular relation)
@@ -163,7 +167,11 @@ export class Index extends BasePgModel {
163
167
  nulls_not_distinct: this.nulls_not_distinct,
164
168
  immediate: this.immediate,
165
169
  is_clustered: this.is_clustered,
166
- is_replica_identity: this.is_replica_identity,
170
+ // is_replica_identity excluded: the table's `replica_identity` /
171
+ // `replica_identity_index` is the source of truth, set via
172
+ // ALTER TABLE ... REPLICA IDENTITY USING INDEX. Including this flag here
173
+ // would trigger spurious DROP+CREATE of the index whenever the table's
174
+ // replica identity changes.
167
175
  // key_columns excluded: contains attribute numbers that can differ between databases
168
176
  // even when indexes are logically identical. The definition field already captures
169
177
  // the logical structure using column names, so we compare by definition instead.
@@ -215,8 +223,16 @@ export class Index extends BasePgModel {
215
223
  }
216
224
  }
217
225
 
218
- export async function extractIndexes(pool: Pool): Promise<Index[]> {
219
- const { rows: indexRows } = await pool.query<IndexProps>(sql`
226
+ export async function extractIndexes(
227
+ pool: Pool,
228
+ options?: ExtractRetryOptions,
229
+ ): Promise<Index[]> {
230
+ const indexRows = await extractWithDefinitionRetry({
231
+ label: "indexes",
232
+ options,
233
+ hasNullDefinition: (row) => row.definition === null,
234
+ query: async () => {
235
+ const result = await pool.query<IndexProps>(sql`
220
236
  with extension_oids as (
221
237
  select objid
222
238
  from pg_depend d
@@ -372,8 +388,11 @@ export async function extractIndexes(pool: Pool): Promise<Index[]> {
372
388
 
373
389
  order by 1, 2
374
390
  `);
375
- const validatedRows = indexRows
376
- .map((row: unknown) => indexRowSchema.parse(row))
377
- .filter((row): row is IndexProps => row.definition !== null);
391
+ return result.rows.map((row: unknown) => indexRowSchema.parse(row));
392
+ },
393
+ });
394
+ const validatedRows = indexRows.filter(
395
+ (row): row is IndexProps => row.definition !== null,
396
+ );
378
397
  return validatedRows.map((row: IndexProps) => new Index(row));
379
398
  }