@intentius/chant-lexicon-sql 0.124.46 → 0.124.48

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 (360) hide show
  1. package/dist/change-set.d.ts +1 -1
  2. package/dist/change-set.d.ts.map +1 -1
  3. package/dist/change-set.js +2 -1
  4. package/dist/clickhouse/apply/statements.d.ts +9 -0
  5. package/dist/clickhouse/apply/statements.d.ts.map +1 -1
  6. package/dist/clickhouse/apply/statements.js +24 -0
  7. package/dist/clickhouse/import/live-export.d.ts +7 -3
  8. package/dist/clickhouse/import/live-export.d.ts.map +1 -1
  9. package/dist/clickhouse/import/live-export.js +21 -5
  10. package/dist/clickhouse/live/named-collections.d.ts +27 -0
  11. package/dist/clickhouse/live/named-collections.d.ts.map +1 -0
  12. package/dist/clickhouse/live/named-collections.js +53 -0
  13. package/dist/clickhouse/live/undeclarable.d.ts +15 -3
  14. package/dist/clickhouse/live/undeclarable.d.ts.map +1 -1
  15. package/dist/clickhouse/live/undeclarable.js +18 -4
  16. package/dist/clickhouse/named-collections.d.ts +83 -0
  17. package/dist/clickhouse/named-collections.d.ts.map +1 -0
  18. package/dist/clickhouse/named-collections.js +203 -0
  19. package/dist/clickhouse/parser.d.ts.map +1 -1
  20. package/dist/clickhouse/parser.js +1 -1
  21. package/dist/clickhouse/plan/commands.d.ts +2 -1
  22. package/dist/clickhouse/plan/commands.d.ts.map +1 -1
  23. package/dist/clickhouse/plan/commands.js +17 -2
  24. package/dist/clickhouse/plan/diff.d.ts +15 -0
  25. package/dist/clickhouse/plan/diff.d.ts.map +1 -1
  26. package/dist/clickhouse/plan/diff.js +43 -1
  27. package/dist/clickhouse/plan/disruption.d.ts.map +1 -1
  28. package/dist/clickhouse/plan/disruption.js +5 -0
  29. package/dist/clickhouse/plan/rebuild-handoff.d.ts.map +1 -1
  30. package/dist/clickhouse/plan/rebuild-handoff.js +4 -0
  31. package/dist/clickhouse/plan/report.d.ts.map +1 -1
  32. package/dist/clickhouse/plan/report.js +6 -1
  33. package/dist/clickhouse/plan/rules.d.ts +5 -1
  34. package/dist/clickhouse/plan/rules.d.ts.map +1 -1
  35. package/dist/clickhouse/plan/rules.js +7 -3
  36. package/dist/clickhouse/plan/stream.d.ts +40 -0
  37. package/dist/clickhouse/plan/stream.d.ts.map +1 -0
  38. package/dist/clickhouse/plan/stream.js +107 -0
  39. package/dist/clickhouse/rebuild/observe.d.ts +3 -1
  40. package/dist/clickhouse/rebuild/observe.d.ts.map +1 -1
  41. package/dist/clickhouse/rebuild/observe.js +14 -1
  42. package/dist/clickhouse/review-catalog.d.ts +213 -0
  43. package/dist/clickhouse/review-catalog.d.ts.map +1 -0
  44. package/dist/clickhouse/review-catalog.js +330 -0
  45. package/dist/codegen/classifier-page.d.ts.map +1 -1
  46. package/dist/codegen/classifier-page.js +21 -4
  47. package/dist/codegen/clickhouse-module.d.ts.map +1 -1
  48. package/dist/codegen/clickhouse-module.js +3 -0
  49. package/dist/codegen/coverage-page.d.ts.map +1 -1
  50. package/dist/codegen/coverage-page.js +5 -3
  51. package/dist/codegen/docs.js +1 -1
  52. package/dist/codegen/postgres-classifier-page.js +1 -1
  53. package/dist/codegen/registry.d.ts.map +1 -1
  54. package/dist/codegen/registry.js +5 -0
  55. package/dist/composites/catalog.d.ts.map +1 -1
  56. package/dist/composites/catalog.js +100 -0
  57. package/dist/composites/clickhouse.d.ts +2 -0
  58. package/dist/composites/clickhouse.d.ts.map +1 -1
  59. package/dist/composites/clickhouse.js +2 -0
  60. package/dist/composites/otel-tables.d.ts +100 -0
  61. package/dist/composites/otel-tables.d.ts.map +1 -0
  62. package/dist/composites/otel-tables.js +282 -0
  63. package/dist/composites/stream-ingest.d.ts +63 -0
  64. package/dist/composites/stream-ingest.d.ts.map +1 -0
  65. package/dist/composites/stream-ingest.js +39 -0
  66. package/dist/coverage/clickhouse.json +1 -1
  67. package/dist/coverage/postgres.json +7 -7
  68. package/dist/coverage.d.ts +16 -6
  69. package/dist/coverage.d.ts.map +1 -1
  70. package/dist/coverage.js +10 -5
  71. package/dist/drizzle-command.d.ts +17 -0
  72. package/dist/drizzle-command.d.ts.map +1 -0
  73. package/dist/drizzle-command.js +91 -0
  74. package/dist/drizzle.d.ts +70 -0
  75. package/dist/drizzle.d.ts.map +1 -0
  76. package/dist/drizzle.js +684 -0
  77. package/dist/files/clickhouse.d.ts.map +1 -1
  78. package/dist/files/clickhouse.js +8 -1
  79. package/dist/files/common.d.ts +13 -0
  80. package/dist/files/common.d.ts.map +1 -1
  81. package/dist/files/common.js +5 -0
  82. package/dist/files/postgres.js +12 -2
  83. package/dist/generated/clickhouse.d.ts +2 -0
  84. package/dist/generated/clickhouse.d.ts.map +1 -1
  85. package/dist/generated/clickhouse.js +2 -0
  86. package/dist/import-parser.d.ts +21 -1
  87. package/dist/import-parser.d.ts.map +1 -1
  88. package/dist/import-parser.js +79 -5
  89. package/dist/index.d.ts +1 -1
  90. package/dist/index.d.ts.map +1 -1
  91. package/dist/index.js +1 -1
  92. package/dist/integrity.json +6 -5
  93. package/dist/lint/post-synth/audit-entries.d.ts.map +1 -1
  94. package/dist/lint/post-synth/audit-entries.js +1 -0
  95. package/dist/lint/post-synth/index.d.ts.map +1 -1
  96. package/dist/lint/post-synth/index.js +2 -0
  97. package/dist/lint/post-synth/sqlch128.d.ts +18 -0
  98. package/dist/lint/post-synth/sqlch128.d.ts.map +1 -0
  99. package/dist/lint/post-synth/sqlch128.js +222 -0
  100. package/dist/lint/rules/postgres-templates.js +2 -2
  101. package/dist/lsp/postgres-template.d.ts.map +1 -1
  102. package/dist/lsp/postgres-template.js +11 -0
  103. package/dist/manifest.json +1 -1
  104. package/dist/mcp/postgres.js +1 -1
  105. package/dist/meta.json +25 -0
  106. package/dist/migrate/from-postgres/detect.d.ts +4 -0
  107. package/dist/migrate/from-postgres/detect.d.ts.map +1 -0
  108. package/dist/migrate/from-postgres/detect.js +7 -0
  109. package/dist/migrate/from-postgres/emit.d.ts +26 -0
  110. package/dist/migrate/from-postgres/emit.d.ts.map +1 -0
  111. package/dist/migrate/from-postgres/emit.js +86 -0
  112. package/dist/migrate/from-postgres/index.d.ts +51 -0
  113. package/dist/migrate/from-postgres/index.d.ts.map +1 -0
  114. package/dist/migrate/from-postgres/index.js +115 -0
  115. package/dist/migrate/from-postgres/translate.d.ts +73 -0
  116. package/dist/migrate/from-postgres/translate.d.ts.map +1 -0
  117. package/dist/migrate/from-postgres/translate.js +261 -0
  118. package/dist/migrate/from-postgres/types.d.ts +51 -0
  119. package/dist/migrate/from-postgres/types.d.ts.map +1 -0
  120. package/dist/migrate/from-postgres/types.js +211 -0
  121. package/dist/migration-replay.d.ts +73 -0
  122. package/dist/migration-replay.d.ts.map +1 -0
  123. package/dist/migration-replay.js +294 -0
  124. package/dist/okf/index.md +6 -0
  125. package/dist/okf/rules/SQLCH128.md +11 -0
  126. package/dist/okf/types/PostgresForeignDataWrapper.md +9 -0
  127. package/dist/okf/types/PostgresForeignServer.md +9 -0
  128. package/dist/okf/types/PostgresOperator.md +9 -0
  129. package/dist/okf/types/PostgresPublication.md +9 -0
  130. package/dist/okf/types/PostgresStatistics.md +9 -0
  131. package/dist/op/activities/postgres-migration.d.ts.map +1 -1
  132. package/dist/op/activities/postgres-migration.js +4 -0
  133. package/dist/op/activity-contracts.d.ts +20 -0
  134. package/dist/op/activity-contracts.d.ts.map +1 -1
  135. package/dist/op/activity-contracts.js +2 -0
  136. package/dist/plugin.d.ts.map +1 -1
  137. package/dist/plugin.js +36 -2
  138. package/dist/postgres/apply/apply.js +8 -1
  139. package/dist/postgres/apply/statements.d.ts.map +1 -1
  140. package/dist/postgres/apply/statements.js +87 -2
  141. package/dist/postgres/constraint-names.d.ts +30 -0
  142. package/dist/postgres/constraint-names.d.ts.map +1 -0
  143. package/dist/postgres/constraint-names.js +72 -0
  144. package/dist/postgres/entities.d.ts +110 -1
  145. package/dist/postgres/entities.d.ts.map +1 -1
  146. package/dist/postgres/entities.js +146 -0
  147. package/dist/postgres/entity-types.d.ts +5 -0
  148. package/dist/postgres/entity-types.d.ts.map +1 -1
  149. package/dist/postgres/entity-types.js +5 -0
  150. package/dist/postgres/import/generator.d.ts.map +1 -1
  151. package/dist/postgres/import/generator.js +50 -2
  152. package/dist/postgres/import/live-export.d.ts.map +1 -1
  153. package/dist/postgres/import/live-export.js +2 -0
  154. package/dist/postgres/live/catalog-kinds.d.ts +14 -0
  155. package/dist/postgres/live/catalog-kinds.d.ts.map +1 -1
  156. package/dist/postgres/live/catalog-kinds.js +206 -0
  157. package/dist/postgres/live/catalog.d.ts +2 -0
  158. package/dist/postgres/live/catalog.d.ts.map +1 -1
  159. package/dist/postgres/live/catalog.js +3 -1
  160. package/dist/postgres/live/describe-resources.d.ts.map +1 -1
  161. package/dist/postgres/live/describe-resources.js +10 -2
  162. package/dist/postgres/migrate/backfill.d.ts +2 -0
  163. package/dist/postgres/migrate/backfill.d.ts.map +1 -1
  164. package/dist/postgres/migrate/backfill.js +7 -1
  165. package/dist/postgres/migrate/carry.d.ts +45 -1
  166. package/dist/postgres/migrate/carry.d.ts.map +1 -1
  167. package/dist/postgres/migrate/carry.js +72 -8
  168. package/dist/postgres/migrate/observe.d.ts +28 -4
  169. package/dist/postgres/migrate/observe.d.ts.map +1 -1
  170. package/dist/postgres/migrate/observe.js +41 -12
  171. package/dist/postgres/migrate/op.d.ts +26 -1
  172. package/dist/postgres/migrate/op.d.ts.map +1 -1
  173. package/dist/postgres/migrate/op.js +3 -1
  174. package/dist/postgres/migrate/steps.d.ts +35 -2
  175. package/dist/postgres/migrate/steps.d.ts.map +1 -1
  176. package/dist/postgres/migrate/steps.js +158 -12
  177. package/dist/postgres/parser.d.ts +72 -1
  178. package/dist/postgres/parser.d.ts.map +1 -1
  179. package/dist/postgres/parser.js +194 -2
  180. package/dist/postgres/plan/deep.d.ts.map +1 -1
  181. package/dist/postgres/plan/deep.js +29 -1
  182. package/dist/postgres/plan/diff.d.ts.map +1 -1
  183. package/dist/postgres/plan/diff.js +39 -1
  184. package/dist/postgres/plan/normalize.d.ts +17 -2
  185. package/dist/postgres/plan/normalize.d.ts.map +1 -1
  186. package/dist/postgres/plan/normalize.js +109 -1
  187. package/dist/postgres/plan/rules.d.ts +1 -0
  188. package/dist/postgres/plan/rules.d.ts.map +1 -1
  189. package/dist/postgres/plan/rules.js +3 -2
  190. package/dist/postgres/plan/schema.js +1 -1
  191. package/dist/postgres/secret-options.d.ts +10 -0
  192. package/dist/postgres/secret-options.d.ts.map +1 -0
  193. package/dist/postgres/secret-options.js +9 -0
  194. package/dist/postgres/testing/roundtrip-schema.d.ts +3 -2
  195. package/dist/postgres/testing/roundtrip-schema.d.ts.map +1 -1
  196. package/dist/postgres/testing/roundtrip-schema.js +13 -1
  197. package/dist/postgres.d.ts +1 -1
  198. package/dist/postgres.d.ts.map +1 -1
  199. package/dist/postgres.js +1 -1
  200. package/dist/rules/audit-entries.ts +1 -0
  201. package/dist/rules/postgres-templates.ts +2 -2
  202. package/dist/rules/sqlch128.ts +230 -0
  203. package/dist/serializer.d.ts +5 -0
  204. package/dist/serializer.d.ts.map +1 -1
  205. package/dist/serializer.js +13 -2
  206. package/dist/sql-types-command.d.ts +13 -0
  207. package/dist/sql-types-command.d.ts.map +1 -1
  208. package/dist/sql-types-command.js +31 -16
  209. package/dist/sql-types.d.ts +22 -0
  210. package/dist/sql-types.d.ts.map +1 -1
  211. package/dist/sql-types.js +10 -6
  212. package/dist/sqlserver/lexer.d.ts +37 -0
  213. package/dist/sqlserver/lexer.d.ts.map +1 -0
  214. package/dist/sqlserver/lexer.js +254 -0
  215. package/dist/sqlserver/parse.d.ts +185 -0
  216. package/dist/sqlserver/parse.d.ts.map +1 -0
  217. package/dist/sqlserver/parse.js +767 -0
  218. package/dist/sqlserver/translate.d.ts +34 -0
  219. package/dist/sqlserver/translate.d.ts.map +1 -0
  220. package/dist/sqlserver/translate.js +495 -0
  221. package/dist/sqlserver/types.d.ts +44 -0
  222. package/dist/sqlserver/types.d.ts.map +1 -0
  223. package/dist/sqlserver/types.js +133 -0
  224. package/dist/sqlserver/validate.d.ts +31 -0
  225. package/dist/sqlserver/validate.d.ts.map +1 -0
  226. package/dist/sqlserver/validate.js +67 -0
  227. package/dist/validate.d.ts.map +1 -1
  228. package/dist/validate.js +5 -0
  229. package/package.json +2 -2
  230. package/src/change-set.ts +2 -1
  231. package/src/clickhouse/apply/statements.ts +25 -0
  232. package/src/clickhouse/import/live-export.ts +20 -5
  233. package/src/clickhouse/live/named-collections.e2e.test.ts +88 -0
  234. package/src/clickhouse/live/named-collections.ts +61 -0
  235. package/src/clickhouse/live/undeclarable.e2e.test.ts +5 -3
  236. package/src/clickhouse/live/undeclarable.ts +20 -4
  237. package/src/clickhouse/named-collections.test.ts +187 -0
  238. package/src/clickhouse/named-collections.ts +224 -0
  239. package/src/clickhouse/parser.ts +2 -1
  240. package/src/clickhouse/plan/commands.ts +17 -2
  241. package/src/clickhouse/plan/diff.ts +57 -1
  242. package/src/clickhouse/plan/disruption.ts +5 -0
  243. package/src/clickhouse/plan/rebuild-handoff.ts +3 -0
  244. package/src/clickhouse/plan/report.ts +6 -1
  245. package/src/clickhouse/plan/rules.ts +14 -4
  246. package/src/clickhouse/plan/stream-s3queue.e2e.test.ts +148 -0
  247. package/src/clickhouse/plan/stream.test.ts +237 -0
  248. package/src/clickhouse/plan/stream.ts +111 -0
  249. package/src/clickhouse/rebuild/observe.ts +15 -1
  250. package/src/clickhouse/review-catalog.test.ts +181 -0
  251. package/src/clickhouse/review-catalog.ts +489 -0
  252. package/src/codegen/classifier-page.ts +21 -4
  253. package/src/codegen/clickhouse-module.ts +3 -0
  254. package/src/codegen/coverage-page.ts +5 -3
  255. package/src/codegen/docs.ts +1 -1
  256. package/src/codegen/postgres-classifier-page.ts +1 -1
  257. package/src/codegen/registry.ts +5 -0
  258. package/src/composites/catalog.test.ts +6 -1
  259. package/src/composites/catalog.ts +100 -0
  260. package/src/composites/clickhouse.ts +2 -0
  261. package/src/composites/composites.test.ts +1 -1
  262. package/src/composites/otel-tables.test.ts +118 -0
  263. package/src/composites/otel-tables.ts +339 -0
  264. package/src/composites/stream-ingest.test.ts +114 -0
  265. package/src/composites/stream-ingest.ts +82 -0
  266. package/src/core/undeclarable.test.ts +6 -5
  267. package/src/coverage/clickhouse.json +1 -1
  268. package/src/coverage/postgres.json +7 -7
  269. package/src/coverage.test.ts +27 -2
  270. package/src/coverage.ts +18 -7
  271. package/src/drizzle-command.ts +94 -0
  272. package/src/drizzle-kit.e2e.test.ts +89 -0
  273. package/src/drizzle-types.e2e.test.ts +67 -0
  274. package/src/drizzle.test.ts +358 -0
  275. package/src/drizzle.ts +737 -0
  276. package/src/files/clickhouse.ts +8 -1
  277. package/src/files/common.ts +14 -1
  278. package/src/files/postgres.ts +12 -2
  279. package/src/generated/clickhouse.ts +3 -0
  280. package/src/generated/lexicon-sql.json +25 -0
  281. package/src/import-parser.sqlserver.test.ts +92 -0
  282. package/src/import-parser.ts +79 -5
  283. package/src/index.ts +1 -1
  284. package/src/lint/post-synth/audit-entries.ts +1 -0
  285. package/src/lint/post-synth/index.ts +2 -0
  286. package/src/lint/post-synth/sqlch128.test.ts +107 -0
  287. package/src/lint/post-synth/sqlch128.ts +230 -0
  288. package/src/lint/rules/postgres-templates.ts +2 -2
  289. package/src/lsp/postgres-template.ts +11 -0
  290. package/src/mcp/postgres.ts +1 -1
  291. package/src/migrate/from-postgres/cli.test.ts +165 -0
  292. package/src/migrate/from-postgres/detect.ts +7 -0
  293. package/src/migrate/from-postgres/emit.ts +96 -0
  294. package/src/migrate/from-postgres/example.test.ts +205 -0
  295. package/src/migrate/from-postgres/index.ts +122 -0
  296. package/src/migrate/from-postgres/translate.test.ts +242 -0
  297. package/src/migrate/from-postgres/translate.ts +359 -0
  298. package/src/migrate/from-postgres/types.test.ts +116 -0
  299. package/src/migrate/from-postgres/types.ts +236 -0
  300. package/src/migrate/from-postgres/validate.e2e.test.ts +44 -0
  301. package/src/migrate/from-postgres/workspace.e2e.test.ts +114 -0
  302. package/src/migration-replay.e2e.test.ts +155 -0
  303. package/src/migration-replay.test.ts +173 -0
  304. package/src/migration-replay.ts +289 -0
  305. package/src/op/__snapshots__/watch-pipeline.test.ts.snap +2 -2
  306. package/src/op/activities/postgres-migration.ts +4 -0
  307. package/src/op/activity-contracts.ts +2 -0
  308. package/src/plugin.ts +37 -2
  309. package/src/postgres/apply/apply.ts +9 -1
  310. package/src/postgres/apply/statements.ts +74 -1
  311. package/src/postgres/constraint-names.ts +71 -0
  312. package/src/postgres/database-kinds.test.ts +384 -0
  313. package/src/postgres/entities.ts +266 -1
  314. package/src/postgres/entity-types.ts +5 -0
  315. package/src/postgres/import/generator.ts +42 -2
  316. package/src/postgres/import/live-export.ts +1 -0
  317. package/src/postgres/live/catalog-kinds.ts +223 -0
  318. package/src/postgres/live/catalog.ts +6 -1
  319. package/src/postgres/live/database-kinds.e2e.test.ts +164 -0
  320. package/src/postgres/live/describe-resources.ts +11 -2
  321. package/src/postgres/live/undeclarable.e2e.test.ts +13 -6
  322. package/src/postgres/migrate/backfill.ts +13 -1
  323. package/src/postgres/migrate/carry.ts +103 -8
  324. package/src/postgres/migrate/followups.e2e.test.ts +218 -0
  325. package/src/postgres/migrate/followups.test.ts +199 -0
  326. package/src/postgres/migrate/kinds.e2e.test.ts +5 -5
  327. package/src/postgres/migrate/observe.ts +53 -14
  328. package/src/postgres/migrate/op.ts +28 -1
  329. package/src/postgres/migrate/partition.e2e.test.ts +129 -0
  330. package/src/postgres/migrate/partition.test.ts +206 -0
  331. package/src/postgres/migrate/steps.ts +157 -12
  332. package/src/postgres/parser.ts +234 -2
  333. package/src/postgres/plan/deep.ts +28 -1
  334. package/src/postgres/plan/diff.ts +36 -1
  335. package/src/postgres/plan/normalize.ts +122 -1
  336. package/src/postgres/plan/rules.ts +10 -3
  337. package/src/postgres/plan/schema.ts +1 -1
  338. package/src/postgres/secret-options.ts +10 -0
  339. package/src/postgres/testing/roundtrip-schema.ts +13 -1
  340. package/src/postgres.ts +16 -0
  341. package/src/serializer.ts +13 -2
  342. package/src/skills/chant-sql-postgres-migration.md +3 -3
  343. package/src/skills/chant-sql-postgres.md +6 -1
  344. package/src/skills/chant-sql.md +2 -1
  345. package/src/sql-types-command.ts +38 -16
  346. package/src/sql-types.ts +11 -6
  347. package/src/sqlserver/fixture.test.ts +186 -0
  348. package/src/sqlserver/import-cli.test.ts +61 -0
  349. package/src/sqlserver/lexer.ts +260 -0
  350. package/src/sqlserver/parse.ts +865 -0
  351. package/src/sqlserver/translate.test.ts +396 -0
  352. package/src/sqlserver/translate.ts +558 -0
  353. package/src/sqlserver/types.test.ts +102 -0
  354. package/src/sqlserver/types.ts +154 -0
  355. package/src/sqlserver/validate.e2e.test.ts +32 -0
  356. package/src/sqlserver/validate.test.ts +49 -0
  357. package/src/sqlserver/validate.ts +76 -0
  358. package/src/validate.ts +5 -0
  359. package/src/workspace-kinds.test.ts +11 -0
  360. package/workspace-kinds.json +100 -2
@@ -21,6 +21,13 @@ import { POSTGRES_ENTITY_TYPES } from "../entity-types.js";
21
21
  import { isChantManaged, readMarker, stripMarker } from "../../core/ownership.js";
22
22
  import { identValue } from "../parser.js";
23
23
  import { canonicalPgObject } from "../plan/normalize.js";
24
+ /** The entity types that belong to the database, not a schema. */
25
+ const UNPLACED = new Set([
26
+ POSTGRES_ENTITY_TYPES.eventTrigger,
27
+ POSTGRES_ENTITY_TYPES.foreignDataWrapper,
28
+ POSTGRES_ENTITY_TYPES.foreignServer,
29
+ POSTGRES_ENTITY_TYPES.publication,
30
+ ]);
24
31
  /**
25
32
  * The schema and name a declared entity is created as; a bare name is in the
26
33
  * default schema. A routine's parameter types and a trigger's table tell it
@@ -28,7 +35,7 @@ import { canonicalPgObject } from "../plan/normalize.js";
28
35
  */
29
36
  export function declaredAddress(entity, defaultSchema) {
30
37
  const name = String(entity.props.name ?? "");
31
- if (entity.type === POSTGRES_ENTITY_TYPES.schema || entity.type === POSTGRES_ENTITY_TYPES.extension || entity.type === POSTGRES_ENTITY_TYPES.role || entity.type === POSTGRES_ENTITY_TYPES.eventTrigger)
38
+ if (entity.type === POSTGRES_ENTITY_TYPES.schema || entity.type === POSTGRES_ENTITY_TYPES.extension || entity.type === POSTGRES_ENTITY_TYPES.role || UNPLACED.has(entity.type))
32
39
  return { name };
33
40
  // A cast is named by its two types, as the catalog prints them.
34
41
  if (entity.type === POSTGRES_ENTITY_TYPES.cast)
@@ -37,6 +44,7 @@ export function declaredAddress(entity, defaultSchema) {
37
44
  if (entity.type === POSTGRES_ENTITY_TYPES.function ||
38
45
  entity.type === POSTGRES_ENTITY_TYPES.procedure ||
39
46
  entity.type === POSTGRES_ENTITY_TYPES.aggregate ||
47
+ entity.type === POSTGRES_ENTITY_TYPES.operator ||
40
48
  entity.type === POSTGRES_ENTITY_TYPES.trigger ||
41
49
  entity.type === POSTGRES_ENTITY_TYPES.policy) {
42
50
  const signature = canonicalPgObject(entity.type, entity.props, defaultSchema).signature;
@@ -66,7 +74,7 @@ export function scopeFor(target, entities) {
66
74
  return undefined;
67
75
  const out = new Set([target.defaultSchema]);
68
76
  // The schema alone: the rest of an address (a routine's parameter types) needs props a caller may not have.
69
- const unplaced = new Set([POSTGRES_ENTITY_TYPES.extension, POSTGRES_ENTITY_TYPES.role, POSTGRES_ENTITY_TYPES.cast, POSTGRES_ENTITY_TYPES.eventTrigger]);
77
+ const unplaced = new Set([POSTGRES_ENTITY_TYPES.extension, POSTGRES_ENTITY_TYPES.role, POSTGRES_ENTITY_TYPES.cast, ...UNPLACED]);
70
78
  for (const e of entities) {
71
79
  if (e.type === POSTGRES_ENTITY_TYPES.schema)
72
80
  out.add(String(e.props.name ?? ""));
@@ -87,6 +87,8 @@ export interface VerifyResult {
87
87
  mismatched: number;
88
88
  /** Rows whose new column is NULL. */
89
89
  nulls: number;
90
+ /** A rename with a type change: rows whose old column differs from `reverse` over the new one. */
91
+ unreversed?: number;
90
92
  /** The sum of a 64-bit hash of every new value, and of every value the expression gives. */
91
93
  checksum: string;
92
94
  expected: string;
@@ -1 +1 @@
1
- {"version":3,"file":"backfill.d.ts","sourceRoot":"","sources":["../../../src/postgres/migrate/backfill.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAKH,OAAO,EAA0C,KAAK,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAC9F,OAAO,EAAwC,KAAK,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAC7F,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAmD,KAAK,YAAY,EAAE,MAAM,SAAS,CAAC;AAE7F,OAAO,EAAiD,KAAK,KAAK,EAAE,MAAM,WAAW,CAAC;AAEtF,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;IACrC,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAC;IAChB,kCAAkC;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,6BAA6B;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,+DAA+D;AAC/D,eAAO,MAAM,WAAW,GAAI,KAAK,MAAM,EAAE,OAAO,MAAM,KAAG,MAAmC,CAAC;AAE7F,8IAA8I;AAC9I,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,oBAAoB,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,GAAG,QAAQ,CAAC,GAAG,MAAM,CASrG;AAED,mGAAmG;AACnG,eAAO,MAAM,cAAc,YAAY,CAAC;AAExC,MAAM,WAAW,YAAY;IAC3B,yFAAyF;IACzF,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC,+GAA+G;IAC/G,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvD,4EAA4E;IAC5E,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,2CAA2C;IAC3C,eAAe,CAAC,EAAE,OAAO,eAAe,CAAC;CAC1C;AAED,wBAAsB,QAAQ,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,GAAE,YAAiB,GAAG,OAAO,CAAC,cAAc,CAAC,CA2DlG;AAID,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;IACrC,8GAA8G;IAC9G,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,UAAU,EAAE,MAAM,CAAC;IACnB,qCAAqC;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,4FAA4F;IAC5F,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,gDAAgD;IAChD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,uDAAuD;AACvD,qBAAa,0BAA2B,SAAQ,KAAK;IAEjD,QAAQ,CAAC,GAAG,EAAE,MAAM;IACpB,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,SAAS,GAAG,YAAY,CAAC;gBADpD,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,SAAS,GAAG,YAAY,CAAC,EAC7D,MAAM,EAAE,MAAM;CAKjB;AAED,wBAAsB,eAAe,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CA0C9E"}
1
+ {"version":3,"file":"backfill.d.ts","sourceRoot":"","sources":["../../../src/postgres/migrate/backfill.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAKH,OAAO,EAA0C,KAAK,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAC9F,OAAO,EAAwC,KAAK,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAC7F,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAmD,KAAK,YAAY,EAAE,MAAM,SAAS,CAAC;AAE7F,OAAO,EAAiD,KAAK,KAAK,EAAE,MAAM,WAAW,CAAC;AAEtF,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;IACrC,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAC;IAChB,kCAAkC;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,6BAA6B;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,+DAA+D;AAC/D,eAAO,MAAM,WAAW,GAAI,KAAK,MAAM,EAAE,OAAO,MAAM,KAAG,MAAmC,CAAC;AAE7F,8IAA8I;AAC9I,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,oBAAoB,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,GAAG,QAAQ,CAAC,GAAG,MAAM,CASrG;AAED,mGAAmG;AACnG,eAAO,MAAM,cAAc,YAAY,CAAC;AAExC,MAAM,WAAW,YAAY;IAC3B,yFAAyF;IACzF,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC,+GAA+G;IAC/G,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvD,4EAA4E;IAC5E,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,2CAA2C;IAC3C,eAAe,CAAC,EAAE,OAAO,eAAe,CAAC;CAC1C;AAED,wBAAsB,QAAQ,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,GAAE,YAAiB,GAAG,OAAO,CAAC,cAAc,CAAC,CA2DlG;AAID,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;IACrC,8GAA8G;IAC9G,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,UAAU,EAAE,MAAM,CAAC;IACnB,qCAAqC;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,kGAAkG;IAClG,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4FAA4F;IAC5F,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,gDAAgD;IAChD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,uDAAuD;AACvD,qBAAa,0BAA2B,SAAQ,KAAK;IAEjD,QAAQ,CAAC,GAAG,EAAE,MAAM;IACpB,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,SAAS,GAAG,YAAY,CAAC;gBADpD,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,SAAS,GAAG,YAAY,CAAC,EAC7D,MAAM,EAAE,MAAM;CAKjB;AAED,wBAAsB,eAAe,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAoD9E"}
@@ -142,6 +142,7 @@ export async function verifyMigration(run) {
142
142
  const nw = col(n.newColumn);
143
143
  const [row] = await inTransaction(run, (exec) => exec(`SELECT count(*)::text AS rows, count(*) FILTER (WHERE ${nw} IS DISTINCT FROM (${o.expression}))::text AS mismatched,
144
144
  count(*) FILTER (WHERE ${nw} IS NULL)::text AS nulls,
145
+ ${o.reverse !== undefined ? `count(*) FILTER (WHERE (${o.reverse}) IS DISTINCT FROM ${col(n.source)})::text` : "'0'"} AS unreversed,
145
146
  COALESCE(sum(pg_catalog.hashtextextended(${nw}::text, 0)), 0)::text AS checksum,
146
147
  COALESCE(sum(pg_catalog.hashtextextended((${o.expression})::text, 0)), 0)::text AS expected
147
148
  FROM ${n.qualifiedTable}`), { scan: true });
@@ -149,6 +150,10 @@ export async function verifyMigration(run) {
149
150
  if (r.mismatched > 0 || r.checksum !== r.expected) {
150
151
  throw new MigrationVerificationError(n.key, { state: o.state, ...r }, `${r.mismatched} of ${r.rows} row(s) have ${n.newColumn} different from ${o.expression} (checksums ${r.checksum} and ${r.expected})`);
151
152
  }
153
+ const unreversed = Number(row?.unreversed ?? 0);
154
+ if (o.reverse !== undefined && unreversed > 0) {
155
+ throw new MigrationVerificationError(n.key, { state: o.state, ...r, unreversed }, `${unreversed} of ${r.rows} row(s) have ${n.source} different from ${o.reverse}, which must give the old value back from the new one (it would write ${n.source} wrongly for a writer of ${n.column})`);
156
+ }
152
157
  if (requiresNotNull(o.column, o.carried) && r.nulls > 0) {
153
158
  throw new MigrationVerificationError(n.key, { state: o.state, ...r }, `${r.nulls} of ${r.rows} row(s) have ${n.newColumn} NULL, and ${n.column} is declared NOT NULL or is in the primary key`);
154
159
  }
@@ -159,12 +164,13 @@ export async function verifyMigration(run) {
159
164
  const carried = o.carried.length > 0 || o.views.length > 0
160
165
  ? `; ${o.carried.map((c) => `${c.kind} ${c.name}${c.target !== c.name ? ` (as ${c.target})` : ""}`).join(", ") || "no index or constraint"} carried over${o.views.length > 0 ? `, view(s) ${o.views.map((v) => v.name).join(", ")} made again at the switch` : ""}`
161
166
  : "";
162
- const summary = `${n.key}: ${r.rows} row(s), ${n.newColumn} equal to ${o.expression} in every one (checksum ${r.checksum})${carried}`;
167
+ const summary = `${n.key}: ${r.rows} row(s), ${n.newColumn} equal to ${o.expression} in every one (checksum ${r.checksum})${o.reverse !== undefined ? `, ${n.source} equal to ${o.reverse} in every one` : ""}${carried}`;
163
168
  run.log(`-- ${summary}`);
164
169
  return {
165
170
  state: o.state,
166
171
  planDigest: computePlanDigest("postgres-migration", { plan: migrationPlanSubject(o), table: o.oid, column: o.newColumn.attnum, verified: { mismatched: 0 } }),
167
172
  ...r,
173
+ ...(o.reverse !== undefined ? { unreversed } : {}),
168
174
  summary,
169
175
  };
170
176
  }
@@ -10,6 +10,7 @@
10
10
  * | On the old column | Before the switch | In the switch's transaction |
11
11
  * |---|---|---|
12
12
  * | an index | `CREATE INDEX CONCURRENTLY` on the new column | the old one dropped (a type change) or kept as `<name>__chant_old` (a rename), the new one renamed to the name |
13
+ * | an index on a partitioned table | `CREATE INDEX ... ON ONLY` the table, `CONCURRENTLY` on each partition, `ALTER INDEX ... ATTACH PARTITION` | as an index; the partitions' indexes go with the old one |
13
14
  * | a primary key or unique constraint | its unique index built `CONCURRENTLY` | the old constraint dropped (or a rename's unique one kept as `__chant_old`), `ADD CONSTRAINT ... USING INDEX` |
14
15
  * | a check or foreign key, this table's or another's that references the column | added `NOT VALID`, then `VALIDATE` | the old one dropped, the new one renamed to the name |
15
16
  * | a view (and the views that read it) | nothing | dropped, then created again from its declaration once the columns are swapped, with its grants and owner |
@@ -33,7 +34,8 @@
33
34
  * key or index the build declares on the new column takes the declared name.
34
35
  *
35
36
  * Refused, each named with what to do: exclusion constraints and constraint
36
- * triggers (no `USING INDEX` form), materialized views (made again only by
37
+ * triggers (no `USING INDEX` form), a key or foreign key on a partitioned table,
38
+ * a partitioned index over a foreign-table partition, materialized views (made again only by
37
39
  * a full scan in the switch), rules, triggers, policies and statistics on the
38
40
  * column, a generated column computed from it, an INVALID index on it, a view
39
41
  * the build does not declare, a view something other than a view depends on.
@@ -77,6 +79,12 @@ export interface CarriedObject {
77
79
  indexComment?: string;
78
80
  replicaIdentity?: boolean;
79
81
  clustered?: boolean;
82
+ /**
83
+ * An index on a partitioned table (#3333): `definition` is the `CREATE INDEX ... ON ONLY` that makes the
84
+ * partitioned index without building anything. Each partition's index is built `CONCURRENTLY` on its own and
85
+ * attached (`partitionIndexStatement`, `carryPartitionedIndex` in `./steps.ts`).
86
+ */
87
+ partitioned?: boolean;
80
88
  }
81
89
  /** A view on the old column (or on such a view), made again at the switch from its declaration. */
82
90
  export interface CarriedView {
@@ -130,6 +138,38 @@ export declare function replaceInForeignKey(def: string, from: string, to: strin
130
138
  * the `CONCURRENTLY` build of `working` on the new column.
131
139
  */
132
140
  export declare function carriedIndexStatement(def: string, working: string, from: string, to: string): string;
141
+ /**
142
+ * A partitioned index (`pg_get_indexdef` prints it `ON ONLY`) as the
143
+ * `CREATE INDEX ... ON ONLY` of `working` on the new column: it builds nothing
144
+ * and stays INVALID until every partition's index is attached.
145
+ */
146
+ export declare function carriedPartitionedIndexStatement(def: string, working: string, from: string, to: string): string;
147
+ /** One table of a partition tree. */
148
+ export interface PartitionNode {
149
+ oid: string;
150
+ schema: string;
151
+ name: string;
152
+ /** Quoted and qualified. */
153
+ ident: string;
154
+ /** The partitioned table (or partition) it is a partition of; absent for the root. */
155
+ parent?: string;
156
+ /** Not partitioned further: the tables that hold rows. */
157
+ leaf: boolean;
158
+ /** `r` (ordinary), `p` (partitioned) or `f` (foreign). */
159
+ kind: string;
160
+ level: number;
161
+ }
162
+ /** The partition tree under a partitioned table, the table itself first (level 0). */
163
+ export declare function partitionTree(client: Pick<PostgresClient, "query">, tableOid: string): Promise<PartitionNode[]>;
164
+ /** The name of the index a partitioned index's working copy has on one partition. */
165
+ export declare const partitionIndexName: (partition: Pick<PartitionNode, "name">, working: string) => string;
166
+ /**
167
+ * The statement that makes a partitioned index's working copy on one
168
+ * partition. A partition that is partitioned further gets `ON ONLY` (it is
169
+ * valid once its own partitions are attached); one that holds rows gets the
170
+ * `CONCURRENTLY` build, so writes go on.
171
+ */
172
+ export declare function partitionIndexStatement(c: Pick<CarriedObject, "definition">, node: Pick<PartitionNode, "ident" | "leaf">, indexName: string): string;
133
173
  /**
134
174
  * The name Postgres gives an object it names after its columns
135
175
  * (`users_email_key`, `orders_user_id_fkey`, `users_email_idx`), when `name`
@@ -149,7 +189,11 @@ interface DiscoverInput {
149
189
  defaultSchema: string;
150
190
  /** The table is partitioned: its indexes cannot be built CONCURRENTLY, nor its keys attached USING INDEX. */
151
191
  partitioned?: boolean;
192
+ /** A rename keeps the column's sequence on the old column until the contract (`defaultAtContract`), instead of being refused. */
193
+ keepSequence?: boolean;
152
194
  }
195
+ /** The sequence that belongs to a column (an identity's, or a `serial`'s), if it has one. */
196
+ export declare function columnSequence(client: PostgresClient, tableOid: string, attnum: number): Promise<ColumnSequence | undefined>;
153
197
  /** What uses the column the values come from, sorted into what the Op carries over and what it refuses. */
154
198
  export declare function discoverDependents(input: DiscoverInput): Promise<Dependents>;
155
199
  /** `ALTER TABLE ... ADD CONSTRAINT <working> <body> NOT VALID`: a check or foreign key on the new column. */
@@ -1 +1 @@
1
- {"version":3,"file":"carry.d.ts","sourceRoot":"","sources":["../../../src/postgres/migrate/carry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"carry.d.ts","sourceRoot":"","sources":["../../../src/postgres/migrate/carry.ts"],"names":[],"mappings":"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"}
@@ -10,6 +10,7 @@
10
10
  * | On the old column | Before the switch | In the switch's transaction |
11
11
  * |---|---|---|
12
12
  * | an index | `CREATE INDEX CONCURRENTLY` on the new column | the old one dropped (a type change) or kept as `<name>__chant_old` (a rename), the new one renamed to the name |
13
+ * | an index on a partitioned table | `CREATE INDEX ... ON ONLY` the table, `CONCURRENTLY` on each partition, `ALTER INDEX ... ATTACH PARTITION` | as an index; the partitions' indexes go with the old one |
13
14
  * | a primary key or unique constraint | its unique index built `CONCURRENTLY` | the old constraint dropped (or a rename's unique one kept as `__chant_old`), `ADD CONSTRAINT ... USING INDEX` |
14
15
  * | a check or foreign key, this table's or another's that references the column | added `NOT VALID`, then `VALIDATE` | the old one dropped, the new one renamed to the name |
15
16
  * | a view (and the views that read it) | nothing | dropped, then created again from its declaration once the columns are swapped, with its grants and owner |
@@ -33,7 +34,8 @@
33
34
  * key or index the build declares on the new column takes the declared name.
34
35
  *
35
36
  * Refused, each named with what to do: exclusion constraints and constraint
36
- * triggers (no `USING INDEX` form), materialized views (made again only by
37
+ * triggers (no `USING INDEX` form), a key or foreign key on a partitioned table,
38
+ * a partitioned index over a foreign-table partition, materialized views (made again only by
37
39
  * a full scan in the switch), rules, triggers, policies and statistics on the
38
40
  * column, a generated column computed from it, an INVALID index on it, a view
39
41
  * the build does not declare, a view something other than a view depends on.
@@ -120,6 +122,53 @@ export function carriedIndexStatement(def, working, from, to) {
120
122
  throw new Error(`unexpected index definition: ${def}`);
121
123
  return `CREATE ${m[1] ?? ""}INDEX CONCURRENTLY ${quoteIdent(working)} ON ${replaceColumn(m[2], from, to)}`;
122
124
  }
125
+ /**
126
+ * A partitioned index (`pg_get_indexdef` prints it `ON ONLY`) as the
127
+ * `CREATE INDEX ... ON ONLY` of `working` on the new column: it builds nothing
128
+ * and stays INVALID until every partition's index is attached.
129
+ */
130
+ export function carriedPartitionedIndexStatement(def, working, from, to) {
131
+ const m = /^CREATE (UNIQUE )?INDEX (?:"(?:[^"]|"")+"|\S+) ON (?:ONLY )?(.*)$/s.exec(def);
132
+ if (!m)
133
+ throw new Error(`unexpected index definition: ${def}`);
134
+ return `CREATE ${m[1] ?? ""}INDEX ${quoteIdent(working)} ON ONLY ${replaceColumn(m[2], from, to)}`;
135
+ }
136
+ /** The partition tree under a partitioned table, the table itself first (level 0). */
137
+ export async function partitionTree(client, tableOid) {
138
+ const rows = await client.query(`SELECT t.relid::oid::text AS oid, n.nspname AS schema, c.relname AS name, t.parentrelid::oid::text AS parent, t.isleaf AS leaf, c.relkind::text AS kind, t.level
139
+ FROM pg_catalog.pg_partition_tree($1::oid::pg_catalog.regclass) t JOIN pg_catalog.pg_class c ON c.oid = t.relid JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
140
+ ORDER BY t.level, n.nspname, c.relname`, [tableOid]);
141
+ return rows.map((r) => ({
142
+ oid: String(r.oid),
143
+ schema: r.schema,
144
+ name: r.name,
145
+ ident: `${quoteIdent(r.schema)}.${quoteIdent(r.name)}`,
146
+ ...(r.parent !== null ? { parent: String(r.parent) } : {}),
147
+ leaf: r.leaf === true,
148
+ kind: r.kind,
149
+ level: Number(r.level),
150
+ }));
151
+ }
152
+ /** The name of the index a partitioned index's working copy has on one partition. */
153
+ export const partitionIndexName = (partition, working) => pgName(`${partition.name}_${working}`);
154
+ /**
155
+ * The statement that makes a partitioned index's working copy on one
156
+ * partition. A partition that is partitioned further gets `ON ONLY` (it is
157
+ * valid once its own partitions are attached); one that holds rows gets the
158
+ * `CONCURRENTLY` build, so writes go on.
159
+ */
160
+ export function partitionIndexStatement(c, node, indexName) {
161
+ const m = /^CREATE (UNIQUE )?INDEX (?:"(?:[^"]|"")+"|\S+) ON ONLY (.*)$/s.exec(c.definition);
162
+ if (!m)
163
+ throw new Error(`unexpected partitioned index definition: ${c.definition}`);
164
+ const using = m[2].indexOf(" USING ");
165
+ if (using < 0)
166
+ throw new Error(`unexpected partitioned index definition: ${c.definition}`);
167
+ const tail = m[2].slice(using);
168
+ return node.leaf
169
+ ? `CREATE ${m[1] ?? ""}INDEX CONCURRENTLY ${quoteIdent(indexName)} ON ${node.ident}${tail}`
170
+ : `CREATE ${m[1] ?? ""}INDEX ${quoteIdent(indexName)} ON ONLY ${node.ident}${tail}`;
171
+ }
123
172
  /**
124
173
  * The name Postgres gives an object it names after its columns
125
174
  * (`users_email_key`, `orders_user_id_fkey`, `users_email_idx`), when `name`
@@ -134,6 +183,15 @@ export function renamedDefault(name, table, oldCols, newCols, suffix) {
134
183
  return Buffer.byteLength(next, "utf8") <= 63 && next !== name ? next : undefined;
135
184
  }
136
185
  const sameColumns = (a, b) => a.length === b.length && a.every((c, i) => c === b[i]);
186
+ /** The sequence that belongs to a column (an identity's, or a `serial`'s), if it has one. */
187
+ export async function columnSequence(client, tableOid, attnum) {
188
+ const [row] = await client.query(`SELECT n.nspname AS schema, c.relname AS name, d.deptype::text AS deptype
189
+ FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_class c ON c.oid = d.objid AND d.classid = 'pg_catalog.pg_class'::pg_catalog.regclass JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
190
+ WHERE d.refclassid = 'pg_catalog.pg_class'::pg_catalog.regclass AND d.refobjid = $1::oid AND d.refobjsubid = $2 AND d.deptype IN ('a', 'i') AND c.relkind = 'S'`, [tableOid, attnum]);
191
+ if (!row)
192
+ return undefined;
193
+ return { kind: row.deptype === "i" ? "identity" : "serial", schema: row.schema, name: row.name, ident: `${quoteIdent(row.schema)}.${quoteIdent(row.name)}` };
194
+ }
137
195
  /** What uses the column the values come from, sorted into what the Op carries over and what it refuses. */
138
196
  export async function discoverDependents(input) {
139
197
  const { client, names: n, oid, attnum } = input;
@@ -251,10 +309,6 @@ export async function discoverDependents(input) {
251
309
  if (kind === "i" || kind === "I") {
252
310
  if (keyIndexes.has(String(r.oid)))
253
311
  continue;
254
- if (kind === "I") {
255
- refused.push(`index ${r.schema}.${name} is a partitioned index, which cannot be built CONCURRENTLY; drop it, migrate, then declare it again (chant #3333)`);
256
- continue;
257
- }
258
312
  if (r.valid !== true) {
259
313
  refused.push(`index ${r.schema}.${name} is INVALID (a failed CONCURRENTLY build); drop it, or REINDEX it, first`);
260
314
  continue;
@@ -277,6 +331,14 @@ export async function discoverDependents(input) {
277
331
  });
278
332
  target = declaredIndex?.canonical.name ?? (r.has_exprs === true ? undefined : renamedDefault(name, table, oldCols, cols, "idx")) ?? name;
279
333
  }
334
+ if (kind === "I") {
335
+ // A partition the index cannot be built on (a foreign table) stops the carry before anything is made.
336
+ const foreign = (await partitionTree(client, oid)).filter((p) => p.kind === "f");
337
+ if (foreign.length > 0) {
338
+ refused.push(`partitioned index ${r.schema}.${name}: ${foreign.map((p) => `${p.schema}.${p.name}`).join(", ")} is a foreign table, which holds no index to attach; drop the index, migrate, then declare it again`);
339
+ continue;
340
+ }
341
+ }
280
342
  carried.push({
281
343
  kind: "index",
282
344
  name,
@@ -286,7 +348,8 @@ export async function discoverDependents(input) {
286
348
  schema: String(r.schema),
287
349
  working,
288
350
  target,
289
- definition: carriedIndexStatement(String(r.def), working, from, to),
351
+ definition: kind === "I" ? carriedPartitionedIndexStatement(String(r.def), working, from, to) : carriedIndexStatement(String(r.def), working, from, to),
352
+ ...(kind === "I" ? { partitioned: true } : {}),
290
353
  validated: true,
291
354
  ...(r.comment !== null ? { comment: String(r.comment) } : {}),
292
355
  ...(r.replident === true ? { replicaIdentity: true } : {}),
@@ -296,8 +359,9 @@ export async function discoverDependents(input) {
296
359
  else if (kind === "S") {
297
360
  const dep = deps.find((d) => d.kind === "class" && d.objid === String(r.oid));
298
361
  const seq = { kind: dep?.deptype === "i" ? "identity" : "serial", schema: String(r.schema), name, ident: `${quoteIdent(String(r.schema))}.${quoteIdent(name)}` };
299
- if (rename) {
300
- refused.push(`sequence ${r.schema}.${name} gives the column its values (${seq.kind === "identity" ? "an identity column" : "a serial column's default"}); during a rename a writer that names the new column would be given the old one's next value by the trigger, which it cannot tell from a value the writer chose; a rename that keeps its sequence needs versioned views (chant #3331)`);
362
+ if (rename && !input.keepSequence) {
363
+ refused.push(`sequence ${r.schema}.${name} gives the column its values (${seq.kind === "identity" ? "an identity column" : "a serial column's default"}); during a rename a writer that names the new column would get the old one's next value, and the trigger would take it over the writer's, ` +
364
+ `because it cannot tell a defaulted value from one the writer gave. Set defaultAtContract: true to leave the new column without the default until the contract (see the Op's reference), or drop the default first`);
301
365
  }
302
366
  else
303
367
  sequence = seq;
@@ -24,12 +24,13 @@
24
24
  * - `done`: the server holds the declared column and nothing of the
25
25
  * migration is left.
26
26
  *
27
+ * A partitioned table's indexes are carried over per partition (`./steps.ts`).
27
28
  * Anything else is refused with a {@link MigrationRefusal}, naming what to
28
29
  * do: a table that is not there, a partition (run it on the partitioned
29
30
  * table) or an inheritance tree; no primary key to batch by; a column in the
30
31
  * partition key; a generated column; a column change the Op does not make
31
- * (a rename and a type change at once, a rename of a column with a default
32
- * or a sequence); something that uses the column and is
32
+ * (a rename and a type change without `reverse`, a rename of a column with a
33
+ * default or a sequence without `defaultAtContract`); something that uses the column and is
33
34
  * not carried over to the new one (`./carry.ts`: indexes, key, check and
34
35
  * foreign key constraints and declared views are); a publication that sends
35
36
  * it; a working object chant did not make.
@@ -43,7 +44,7 @@ import type { DeclaredPgObject } from "../apply/statements.js";
43
44
  import { MIGRATION_TRAILER_KEY, type MigrationNames } from "./names.js";
44
45
  import { type PublicationHit } from "./replication.js";
45
46
  import { type KeyColumn } from "./batches.js";
46
- import { type CarriedObject, type CarriedState, type CarriedView, type ColumnSequence } from "./carry.js";
47
+ import { type CarriedObject, type CarriedState, type CarriedView, type ColumnSequence, type Dependents } from "./carry.js";
47
48
  /** A refusal: the migration cannot start, or cannot go on, for a reason a person has to act on. */
48
49
  export declare class MigrationRefusal extends Error {
49
50
  constructor(message: string);
@@ -87,7 +88,7 @@ export interface MigrationObservation {
87
88
  carriedStates: Map<string, CarriedState>;
88
89
  /** The views that read the old column, made again at the switch. */
89
90
  views: CarriedView[];
90
- /** The sequence that gives the old column its values (identity or serial), moved to the new column at the switch. */
91
+ /** The sequence that gives the old column its values (identity or serial): moved to the new column at the switch for a type change, at the contract for a rename. */
91
92
  sequence?: ColumnSequence;
92
93
  /** The table is partitioned: every step runs on it, and its partitions follow. */
93
94
  partitioned: boolean;
@@ -105,6 +106,10 @@ export interface MigrationObservation {
105
106
  changes: PgChange[];
106
107
  /** The expression the new column's value is computed with, over the old row's columns. */
107
108
  expression: string;
109
+ /** A rename with a type change: the expression over the new row's columns that gives the old column's value. */
110
+ reverse?: string;
111
+ /** A rename keeps its default, serial or identity on the old column until the contract (`defaultAtContract`). */
112
+ defaultAtContract?: boolean;
108
113
  publications: PublicationHit[];
109
114
  major: number;
110
115
  }
@@ -118,6 +123,10 @@ export interface ObserveInput {
118
123
  objects?: readonly DeclaredPgObject[];
119
124
  /** The type change's expression, over the old row's columns. Default: `CAST(<column> AS <declared type>)`. */
120
125
  using?: string;
126
+ /** A rename with a type change: the expression over the new row's columns that gives the old column's value. */
127
+ reverse?: string;
128
+ /** A rename of a column with a default, serial or identity: the declared default lands on the new column at the contract, not the switch. */
129
+ defaultAtContract?: boolean;
121
130
  /** The major the build targets; the server's own wins. */
122
131
  major?: number;
123
132
  }
@@ -125,8 +134,23 @@ export interface ObserveInput {
125
134
  export declare function declaredTable(declared: readonly DeclaredPgObject[], key: string): DeclaredPgObject;
126
135
  /** The server's major, from `server_version_num`. */
127
136
  export declare function serverMajor(client: PostgresClient): Promise<number>;
137
+ export interface RelationRow {
138
+ oid: string;
139
+ kind: string;
140
+ partition: boolean;
141
+ parents: string | null;
142
+ children: string | null;
143
+ partkey: string | null;
144
+ partattrs: number[] | null;
145
+ }
128
146
  /** Observe the migration of the declared table's column on the server. */
129
147
  export declare function observeMigration(input: ObserveInput): Promise<MigrationObservation>;
148
+ /** The checks that hold only while there is something to migrate. Returns the batch key and what the Op carries over. */
149
+ export declare function refuseUnsupported(input: ObserveInput, names: MigrationNames, rel: RelationRow, source: ServerColumn, column: ColumnDef, changes: readonly PgChange[], rename: boolean, typeChange: PgChange | undefined): Promise<{
150
+ batchKey: string;
151
+ keyColumns: KeyColumn[];
152
+ dependents: Pick<Dependents, "carried" | "views" | "sequence">;
153
+ }>;
130
154
  /**
131
155
  * What the migration will do, independent of how far it has got: the
132
156
  * change, the column on both sides, the expression, the batch key. The
@@ -1 +1 @@
1
- {"version":3,"file":"observe.d.ts","sourceRoot":"","sources":["../../../src/postgres/migrate/observe.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"observe.d.ts","sourceRoot":"","sources":["../../../src/postgres/migrate/observe.ts"],"names":[],"mappings":"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"}
@@ -24,12 +24,13 @@
24
24
  * - `done`: the server holds the declared column and nothing of the
25
25
  * migration is left.
26
26
  *
27
+ * A partitioned table's indexes are carried over per partition (`./steps.ts`).
27
28
  * Anything else is refused with a {@link MigrationRefusal}, naming what to
28
29
  * do: a table that is not there, a partition (run it on the partitioned
29
30
  * table) or an inheritance tree; no primary key to batch by; a column in the
30
31
  * partition key; a generated column; a column change the Op does not make
31
- * (a rename and a type change at once, a rename of a column with a default
32
- * or a sequence); something that uses the column and is
32
+ * (a rename and a type change without `reverse`, a rename of a column with a
33
+ * default or a sequence without `defaultAtContract`); something that uses the column and is
33
34
  * not carried over to the new one (`./carry.ts`: indexes, key, check and
34
35
  * foreign key constraints and declared views are); a publication that sends
35
36
  * it; a working object chant did not make.
@@ -43,7 +44,7 @@ import { POSTGRES_ENTITY_TYPES } from "../entity-types.js";
43
44
  import { col, migrationNames, MIGRATION_TRAILER_KEY, pgName } from "./names.js";
44
45
  import { publicationsOf } from "./replication.js";
45
46
  import { keyText } from "./batches.js";
46
- import { carriedStates, carriedSubject, discoverDependents, replaceColumn, storedType } from "./carry.js";
47
+ import { carriedStates, carriedSubject, columnSequence, discoverDependents, replaceColumn, storedType } from "./carry.js";
47
48
  /** A refusal: the migration cannot start, or cannot go on, for a reason a person has to act on. */
48
49
  export class MigrationRefusal extends Error {
49
50
  constructor(message) {
@@ -241,7 +242,8 @@ export async function observeMigration(input) {
241
242
  }, (what) => {
242
243
  throw new MigrationRefusal(`${where}: ${what} exists and is not this migration's (its comment carries no migration=${names.key} role=carry marker for this project). The migration uses that name, so it stops rather than touch it; rename or drop it by hand.`);
243
244
  });
244
- if (change === "type") {
245
+ const retyped = change === "rename" && input.reverse !== undefined;
246
+ if (change === "type" || retyped) {
245
247
  expression = input.using ?? `CAST(${col(sourceName)} AS ${storedType(column.type)})`;
246
248
  if (expression.includes("$chant$"))
247
249
  throw new MigrationRefusal(`${where}: the using expression may not contain $chant$, which quotes the dual-write function's body`);
@@ -251,7 +253,7 @@ export async function observeMigration(input) {
251
253
  }
252
254
  // The declared type as the classifier compared it: the server's spelling (character varying(10), not varchar(10)).
253
255
  const serial = storedType(column.type) !== column.type;
254
- const expectedType = change === "type" ? (serial ? storedType(column.type) : String(typeChange?.after ?? column.type)) : source.type;
256
+ const expectedType = change === "type" || retyped ? (serial ? storedType(column.type) : String(typeChange?.after ?? column.type)) : source.type;
255
257
  if (newColumn && newColumn.type !== expectedType) {
256
258
  throw new MigrationRefusal(`${where}: ${newColumn.name} was made as ${newColumn.type}, from another declaration of ${input.column} (now ${column.type}). The run fails so onFailure drops it, and the next run starts again from the current declaration.`);
257
259
  }
@@ -264,6 +266,12 @@ export async function observeMigration(input) {
264
266
  `A migration of the publisher and the subscriber together is chant #3332.`);
265
267
  }
266
268
  }
269
+ // After a rename's switch the old column still holds the default or the identity the contract moves, and its sequence is read for it.
270
+ if (state === "switched" && old && (old.identity || old.default?.includes("nextval("))) {
271
+ const seq = await columnSequence(client, rel.oid, old.attnum);
272
+ if (seq)
273
+ dependents = { ...dependents, sequence: seq };
274
+ }
267
275
  return {
268
276
  state,
269
277
  names,
@@ -286,23 +294,41 @@ export async function observeMigration(input) {
286
294
  ...(check ? { check } : {}),
287
295
  changes,
288
296
  expression,
297
+ ...(input.reverse !== undefined && state === "migrate" ? { reverse: input.reverse } : {}),
298
+ ...(input.defaultAtContract && state === "migrate" ? { defaultAtContract: true } : {}),
289
299
  publications,
290
300
  major,
291
301
  };
292
302
  }
293
303
  /** The checks that hold only while there is something to migrate. Returns the batch key and what the Op carries over. */
294
- async function refuseUnsupported(input, names, rel, source, column, changes, rename, typeChange) {
304
+ export async function refuseUnsupported(input, names, rel, source, column, changes, rename, typeChange) {
295
305
  const where = `${names.schema}.${names.table}`;
296
306
  const oid = rel.oid;
297
- if (rename && typeChange) {
307
+ if (rename && typeChange && input.reverse === undefined) {
298
308
  throw new MigrationRefusal(`${where}: ${source.name} is renamed to ${names.column} and its type changes (${typeChange.before} -> ${typeChange.after}) in one declaration. ` +
299
- `Migrate one at a time: declare the rename with the old type, run the Op to its end, then change the type. ` +
300
- `(In one run both names would stay written until the contract, which needs a conversion back to the old type for writers of the new name; chant #3331.)`);
309
+ `Both names stay written until the contract, so a writer of the new name needs its value converted back to the old type: give that expression as reverse ` +
310
+ `(over the row's columns, the new column by its declared name; using gives the new value from the old one). The verification fails the run unless reverse over every new value gives the old one back. ` +
311
+ `Or migrate one at a time: declare the rename with the old type, run the Op to its end, then change the type.`);
312
+ }
313
+ if (input.reverse !== undefined && !(rename && typeChange)) {
314
+ throw new MigrationRefusal(`${where}.${names.column}: reverse is for a rename combined with a type change, and this migration is ${rename ? "a rename only" : "a type change without a rename"}`);
315
+ }
316
+ if (input.reverse !== undefined && input.reverse.includes("$chant$")) {
317
+ throw new MigrationRefusal(`${where}: the reverse expression may not contain $chant$, which quotes the dual-write function's body`);
301
318
  }
302
- if (rename && (source.default !== undefined || column.default !== undefined)) {
303
- throw new MigrationRefusal(`${where}: ${source.name} has a default (${source.default ?? column.default}). During a rename both columns are written, each from the other, ` +
319
+ const hasDefault = source.default !== undefined || column.default !== undefined;
320
+ if (rename && typeChange && (hasDefault || source.identity || column.generated?.kind === "identity")) {
321
+ throw new MigrationRefusal(`${where}: ${source.name} has a default, serial or identity, and its rename also changes its type. The default is an expression of the old type; converting it, and the sequence's type, ` +
322
+ `is a third conversion on top of using and reverse that nothing verifies. Rename first (defaultAtContract: true), run the Op to its end, then change the type.`);
323
+ }
324
+ if (rename && (hasDefault || source.identity) && !input.defaultAtContract) {
325
+ throw new MigrationRefusal(`${where}: ${source.name} has a default (${source.default ?? column.default ?? "identity"}). During a rename both columns are written, each from the other, ` +
304
326
  `and a row inserted by a writer that names one column would get the other's default, which the trigger cannot tell from a value it was given. ` +
305
- `Drop the default first (the applier does), rename, then declare it again. A rename that keeps its default needs versioned views (chant #3331).`);
327
+ `Set defaultAtContract: true to leave the new column without the default until the contract, which gives the default to the new column in the contract's transaction; ` +
328
+ `until then an INSERT that names the new column with an explicit NULL gets the default, and UPDATE ... SET <new> = DEFAULT sets NULL. Or drop the default first (the applier does), rename, then declare it again.`);
329
+ }
330
+ if (input.defaultAtContract && !(rename && (hasDefault || source.identity))) {
331
+ throw new MigrationRefusal(`${where}.${names.column}: defaultAtContract is for a rename of a column with a default, serial or identity, and this migration is not one`);
306
332
  }
307
333
  if (source.generated || (column.generated && column.generated.kind !== "identity")) {
308
334
  throw new MigrationRefusal(`${where}.${source.name} is a generated column. There is no expand and contract for one: Postgres cannot make an existing column generated, so the new column could only be added generated, ` +
@@ -324,6 +350,7 @@ async function refuseUnsupported(input, names, rel, source, column, changes, ren
324
350
  declared: input.declared,
325
351
  defaultSchema: input.target.defaultSchema,
326
352
  partitioned: rel.kind === "p",
353
+ ...(input.defaultAtContract ? { keepSequence: true } : {}),
327
354
  });
328
355
  if (dependents.refused.length > 0) {
329
356
  throw new MigrationRefusal(`${where}.${source.name} is used by what the migration Op does not carry over to the new column: ${dependents.refused.join("; ")}. ` +
@@ -351,6 +378,8 @@ export function migrationPlanSubject(o) {
351
378
  from: o.source ? { name: o.source.name, type: o.source.type, notNull: o.source.notNull, default: o.source.default ?? null } : null,
352
379
  to: { name: o.column.name, type: o.column.type ?? null, notNull: o.column.notNull === true, default: o.column.default ?? null, comment: o.column.comment ?? null },
353
380
  expression: o.expression,
381
+ ...(o.reverse !== undefined ? { reverse: o.reverse } : {}),
382
+ ...(o.defaultAtContract ? { defaultAtContract: true } : {}),
354
383
  batchKey: o.batchKey,
355
384
  rules: o.changes.map((c) => c.rule),
356
385
  ...(o.carried.length > 0 || o.views.length > 0 || o.sequence ? { dependents: carriedSubject(o) } : {}),
@@ -20,7 +20,7 @@
20
20
  * | Expand | add the new column, nullable, no default (a catalog change) |
21
21
  * | Dual write | a trigger that keeps the new column written: computed from the old one for a type change, both ways for a rename |
22
22
  * | Backfill | `UPDATE` per key range, each batch with its receipt in its own transaction, under `lock_timeout`, pausing while a replica is behind |
23
- * | Carry over | each index on the old column built again on the new one `CONCURRENTLY`, each check and foreign key (another table's too) added `NOT VALID` and validated (`./carry.ts`) |
23
+ * | Carry over | each index on the old column built again on the new one `CONCURRENTLY` (on a partitioned table: `ON ONLY` the table, `CONCURRENTLY` per partition, then attached), each check and foreign key (another table's too) added `NOT VALID` and validated (`./carry.ts`) |
24
24
  * | Verify | rows, mismatches and checksums of the new column against the old |
25
25
  * | Approve | a gate bound to the plan and the verified new column |
26
26
  * | Switch | NOT NULL proven by a validated check; then one short transaction: a type change swaps the columns by name, a rename finishes the new column; the carried indexes and constraints take their names (keys `USING INDEX`), and the views that read the column are made again from their declarations |
@@ -60,6 +60,29 @@ export interface PostgresMigrationOpConfig {
60
60
  * and the verification all use it.
61
61
  */
62
62
  using?: string;
63
+ /**
64
+ * A rename combined with a type change: the SQL expression that gives the
65
+ * old column's value from the new column's, over the row's columns (the
66
+ * new column by its declared name). `using` gives the new value from the
67
+ * old one. Both stay in force until the contract: a writer of the old name
68
+ * has `using` applied, a writer of the new name has `reverse` applied. The
69
+ * verification fails the run unless `reverse` over every new value gives
70
+ * the old value back. A new value the old type cannot hold fails the
71
+ * writer's statement until the contract drops the old column.
72
+ */
73
+ reverse?: string;
74
+ /**
75
+ * A rename of a column with a default, an identity or a serial sequence.
76
+ * The declared default (or identity, or the serial's default) lands on the
77
+ * new column at the contract, not at the switch, so that until then a
78
+ * writer of either name gets the old column's default once. Two writes
79
+ * differ from the declaration until then, and `true` accepts both: an
80
+ * INSERT that names the new column with an explicit NULL gets the default
81
+ * instead of NULL, and `UPDATE ... SET <new> = DEFAULT` sets NULL. An
82
+ * `ApplyOp` between the switch and the contract must not set the default
83
+ * on the new column. Needs `gates: "own"` to end with the contract.
84
+ */
85
+ defaultAtContract?: boolean;
63
86
  /** The build output the declaration is read from. Default: `dist/schema.json`. */
64
87
  output?: string;
65
88
  /** Project directory to build. Default: `.`. */
@@ -139,6 +162,8 @@ export interface PostgresMigrationArgs {
139
162
  buildPath: string;
140
163
  environment?: string;
141
164
  using?: string;
165
+ reverse?: string;
166
+ defaultAtContract?: boolean;
142
167
  retain?: string;
143
168
  batchSize?: number;
144
169
  replicationLag?: {