@rocicorp/zero 1.3.0 → 1.4.0-canary.0

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 (311) hide show
  1. package/out/analyze-query/src/analyze-cli.d.ts +24 -0
  2. package/out/analyze-query/src/analyze-cli.d.ts.map +1 -0
  3. package/out/analyze-query/src/analyze-cli.js +279 -0
  4. package/out/analyze-query/src/analyze-cli.js.map +1 -0
  5. package/out/analyze-query/src/bin-analyze.js +6 -6
  6. package/out/analyze-query/src/bin-transform.js +2 -2
  7. package/out/ast-to-zql/src/bin.js +1 -1
  8. package/out/shared/src/logging.d.ts.map +1 -1
  9. package/out/shared/src/logging.js +1 -1
  10. package/out/shared/src/logging.js.map +1 -1
  11. package/out/shared/src/options.d.ts.map +1 -1
  12. package/out/shared/src/options.js +1 -1
  13. package/out/shared/src/options.js.map +1 -1
  14. package/out/z2s/src/compiler.d.ts.map +1 -1
  15. package/out/z2s/src/compiler.js +4 -1
  16. package/out/z2s/src/compiler.js.map +1 -1
  17. package/out/z2s/src/sql.d.ts.map +1 -1
  18. package/out/z2s/src/sql.js +1 -0
  19. package/out/z2s/src/sql.js.map +1 -1
  20. package/out/zero/package.js +95 -89
  21. package/out/zero/package.js.map +1 -1
  22. package/out/zero/src/analyze.d.ts +2 -0
  23. package/out/zero/src/analyze.d.ts.map +1 -0
  24. package/out/zero/src/analyze.js +2 -0
  25. package/out/zero/src/zero-cache-dev.js +1 -1
  26. package/out/zero/src/zero-cache-dev.js.map +1 -1
  27. package/out/zero/src/zero-out.js +1 -1
  28. package/out/zero-cache/src/auth/auth.d.ts.map +1 -1
  29. package/out/zero-cache/src/auth/auth.js.map +1 -1
  30. package/out/zero-cache/src/auth/load-permissions.js +2 -2
  31. package/out/zero-cache/src/auth/write-authorizer.d.ts.map +1 -1
  32. package/out/zero-cache/src/auth/write-authorizer.js +5 -14
  33. package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
  34. package/out/zero-cache/src/config/network.d.ts +1 -1
  35. package/out/zero-cache/src/config/network.d.ts.map +1 -1
  36. package/out/zero-cache/src/config/network.js +1 -1
  37. package/out/zero-cache/src/config/network.js.map +1 -1
  38. package/out/zero-cache/src/config/normalize.d.ts.map +1 -1
  39. package/out/zero-cache/src/config/normalize.js.map +1 -1
  40. package/out/zero-cache/src/config/zero-config.d.ts +5 -0
  41. package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
  42. package/out/zero-cache/src/config/zero-config.js +16 -3
  43. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  44. package/out/zero-cache/src/db/lite-tables.d.ts.map +1 -1
  45. package/out/zero-cache/src/db/lite-tables.js +3 -3
  46. package/out/zero-cache/src/db/lite-tables.js.map +1 -1
  47. package/out/zero-cache/src/db/transaction-pool.d.ts +43 -40
  48. package/out/zero-cache/src/db/transaction-pool.d.ts.map +1 -1
  49. package/out/zero-cache/src/db/transaction-pool.js +76 -56
  50. package/out/zero-cache/src/db/transaction-pool.js.map +1 -1
  51. package/out/zero-cache/src/observability/events.d.ts.map +1 -1
  52. package/out/zero-cache/src/observability/events.js +1 -1
  53. package/out/zero-cache/src/observability/events.js.map +1 -1
  54. package/out/zero-cache/src/scripts/decommission.js +1 -1
  55. package/out/zero-cache/src/scripts/deploy-permissions.js +2 -2
  56. package/out/zero-cache/src/scripts/permissions.js +1 -1
  57. package/out/zero-cache/src/server/anonymous-otel-start.d.ts.map +1 -1
  58. package/out/zero-cache/src/server/anonymous-otel-start.js +4 -4
  59. package/out/zero-cache/src/server/anonymous-otel-start.js.map +1 -1
  60. package/out/zero-cache/src/server/change-streamer.d.ts +1 -1
  61. package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
  62. package/out/zero-cache/src/server/change-streamer.js +27 -12
  63. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  64. package/out/zero-cache/src/server/logging.d.ts +1 -3
  65. package/out/zero-cache/src/server/logging.d.ts.map +1 -1
  66. package/out/zero-cache/src/server/logging.js +6 -3
  67. package/out/zero-cache/src/server/logging.js.map +1 -1
  68. package/out/zero-cache/src/server/main.d.ts.map +1 -1
  69. package/out/zero-cache/src/server/main.js +26 -26
  70. package/out/zero-cache/src/server/main.js.map +1 -1
  71. package/out/zero-cache/src/server/mutator.js +4 -2
  72. package/out/zero-cache/src/server/mutator.js.map +1 -1
  73. package/out/zero-cache/src/server/otel-log-sink.d.ts.map +1 -1
  74. package/out/zero-cache/src/server/otel-log-sink.js +0 -2
  75. package/out/zero-cache/src/server/otel-log-sink.js.map +1 -1
  76. package/out/zero-cache/src/server/otel-start.d.ts +1 -1
  77. package/out/zero-cache/src/server/otel-start.d.ts.map +1 -1
  78. package/out/zero-cache/src/server/otel-start.js +7 -3
  79. package/out/zero-cache/src/server/otel-start.js.map +1 -1
  80. package/out/zero-cache/src/server/reaper.js +6 -6
  81. package/out/zero-cache/src/server/reaper.js.map +1 -1
  82. package/out/zero-cache/src/server/replicator.d.ts.map +1 -1
  83. package/out/zero-cache/src/server/replicator.js +5 -3
  84. package/out/zero-cache/src/server/replicator.js.map +1 -1
  85. package/out/zero-cache/src/server/runner/run-worker.js +2 -2
  86. package/out/zero-cache/src/server/runner/run-worker.js.map +1 -1
  87. package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
  88. package/out/zero-cache/src/server/syncer.js +13 -12
  89. package/out/zero-cache/src/server/syncer.js.map +1 -1
  90. package/out/zero-cache/src/server/worker-dispatcher.js +1 -1
  91. package/out/zero-cache/src/services/analyze.js +1 -1
  92. package/out/zero-cache/src/services/change-source/common/backfill-manager.js +1 -1
  93. package/out/zero-cache/src/services/change-source/common/replica-schema.js +1 -1
  94. package/out/zero-cache/src/services/change-source/custom/change-source.js +4 -4
  95. package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
  96. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +4 -1
  97. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
  98. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +1 -1
  99. package/out/zero-cache/src/services/change-source/pg/change-source.js +19 -23
  100. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  101. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts +58 -3
  102. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts.map +1 -1
  103. package/out/zero-cache/src/services/change-source/pg/initial-sync.js +209 -52
  104. package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
  105. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js +2 -2
  106. package/out/zero-cache/src/services/change-source/pg/schema/ddl.d.ts +24 -15
  107. package/out/zero-cache/src/services/change-source/pg/schema/ddl.d.ts.map +1 -1
  108. package/out/zero-cache/src/services/change-source/pg/schema/ddl.js +35 -58
  109. package/out/zero-cache/src/services/change-source/pg/schema/ddl.js.map +1 -1
  110. package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts.map +1 -1
  111. package/out/zero-cache/src/services/change-source/pg/schema/init.js +2 -2
  112. package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
  113. package/out/zero-cache/src/services/change-source/pg/schema/published.d.ts +1 -2
  114. package/out/zero-cache/src/services/change-source/pg/schema/published.d.ts.map +1 -1
  115. package/out/zero-cache/src/services/change-source/pg/schema/published.js +15 -18
  116. package/out/zero-cache/src/services/change-source/pg/schema/published.js.map +1 -1
  117. package/out/zero-cache/src/services/change-source/pg/schema/shard.js +1 -1
  118. package/out/zero-cache/src/services/change-source/protocol/current/data.js +1 -1
  119. package/out/zero-cache/src/services/change-streamer/backup-monitor.js +1 -1
  120. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts +1 -1
  121. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts.map +1 -1
  122. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +4 -4
  123. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
  124. package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts +5 -1
  125. package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts.map +1 -1
  126. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +10 -7
  127. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  128. package/out/zero-cache/src/services/change-streamer/replica-monitor.js +2 -2
  129. package/out/zero-cache/src/services/change-streamer/storer.d.ts +19 -2
  130. package/out/zero-cache/src/services/change-streamer/storer.d.ts.map +1 -1
  131. package/out/zero-cache/src/services/change-streamer/storer.js +70 -6
  132. package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
  133. package/out/zero-cache/src/services/heapz.d.ts.map +1 -1
  134. package/out/zero-cache/src/services/heapz.js +1 -1
  135. package/out/zero-cache/src/services/heapz.js.map +1 -1
  136. package/out/zero-cache/src/services/life-cycle.d.ts +2 -1
  137. package/out/zero-cache/src/services/life-cycle.d.ts.map +1 -1
  138. package/out/zero-cache/src/services/life-cycle.js +10 -7
  139. package/out/zero-cache/src/services/life-cycle.js.map +1 -1
  140. package/out/zero-cache/src/services/litestream/commands.d.ts +15 -4
  141. package/out/zero-cache/src/services/litestream/commands.d.ts.map +1 -1
  142. package/out/zero-cache/src/services/litestream/commands.js +40 -34
  143. package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
  144. package/out/zero-cache/src/services/mutagen/mutagen.js +3 -3
  145. package/out/zero-cache/src/services/mutagen/pusher.d.ts +28 -28
  146. package/out/zero-cache/src/services/replicator/change-processor.js +2 -2
  147. package/out/zero-cache/src/services/replicator/incremental-sync.js +1 -1
  148. package/out/zero-cache/src/services/replicator/schema/replication-state.js +1 -1
  149. package/out/zero-cache/src/services/replicator/write-worker-client.js.map +1 -1
  150. package/out/zero-cache/src/services/replicator/write-worker.js +3 -3
  151. package/out/zero-cache/src/services/replicator/write-worker.js.map +1 -1
  152. package/out/zero-cache/src/services/run-ast.d.ts.map +1 -1
  153. package/out/zero-cache/src/services/run-ast.js +2 -2
  154. package/out/zero-cache/src/services/run-ast.js.map +1 -1
  155. package/out/zero-cache/src/services/statz.d.ts.map +1 -1
  156. package/out/zero-cache/src/services/statz.js +3 -3
  157. package/out/zero-cache/src/services/statz.js.map +1 -1
  158. package/out/zero-cache/src/services/view-syncer/active-users-gauge.js +1 -1
  159. package/out/zero-cache/src/services/view-syncer/connection-context-manager.d.ts +2 -2
  160. package/out/zero-cache/src/services/view-syncer/connection-context-manager.d.ts.map +1 -1
  161. package/out/zero-cache/src/services/view-syncer/connection-context-manager.js.map +1 -1
  162. package/out/zero-cache/src/services/view-syncer/cvr-purger.js +1 -1
  163. package/out/zero-cache/src/services/view-syncer/cvr-store.js +3 -3
  164. package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
  165. package/out/zero-cache/src/services/view-syncer/cvr.js +1 -1
  166. package/out/zero-cache/src/services/view-syncer/inspect-handler.js +2 -2
  167. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts +6 -16
  168. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
  169. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +30 -38
  170. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  171. package/out/zero-cache/src/services/view-syncer/row-record-cache.d.ts.map +1 -1
  172. package/out/zero-cache/src/services/view-syncer/row-record-cache.js +4 -4
  173. package/out/zero-cache/src/services/view-syncer/row-record-cache.js.map +1 -1
  174. package/out/zero-cache/src/services/view-syncer/snapshotter.js +2 -2
  175. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
  176. package/out/zero-cache/src/services/view-syncer/view-syncer.js +6 -6
  177. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  178. package/out/zero-cache/src/types/profiler.d.ts.map +1 -1
  179. package/out/zero-cache/src/types/profiler.js.map +1 -1
  180. package/out/zero-cache/src/types/row-key.d.ts.map +1 -1
  181. package/out/zero-cache/src/types/row-key.js.map +1 -1
  182. package/out/zero-cache/src/types/streams.d.ts +1 -1
  183. package/out/zero-cache/src/types/streams.d.ts.map +1 -1
  184. package/out/zero-cache/src/types/streams.js.map +1 -1
  185. package/out/zero-cache/src/types/websocket-handoff.d.ts +1 -1
  186. package/out/zero-cache/src/types/websocket-handoff.d.ts.map +1 -1
  187. package/out/zero-cache/src/types/websocket-handoff.js +1 -1
  188. package/out/zero-cache/src/types/websocket-handoff.js.map +1 -1
  189. package/out/zero-cache/src/workers/connection.d.ts +1 -1
  190. package/out/zero-cache/src/workers/connection.d.ts.map +1 -1
  191. package/out/zero-cache/src/workers/connection.js +2 -2
  192. package/out/zero-cache/src/workers/connection.js.map +1 -1
  193. package/out/zero-cache/src/workers/mutator.js.map +1 -1
  194. package/out/zero-cache/src/workers/syncer.d.ts +1 -1
  195. package/out/zero-cache/src/workers/syncer.d.ts.map +1 -1
  196. package/out/zero-cache/src/workers/syncer.js +3 -3
  197. package/out/zero-cache/src/workers/syncer.js.map +1 -1
  198. package/out/zero-client/src/client/crud-impl.d.ts.map +1 -1
  199. package/out/zero-client/src/client/crud-impl.js +4 -13
  200. package/out/zero-client/src/client/crud-impl.js.map +1 -1
  201. package/out/zero-client/src/client/inspector/inspector.d.ts +24 -0
  202. package/out/zero-client/src/client/inspector/inspector.d.ts.map +1 -1
  203. package/out/zero-client/src/client/inspector/inspector.js +28 -0
  204. package/out/zero-client/src/client/inspector/inspector.js.map +1 -1
  205. package/out/zero-client/src/client/inspector/lazy-inspector.d.ts +9 -0
  206. package/out/zero-client/src/client/inspector/lazy-inspector.d.ts.map +1 -1
  207. package/out/zero-client/src/client/inspector/lazy-inspector.js +28 -1
  208. package/out/zero-client/src/client/inspector/lazy-inspector.js.map +1 -1
  209. package/out/zero-client/src/client/ivm-branch.d.ts.map +1 -1
  210. package/out/zero-client/src/client/ivm-branch.js +4 -13
  211. package/out/zero-client/src/client/ivm-branch.js.map +1 -1
  212. package/out/zero-client/src/client/version.js +1 -1
  213. package/out/zero-protocol/src/error.d.ts.map +1 -1
  214. package/out/zero-protocol/src/error.js +1 -1
  215. package/out/zero-protocol/src/error.js.map +1 -1
  216. package/out/zero-solid/src/solid-view.d.ts.map +1 -1
  217. package/out/zero-solid/src/solid-view.js +13 -13
  218. package/out/zero-solid/src/solid-view.js.map +1 -1
  219. package/out/zql/src/builder/builder.d.ts.map +1 -1
  220. package/out/zql/src/builder/builder.js.map +1 -1
  221. package/out/zql/src/ivm/array-view.d.ts.map +1 -1
  222. package/out/zql/src/ivm/array-view.js +26 -1
  223. package/out/zql/src/ivm/array-view.js.map +1 -1
  224. package/out/zql/src/ivm/change-index-enum.d.ts +9 -0
  225. package/out/zql/src/ivm/change-index-enum.d.ts.map +1 -0
  226. package/out/zql/src/ivm/change-index.d.ts +5 -0
  227. package/out/zql/src/ivm/change-index.d.ts.map +1 -0
  228. package/out/zql/src/ivm/change-type-enum.d.ts +9 -0
  229. package/out/zql/src/ivm/change-type-enum.d.ts.map +1 -0
  230. package/out/zql/src/ivm/change-type.d.ts +5 -0
  231. package/out/zql/src/ivm/change-type.d.ts.map +1 -0
  232. package/out/zql/src/ivm/change.d.ts +20 -22
  233. package/out/zql/src/ivm/change.d.ts.map +1 -1
  234. package/out/zql/src/ivm/change.js +33 -0
  235. package/out/zql/src/ivm/change.js.map +1 -0
  236. package/out/zql/src/ivm/exists.d.ts.map +1 -1
  237. package/out/zql/src/ivm/exists.js +27 -38
  238. package/out/zql/src/ivm/exists.js.map +1 -1
  239. package/out/zql/src/ivm/fan-in.d.ts +3 -2
  240. package/out/zql/src/ivm/fan-in.d.ts.map +1 -1
  241. package/out/zql/src/ivm/fan-in.js.map +1 -1
  242. package/out/zql/src/ivm/fan-out.d.ts +1 -1
  243. package/out/zql/src/ivm/fan-out.d.ts.map +1 -1
  244. package/out/zql/src/ivm/fan-out.js +1 -1
  245. package/out/zql/src/ivm/fan-out.js.map +1 -1
  246. package/out/zql/src/ivm/filter-operators.d.ts +3 -3
  247. package/out/zql/src/ivm/filter-operators.d.ts.map +1 -1
  248. package/out/zql/src/ivm/filter-operators.js.map +1 -1
  249. package/out/zql/src/ivm/filter-push.d.ts.map +1 -1
  250. package/out/zql/src/ivm/filter-push.js +7 -7
  251. package/out/zql/src/ivm/filter-push.js.map +1 -1
  252. package/out/zql/src/ivm/filter.d.ts +1 -1
  253. package/out/zql/src/ivm/filter.d.ts.map +1 -1
  254. package/out/zql/src/ivm/filter.js.map +1 -1
  255. package/out/zql/src/ivm/flipped-join.d.ts.map +1 -1
  256. package/out/zql/src/ivm/flipped-join.js +49 -58
  257. package/out/zql/src/ivm/flipped-join.js.map +1 -1
  258. package/out/zql/src/ivm/join-utils.d.ts +2 -6
  259. package/out/zql/src/ivm/join-utils.d.ts.map +1 -1
  260. package/out/zql/src/ivm/join-utils.js +25 -25
  261. package/out/zql/src/ivm/join-utils.js.map +1 -1
  262. package/out/zql/src/ivm/join.d.ts.map +1 -1
  263. package/out/zql/src/ivm/join.js +32 -51
  264. package/out/zql/src/ivm/join.js.map +1 -1
  265. package/out/zql/src/ivm/maybe-split-and-push-edit-change.d.ts +1 -1
  266. package/out/zql/src/ivm/maybe-split-and-push-edit-change.d.ts.map +1 -1
  267. package/out/zql/src/ivm/maybe-split-and-push-edit-change.js +5 -10
  268. package/out/zql/src/ivm/maybe-split-and-push-edit-change.js.map +1 -1
  269. package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
  270. package/out/zql/src/ivm/memory-source.js +51 -59
  271. package/out/zql/src/ivm/memory-source.js.map +1 -1
  272. package/out/zql/src/ivm/push-accumulated.d.ts +3 -2
  273. package/out/zql/src/ivm/push-accumulated.d.ts.map +1 -1
  274. package/out/zql/src/ivm/push-accumulated.js +98 -122
  275. package/out/zql/src/ivm/push-accumulated.js.map +1 -1
  276. package/out/zql/src/ivm/skip.d.ts +1 -1
  277. package/out/zql/src/ivm/skip.d.ts.map +1 -1
  278. package/out/zql/src/ivm/skip.js +2 -2
  279. package/out/zql/src/ivm/skip.js.map +1 -1
  280. package/out/zql/src/ivm/source-change-index-enum.d.ts +7 -0
  281. package/out/zql/src/ivm/source-change-index-enum.d.ts.map +1 -0
  282. package/out/zql/src/ivm/source-change-index.d.ts +5 -0
  283. package/out/zql/src/ivm/source-change-index.d.ts.map +1 -0
  284. package/out/zql/src/ivm/source.d.ts +11 -13
  285. package/out/zql/src/ivm/source.d.ts.map +1 -1
  286. package/out/zql/src/ivm/source.js +26 -0
  287. package/out/zql/src/ivm/source.js.map +1 -0
  288. package/out/zql/src/ivm/take.d.ts.map +1 -1
  289. package/out/zql/src/ivm/take.js +27 -50
  290. package/out/zql/src/ivm/take.js.map +1 -1
  291. package/out/zql/src/ivm/union-fan-in.d.ts +2 -1
  292. package/out/zql/src/ivm/union-fan-in.d.ts.map +1 -1
  293. package/out/zql/src/ivm/union-fan-in.js +3 -3
  294. package/out/zql/src/ivm/union-fan-in.js.map +1 -1
  295. package/out/zql/src/ivm/union-fan-out.d.ts.map +1 -1
  296. package/out/zql/src/ivm/union-fan-out.js +1 -1
  297. package/out/zql/src/ivm/union-fan-out.js.map +1 -1
  298. package/out/zql/src/planner/planner-debug.d.ts +2 -2
  299. package/out/zql/src/planner/planner-debug.d.ts.map +1 -1
  300. package/out/zql/src/planner/planner-debug.js.map +1 -1
  301. package/out/zql/src/planner/planner-graph.d.ts +1 -1
  302. package/out/zql/src/planner/planner-graph.d.ts.map +1 -1
  303. package/out/zql/src/planner/planner-graph.js.map +1 -1
  304. package/out/zqlite/src/internal/sql-inline.d.ts.map +1 -1
  305. package/out/zqlite/src/internal/sql-inline.js.map +1 -1
  306. package/out/zqlite/src/query-builder.d.ts.map +1 -1
  307. package/out/zqlite/src/query-builder.js.map +1 -1
  308. package/out/zqlite/src/table-source.d.ts.map +1 -1
  309. package/out/zqlite/src/table-source.js +11 -11
  310. package/out/zqlite/src/table-source.js.map +1 -1
  311. package/package.json +99 -93
@@ -5,7 +5,7 @@ export declare const ddlEventSchema: v.ObjectType<Omit<{
5
5
  context: v.ObjectType<{
6
6
  query: v.Type<string>;
7
7
  }, v.Type<string>>;
8
- }, "schema" | "version"> & {
8
+ }, "schema" | "event" | "version"> & {
9
9
  version: v.Type<1>;
10
10
  schema: v.Type<{
11
11
  indexes: {
@@ -41,12 +41,15 @@ export declare const ddlEventSchema: v.ObjectType<Omit<{
41
41
  }>;
42
42
  }[];
43
43
  }>;
44
+ event: v.ObjectType<{
45
+ tag: v.Type<string>;
46
+ }, undefined>;
44
47
  }, undefined>;
45
48
  export declare const ddlStartEventSchema: v.ObjectType<Omit<Omit<{
46
49
  context: v.ObjectType<{
47
50
  query: v.Type<string>;
48
51
  }, v.Type<string>>;
49
- }, "schema" | "version"> & {
52
+ }, "schema" | "event" | "version"> & {
50
53
  version: v.Type<1>;
51
54
  schema: v.Type<{
52
55
  indexes: {
@@ -82,6 +85,9 @@ export declare const ddlStartEventSchema: v.ObjectType<Omit<Omit<{
82
85
  }>;
83
86
  }[];
84
87
  }>;
88
+ event: v.ObjectType<{
89
+ tag: v.Type<string>;
90
+ }, undefined>;
85
91
  }, "type"> & {
86
92
  type: v.Type<"ddlStart">;
87
93
  }, undefined>;
@@ -102,7 +108,7 @@ export declare const ddlUpdateEventSchema: v.ObjectType<Omit<Omit<{
102
108
  context: v.ObjectType<{
103
109
  query: v.Type<string>;
104
110
  }, v.Type<string>>;
105
- }, "schema" | "version"> & {
111
+ }, "schema" | "event" | "version"> & {
106
112
  version: v.Type<1>;
107
113
  schema: v.Type<{
108
114
  indexes: {
@@ -138,11 +144,11 @@ export declare const ddlUpdateEventSchema: v.ObjectType<Omit<Omit<{
138
144
  }>;
139
145
  }[];
140
146
  }>;
141
- }, "type" | "event"> & {
142
- type: v.Type<"ddlUpdate">;
143
147
  event: v.ObjectType<{
144
148
  tag: v.Type<string>;
145
149
  }, undefined>;
150
+ }, "type"> & {
151
+ type: v.Type<"ddlUpdate">;
146
152
  }, undefined>;
147
153
  export type DdlUpdateEvent = v.Infer<typeof ddlUpdateEventSchema>;
148
154
  /**
@@ -191,7 +197,7 @@ export declare const schemaSnapshotEventSchema: v.ObjectType<Omit<Omit<{
191
197
  context: v.ObjectType<{
192
198
  query: v.Type<string>;
193
199
  }, v.Type<string>>;
194
- }, "schema" | "version"> & {
200
+ }, "schema" | "event" | "version"> & {
195
201
  version: v.Type<1>;
196
202
  schema: v.Type<{
197
203
  indexes: {
@@ -227,18 +233,18 @@ export declare const schemaSnapshotEventSchema: v.ObjectType<Omit<Omit<{
227
233
  }>;
228
234
  }[];
229
235
  }>;
230
- }, "type" | "event"> & {
231
- type: v.Type<"schemaSnapshot">;
232
236
  event: v.ObjectType<{
233
237
  tag: v.Type<string>;
234
238
  }, undefined>;
239
+ }, "type"> & {
240
+ type: v.Type<"schemaSnapshot">;
235
241
  }, undefined>;
236
242
  export type SchemaSnapshotEvent = v.Infer<typeof schemaSnapshotEventSchema>;
237
243
  export declare const replicationEventSchema: v.UnionType<[v.ObjectType<Omit<Omit<{
238
244
  context: v.ObjectType<{
239
245
  query: v.Type<string>;
240
246
  }, v.Type<string>>;
241
- }, "schema" | "version"> & {
247
+ }, "schema" | "event" | "version"> & {
242
248
  version: v.Type<1>;
243
249
  schema: v.Type<{
244
250
  indexes: {
@@ -274,13 +280,16 @@ export declare const replicationEventSchema: v.UnionType<[v.ObjectType<Omit<Omit
274
280
  }>;
275
281
  }[];
276
282
  }>;
283
+ event: v.ObjectType<{
284
+ tag: v.Type<string>;
285
+ }, undefined>;
277
286
  }, "type"> & {
278
287
  type: v.Type<"ddlStart">;
279
288
  }, undefined>, v.ObjectType<Omit<Omit<{
280
289
  context: v.ObjectType<{
281
290
  query: v.Type<string>;
282
291
  }, v.Type<string>>;
283
- }, "schema" | "version"> & {
292
+ }, "schema" | "event" | "version"> & {
284
293
  version: v.Type<1>;
285
294
  schema: v.Type<{
286
295
  indexes: {
@@ -316,16 +325,16 @@ export declare const replicationEventSchema: v.UnionType<[v.ObjectType<Omit<Omit
316
325
  }>;
317
326
  }[];
318
327
  }>;
319
- }, "type" | "event"> & {
320
- type: v.Type<"ddlUpdate">;
321
328
  event: v.ObjectType<{
322
329
  tag: v.Type<string>;
323
330
  }, undefined>;
331
+ }, "type"> & {
332
+ type: v.Type<"ddlUpdate">;
324
333
  }, undefined>, v.ObjectType<Omit<Omit<{
325
334
  context: v.ObjectType<{
326
335
  query: v.Type<string>;
327
336
  }, v.Type<string>>;
328
- }, "schema" | "version"> & {
337
+ }, "schema" | "event" | "version"> & {
329
338
  version: v.Type<1>;
330
339
  schema: v.Type<{
331
340
  indexes: {
@@ -361,11 +370,11 @@ export declare const replicationEventSchema: v.UnionType<[v.ObjectType<Omit<Omit
361
370
  }>;
362
371
  }[];
363
372
  }>;
364
- }, "type" | "event"> & {
365
- type: v.Type<"schemaSnapshot">;
366
373
  event: v.ObjectType<{
367
374
  tag: v.Type<string>;
368
375
  }, undefined>;
376
+ }, "type"> & {
377
+ type: v.Type<"schemaSnapshot">;
369
378
  }, undefined>]>;
370
379
  export type ReplicationEvent = v.Infer<typeof replicationEventSchema>;
371
380
  export declare const TAGS: readonly ["CREATE TABLE", "ALTER TABLE", "CREATE INDEX", "DROP TABLE", "DROP INDEX", "ALTER PUBLICATION", "ALTER SCHEMA"];
@@ -1 +1 @@
1
- {"version":3,"file":"ddl.d.ts","sourceRoot":"","sources":["../../../../../../../../zero-cache/src/services/change-source/pg/schema/ddl.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,CAAC,MAAM,wCAAwC,CAAC;AAC5D,OAAO,EAAiB,KAAK,WAAW,EAAC,MAAM,6BAA6B,CAAC;AAc7E,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAQlC,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAGzB,CAAC;AASH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAE9B,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAG/B,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAGpC,CAAC;AAEH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;eAIlC,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAuMtE,eAAO,MAAM,IAAI,2HAQP,CAAC;AAEX,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,WAAW,UA0C9D;AAGD,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,GAAG,MAAM,UAgBzB"}
1
+ {"version":3,"file":"ddl.d.ts","sourceRoot":"","sources":["../../../../../../../../zero-cache/src/services/change-source/pg/schema/ddl.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,CAAC,MAAM,wCAAwC,CAAC;AAC5D,OAAO,EAAiB,KAAK,WAAW,EAAC,MAAM,6BAA6B,CAAC;AAU7E,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAQlC,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAIzB,CAAC;AASH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAE9B,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAE/B,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAEpC,CAAC;AAEH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;eAIlC,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAsMtE,eAAO,MAAM,IAAI,2HAQP,CAAC;AAEX,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,WAAW,UAmC9D;AAGD,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,GAAG,MAAM,UAMzB"}
@@ -1,12 +1,13 @@
1
1
  import { assert } from "../../../../../../shared/src/asserts.js";
2
2
  import { valita_exports } from "../../../../../../shared/src/valita.js";
3
3
  import { upstreamSchema } from "../../../../types/shards.js";
4
- import { indexDefinitionsQuery, publishedSchema, publishedTableQuery } from "./published.js";
5
4
  import { id } from "../../../../types/sql.js";
5
+ import { publishedSchema, publishedSchemaQuery } from "./published.js";
6
6
  import { literal } from "pg-format";
7
7
  var ddlEventSchema = valita_exports.object({ context: valita_exports.object({ query: valita_exports.string() }).rest(valita_exports.string()) }).extend({
8
8
  version: valita_exports.literal(1),
9
- schema: publishedSchema
9
+ schema: publishedSchema,
10
+ event: valita_exports.object({ tag: valita_exports.string() })
10
11
  });
11
12
  var ddlStartEventSchema = ddlEventSchema.extend({ type: valita_exports.literal("ddlStart") });
12
13
  /**
@@ -21,10 +22,7 @@ var ddlStartEventSchema = ddlEventSchema.extend({ type: valita_exports.literal("
21
22
  * schema (e.g. column constraints) are not relevant to downstream
22
23
  * replication.
23
24
  */
24
- var ddlUpdateEventSchema = ddlEventSchema.extend({
25
- type: valita_exports.literal("ddlUpdate"),
26
- event: valita_exports.object({ tag: valita_exports.string() })
27
- });
25
+ var ddlUpdateEventSchema = ddlEventSchema.extend({ type: valita_exports.literal("ddlUpdate") });
28
26
  /**
29
27
  * The `schemaSnapshot` message is a snapshot of a schema taken in response to
30
28
  * a `COMMENT ON PUBLICATION` command, which is a hook recognized by zero
@@ -67,14 +65,12 @@ var ddlUpdateEventSchema = ddlEventSchema.extend({
67
65
  * difference between the `ddlUpdate` and the second `schemaSnapshot`, and
68
66
  * thus the extra `COMMENT` statements will effectively be no-ops.
69
67
  */
70
- var schemaSnapshotEventSchema = ddlEventSchema.extend({
71
- type: valita_exports.literal("schemaSnapshot"),
72
- event: valita_exports.object({ tag: valita_exports.string() })
73
- });
68
+ var schemaSnapshotEventSchema = ddlEventSchema.extend({ type: valita_exports.literal("schemaSnapshot") });
74
69
  var replicationEventSchema = valita_exports.union(ddlStartEventSchema, ddlUpdateEventSchema, schemaSnapshotEventSchema);
75
70
  function append(shardNum) {
76
71
  return (name) => id(name + "_" + String(shardNum));
77
72
  }
73
+ var DDL_SERIALIZATION_LOCK = 4353719051050729648n;
78
74
  /**
79
75
  * Event trigger functions contain the core logic that are invoked by triggers.
80
76
  *
@@ -118,19 +114,11 @@ $$ LANGUAGE plpgsql;
118
114
 
119
115
 
120
116
  CREATE OR REPLACE FUNCTION ${schema}.schema_specs()
121
- RETURNS TEXT AS $$
122
- DECLARE
123
- tables record;
124
- indexes record;
125
- BEGIN
126
- ${publishedTableQuery(publications)} INTO tables;
127
- ${indexDefinitionsQuery(publications)} INTO indexes;
128
- RETURN json_build_object(
129
- 'tables', tables.tables,
130
- 'indexes', indexes.indexes
131
- );
132
- END
133
- $$ LANGUAGE plpgsql;
117
+ RETURNS TEXT
118
+ STABLE
119
+ AS $$
120
+ ${publishedSchemaQuery(publications)}
121
+ $$ LANGUAGE sql;
134
122
 
135
123
 
136
124
  CREATE OR REPLACE FUNCTION ${schema}.emit_ddl_start()
@@ -139,12 +127,16 @@ DECLARE
139
127
  schema_specs TEXT;
140
128
  message TEXT;
141
129
  BEGIN
130
+ -- serialize DDL statements to compute correct schema change diffs
131
+ PERFORM pg_advisory_xact_lock(${DDL_SERIALIZATION_LOCK});
132
+
142
133
  SELECT ${schema}.schema_specs() INTO schema_specs;
143
134
 
144
135
  SELECT json_build_object(
145
136
  'type', 'ddlStart',
146
137
  'version', 1,
147
138
  'schema', schema_specs::json,
139
+ 'event', json_build_object('tag', TG_TAG),
148
140
  'context', ${schema}.get_trigger_context()
149
141
  ) INTO message;
150
142
 
@@ -152,9 +144,11 @@ BEGIN
152
144
  END
153
145
  $$ LANGUAGE plpgsql;
154
146
 
147
+ -- Delete legacy function (and dependent legacy triggers).
148
+ DROP FUNCTION IF EXISTS ${schema}.emit_ddl_end(text) CASCADE;
155
149
 
156
- CREATE OR REPLACE FUNCTION ${schema}.emit_ddl_end(tag TEXT)
157
- RETURNS void AS $$
150
+ CREATE OR REPLACE FUNCTION ${schema}.emit_ddl_end()
151
+ RETURNS event_trigger AS $$
158
152
  DECLARE
159
153
  publications TEXT[];
160
154
  target RECORD;
@@ -178,7 +172,7 @@ BEGIN
178
172
  -- tables, and there is no way to determine if the table "used to be" in the
179
173
  -- set. Thus, all ALTER TABLE statements must produce a ddl update, similar to
180
174
  -- any DROP * statement.
181
- IF (target.object_type = 'table' AND tag != 'ALTER TABLE')
175
+ IF (target.object_type = 'table' AND TG_TAG != 'ALTER TABLE')
182
176
  OR target.object_type = 'table column' THEN
183
177
  SELECT ns.nspname AS "schema", c.relname AS "name" FROM pg_class AS c
184
178
  JOIN pg_namespace AS ns ON c.relnamespace = ns.oid
@@ -218,19 +212,19 @@ BEGIN
218
212
  INTO relevant;
219
213
 
220
214
  -- no-op CREATE IF NOT EXIST statements
221
- ELSIF tag LIKE 'CREATE %' AND target.object_type IS NULL THEN
215
+ ELSIF TG_TAG LIKE 'CREATE %' AND target.object_type IS NULL THEN
222
216
  relevant := NULL;
223
217
  END IF;
224
218
 
225
219
  IF relevant IS NULL THEN
226
- PERFORM ${schema}.notice_ignore(tag, target);
220
+ PERFORM ${schema}.notice_ignore(TG_TAG, target);
227
221
  RETURN;
228
222
  END IF;
229
223
 
230
- IF tag = 'COMMENT' THEN
224
+ IF TG_TAG = 'COMMENT' THEN
231
225
  -- Only make schemaSnapshots for COMMENT ON PUBLICATION
232
226
  IF target.object_type != 'publication' THEN
233
- PERFORM ${schema}.notice_ignore(tag, target);
227
+ PERFORM ${schema}.notice_ignore(TG_TAG, target);
234
228
  RETURN;
235
229
  END IF;
236
230
  event_type := 'schemaSnapshot';
@@ -240,18 +234,15 @@ BEGIN
240
234
  event_prefix := ''; -- TODO: Use '/ddl' for both when rollback safe
241
235
  END IF;
242
236
 
243
- RAISE INFO 'Creating % for % %', event_type, tag, row_to_json(target);
237
+ RAISE INFO 'Creating % for % %', event_type, TG_TAG, row_to_json(target);
244
238
 
245
- -- Construct and emit the DdlUpdateEvent message.
246
- SELECT json_build_object('tag', tag) INTO event;
247
-
248
239
  SELECT ${schema}.schema_specs() INTO schema_specs;
249
240
 
250
241
  SELECT json_build_object(
251
242
  'type', event_type,
252
243
  'version', 1,
253
244
  'schema', schema_specs::json,
254
- 'event', event::json,
245
+ 'event', json_build_object('tag', TG_TAG),
255
246
  'context', ${schema}.get_trigger_context()
256
247
  ) INTO message;
257
248
 
@@ -280,37 +271,23 @@ CREATE EVENT TRIGGER ${sharded(`${appID}_ddl_start`)}
280
271
  ON ddl_command_start
281
272
  WHEN TAG IN (${literal(TAGS)})
282
273
  EXECUTE PROCEDURE ${schema}.emit_ddl_start();
283
- `);
284
- for (const tag of [...TAGS, "COMMENT"]) {
285
- const tagID = tag.toLowerCase().replace(" ", "_");
286
- triggers.push(`
287
- CREATE OR REPLACE FUNCTION ${schema}.emit_${tagID}()
288
- RETURNS event_trigger AS $$
289
- BEGIN
290
- PERFORM ${schema}.emit_ddl_end(${literal(tag)});
291
- END
292
- $$ LANGUAGE plpgsql;
293
274
 
294
- CREATE EVENT TRIGGER ${sharded(`${appID}_${tagID}`)}
275
+ CREATE EVENT TRIGGER ${sharded(`${appID}_ddl_end`)}
295
276
  ON ddl_command_end
296
- WHEN TAG IN (${literal(tag)})
297
- EXECUTE PROCEDURE ${schema}.emit_${tagID}();
277
+ WHEN TAG IN (${literal([...TAGS, "COMMENT"])})
278
+ EXECUTE PROCEDURE ${schema}.emit_ddl_end();
298
279
  `);
280
+ for (const tag of [...TAGS, "COMMENT"]) {
281
+ const tagID = tag.toLowerCase().replace(" ", "_");
282
+ triggers.push(`DROP FUNCTION IF EXISTS ${schema}.emit_${tagID}() CASCADE;`);
299
283
  }
300
284
  return triggers.join("");
301
285
  }
302
286
  function dropEventTriggerStatements(appID, shardID) {
303
- const stmts = [];
304
- stmts.push(`
287
+ return `
305
288
  DROP EVENT TRIGGER IF EXISTS ${id(`${appID}_ddl_start_${shardID}`)};
306
- `);
307
- for (const tag of [...TAGS, "COMMENT"]) {
308
- const tagID = tag.toLowerCase().replace(" ", "_");
309
- stmts.push(`
310
- DROP EVENT TRIGGER IF EXISTS ${id(`${appID}_${tagID}_${shardID}`)};
311
- `);
312
- }
313
- return stmts.join("");
289
+ DROP EVENT TRIGGER IF EXISTS ${id(`${appID}_ddl_end_${shardID}`)};
290
+ `;
314
291
  }
315
292
  //#endregion
316
293
  export { createEventTriggerStatements, replicationEventSchema };
@@ -1 +1 @@
1
- {"version":3,"file":"ddl.js","names":[],"sources":["../../../../../../../../zero-cache/src/services/change-source/pg/schema/ddl.ts"],"sourcesContent":["import {literal as lit} from 'pg-format';\nimport {assert} from '../../../../../../shared/src/asserts.ts';\nimport * as v from '../../../../../../shared/src/valita.ts';\nimport {upstreamSchema, type ShardConfig} from '../../../../types/shards.ts';\nimport {id} from '../../../../types/sql.ts';\nimport {\n indexDefinitionsQuery,\n publishedSchema,\n publishedTableQuery,\n} from './published.ts';\n\n// Sent in the 'version' tag of \"ddlStart\" and \"ddlUpdate\" event messages.\n// This is used to ensure that the message constructed in the upstream\n// Trigger function is compatible with the code processing it in the zero-cache.\n//\n// Increment this when changing the format of the contents of the \"ddl\" events.\n// This will allow old / incompatible code to detect the change and abort.\nexport const PROTOCOL_VERSION = 1;\n\nconst triggerEvent = v.object({\n context: v.object({query: v.string()}).rest(v.string()),\n});\n\n// All DDL events contain a snapshot of the current tables and indexes that\n// are published / relevant to the shard.\nexport const ddlEventSchema = triggerEvent.extend({\n version: v.literal(PROTOCOL_VERSION),\n schema: publishedSchema,\n});\n\n// The `ddlStart` message is computed before every DDL event, regardless of\n// whether the subsequent event affects the shard. Downstream processing should\n// capture the contained schema information in order to determine the schema\n// changes necessary to apply a subsequent `ddlUpdate` message. Note that a\n// `ddlUpdate` message may not follow, as updates determined to be irrelevant\n// to the shard will not result in a message. However, all `ddlUpdate` messages\n// are guaranteed to be preceded by a `ddlStart` message.\nexport const ddlStartEventSchema = ddlEventSchema.extend({\n type: v.literal('ddlStart'),\n});\n\nexport type DdlStartEvent = v.Infer<typeof ddlStartEventSchema>;\n\n/**\n * The {@link DdlUpdateEvent} contains an updated schema resulting from\n * a particular ddl event. The event type provides information\n * (i.e. constraints) on the difference from the schema of the preceding\n * {@link DdlStartEvent}.\n *\n * Note that in almost all cases (the exception being `CREATE` events),\n * it is possible that there is no relevant difference between the\n * ddl-start schema and the ddl-update schema, as many aspects of the\n * schema (e.g. column constraints) are not relevant to downstream\n * replication.\n */\nexport const ddlUpdateEventSchema = ddlEventSchema.extend({\n type: v.literal('ddlUpdate'),\n event: v.object({tag: v.string()}),\n});\n\nexport type DdlUpdateEvent = v.Infer<typeof ddlUpdateEventSchema>;\n\n/**\n * The `schemaSnapshot` message is a snapshot of a schema taken in response to\n * a `COMMENT ON PUBLICATION` command, which is a hook recognized by zero\n * to manually emit schema snapshots to support detection of schema changes\n * from `ALTER PUBLICATION` commands on supabase, which does not fire event\n * triggers for them (https://github.com/supabase/supautils/issues/123).\n *\n * The hook is exercised by bookmarking the publication change with\n * `COMMENT ON PUBLICATION` statements within e.g.\n *\n * ```sql\n * BEGIN;\n * COMMENT ON PUBLICATION my_publication IS 'whatever';\n * ALTER PUBLICATION my_publication ...;\n * COMMENT ON PUBLICATION my_publication IS 'whatever';\n * COMMIT;\n * ```\n *\n * The `change-source` will perform the diff between a `schemaSnapshot`\n * events and its preceding `schemaSnapshot` (or `ddlUpdate`) within the\n * transaction.\n *\n * In the case where event trigger support is missing, this results in\n * diffing the `schemaSnapshot`s before and after the `ALTER PUBLICATION`\n * statement, thus effecting the same logic that would have been exercised\n * between the `ddlStart` and `ddlEvent` events fired by a database with\n * fully functional event triggers.\n *\n * Note that if the same transaction is run on a database that *does*\n * support event triggers on `ALTER PUBLICATION` statements, the sequence\n * of emitted messages will be:\n *\n * * `schemaSnapshot`\n * * `ddlStart`\n * * `ddlUpdate`\n * * `schemaSnapshot`\n *\n * Since `schemaSnapshot` messages are diffed with the preceding\n * `schemaSnapshot` or `ddlUpdate` event (if any), there will be no schema\n * difference between the `ddlUpdate` and the second `schemaSnapshot`, and\n * thus the extra `COMMENT` statements will effectively be no-ops.\n */\nexport const schemaSnapshotEventSchema = ddlEventSchema.extend({\n type: v.literal('schemaSnapshot'),\n event: v.object({tag: v.string()}),\n});\n\nexport type SchemaSnapshotEvent = v.Infer<typeof schemaSnapshotEventSchema>;\n\nexport const replicationEventSchema = v.union(\n ddlStartEventSchema,\n ddlUpdateEventSchema,\n schemaSnapshotEventSchema,\n);\n\nexport type ReplicationEvent = v.Infer<typeof replicationEventSchema>;\n\n// Creates a function that appends `_{shard-num}` to the input and\n// quotes the result to be a valid identifier.\nfunction append(shardNum: number) {\n return (name: string) => id(name + '_' + String(shardNum));\n}\n\n/**\n * Event trigger functions contain the core logic that are invoked by triggers.\n *\n * Note that although many of these functions can theoretically be parameterized and\n * shared across shards, it is advantageous to keep the functions in each shard\n * isolated from each other in order to avoid the complexity of shared-function\n * versioning.\n *\n * In a sense, shards (and their triggers and functions) should be thought of as\n * execution environments that can be updated at different schedules. If per-shard\n * triggers called into shared functions, we would have to consider versioning the\n * functions when changing their behavior, backwards compatibility, removal of\n * unused versions, etc. (not unlike versioning of npm packages).\n *\n * Instead, we opt for the simplicity and isolation of having each shard\n * completely own (and maintain) the entirety of its trigger/function stack.\n */\nfunction createEventFunctionStatements(shard: ShardConfig) {\n const {appID, shardNum, publications} = shard;\n const schema = id(upstreamSchema(shard)); // e.g. \"{APP_ID}_{SHARD_ID}\"\n return /*sql*/ `\nCREATE SCHEMA IF NOT EXISTS ${schema};\n\nCREATE OR REPLACE FUNCTION ${schema}.get_trigger_context()\nRETURNS record AS $$\nDECLARE\n result record;\nBEGIN\n SELECT current_query() AS \"query\" into result;\n RETURN result;\nEND\n$$ LANGUAGE plpgsql;\n\n\nCREATE OR REPLACE FUNCTION ${schema}.notice_ignore(tag TEXT, target record)\nRETURNS void AS $$\nBEGIN\n RAISE NOTICE 'zero(%) ignoring % %', ${lit(shardNum)}, tag, row_to_json(target);\nEND\n$$ LANGUAGE plpgsql;\n\n\nCREATE OR REPLACE FUNCTION ${schema}.schema_specs()\nRETURNS TEXT AS $$\nDECLARE\n tables record;\n indexes record;\nBEGIN\n ${publishedTableQuery(publications)} INTO tables;\n ${indexDefinitionsQuery(publications)} INTO indexes;\n RETURN json_build_object(\n 'tables', tables.tables,\n 'indexes', indexes.indexes\n );\nEND\n$$ LANGUAGE plpgsql;\n\n\nCREATE OR REPLACE FUNCTION ${schema}.emit_ddl_start()\nRETURNS event_trigger AS $$\nDECLARE\n schema_specs TEXT;\n message TEXT;\nBEGIN\n SELECT ${schema}.schema_specs() INTO schema_specs;\n\n SELECT json_build_object(\n 'type', 'ddlStart',\n 'version', ${PROTOCOL_VERSION},\n 'schema', schema_specs::json,\n 'context', ${schema}.get_trigger_context()\n ) INTO message;\n\n PERFORM pg_logical_emit_message(true, ${lit(\n `${appID}/${shardNum}`,\n )}, message);\nEND\n$$ LANGUAGE plpgsql;\n\n\nCREATE OR REPLACE FUNCTION ${schema}.emit_ddl_end(tag TEXT)\nRETURNS void AS $$\nDECLARE\n publications TEXT[];\n target RECORD;\n relevant RECORD;\n schema_specs TEXT;\n message TEXT;\n event TEXT;\n event_type TEXT;\n event_prefix TEXT;\nBEGIN\n publications := ARRAY[${lit(publications)}];\n\n SELECT objid, object_type, object_identity \n FROM pg_event_trigger_ddl_commands() \n LIMIT 1 INTO target;\n\n -- Filter DDL updates that are not relevant to the shard (i.e. publications) when possible.\n SELECT true INTO relevant;\n\n -- Note: ALTER TABLE statements may *remove* the table from the set of published\n -- tables, and there is no way to determine if the table \"used to be\" in the\n -- set. Thus, all ALTER TABLE statements must produce a ddl update, similar to\n -- any DROP * statement.\n IF (target.object_type = 'table' AND tag != 'ALTER TABLE') \n OR target.object_type = 'table column' THEN\n SELECT ns.nspname AS \"schema\", c.relname AS \"name\" FROM pg_class AS c\n JOIN pg_namespace AS ns ON c.relnamespace = ns.oid\n JOIN pg_publication_tables AS pb ON pb.schemaname = ns.nspname AND pb.tablename = c.relname\n WHERE c.oid = target.objid AND pb.pubname = ANY (publications)\n INTO relevant;\n\n ELSIF target.object_type = 'index' THEN\n SELECT ns.nspname AS \"schema\", c.relname AS \"name\" FROM pg_class AS c\n JOIN pg_namespace AS ns ON c.relnamespace = ns.oid\n JOIN pg_indexes as ind ON ind.schemaname = ns.nspname AND ind.indexname = c.relname\n JOIN pg_publication_tables AS pb ON pb.schemaname = ns.nspname AND pb.tablename = ind.tablename\n WHERE c.oid = target.objid AND pb.pubname = ANY (publications)\n INTO relevant;\n\n ELSIF target.object_type = 'publication relation' THEN\n SELECT pb.pubname FROM pg_publication_rel AS rel\n JOIN pg_publication AS pb ON pb.oid = rel.prpubid\n WHERE rel.oid = target.objid AND pb.pubname = ANY (publications) \n INTO relevant;\n\n ELSIF target.object_type = 'publication namespace' THEN\n SELECT pb.pubname FROM pg_publication_namespace AS ns\n JOIN pg_publication AS pb ON pb.oid = ns.pnpubid\n WHERE ns.oid = target.objid AND pb.pubname = ANY (publications) \n INTO relevant;\n\n ELSIF target.object_type = 'schema' THEN\n SELECT ns.nspname AS \"schema\", c.relname AS \"name\" FROM pg_class AS c\n JOIN pg_namespace AS ns ON c.relnamespace = ns.oid\n JOIN pg_publication_tables AS pb ON pb.schemaname = ns.nspname AND pb.tablename = c.relname\n WHERE ns.oid = target.objid AND pb.pubname = ANY (publications)\n INTO relevant;\n\n ELSIF target.object_type = 'publication' THEN\n SELECT 1 WHERE target.object_identity = ANY (publications)\n INTO relevant;\n\n -- no-op CREATE IF NOT EXIST statements\n ELSIF tag LIKE 'CREATE %' AND target.object_type IS NULL THEN\n relevant := NULL;\n END IF;\n\n IF relevant IS NULL THEN\n PERFORM ${schema}.notice_ignore(tag, target);\n RETURN;\n END IF;\n\n IF tag = 'COMMENT' THEN\n -- Only make schemaSnapshots for COMMENT ON PUBLICATION\n IF target.object_type != 'publication' THEN\n PERFORM ${schema}.notice_ignore(tag, target);\n RETURN;\n END IF;\n event_type := 'schemaSnapshot';\n event_prefix := '/ddl';\n ELSE\n event_type := 'ddlUpdate';\n event_prefix := ''; -- TODO: Use '/ddl' for both when rollback safe\n END IF;\n\n RAISE INFO 'Creating % for % %', event_type, tag, row_to_json(target);\n\n -- Construct and emit the DdlUpdateEvent message.\n SELECT json_build_object('tag', tag) INTO event;\n \n SELECT ${schema}.schema_specs() INTO schema_specs;\n\n SELECT json_build_object(\n 'type', event_type,\n 'version', ${PROTOCOL_VERSION},\n 'schema', schema_specs::json,\n 'event', event::json,\n 'context', ${schema}.get_trigger_context()\n ) INTO message;\n\n PERFORM pg_logical_emit_message(true, ${lit(\n `${appID}/${shardNum}`,\n )} || event_prefix, message);\nEND\n$$ LANGUAGE plpgsql;\n`;\n}\n\n// Exported for testing.\nexport const TAGS = [\n 'CREATE TABLE',\n 'ALTER TABLE',\n 'CREATE INDEX',\n 'DROP TABLE',\n 'DROP INDEX',\n 'ALTER PUBLICATION',\n 'ALTER SCHEMA',\n] as const;\n\nexport function createEventTriggerStatements(shard: ShardConfig) {\n // Better to assert here than get a cryptic syntax error from Postgres.\n assert(shard.publications.length, `shard publications must be non-empty`);\n\n // Unlike functions, which are namespaced in shard-specific schemas,\n // EVENT TRIGGER names are in the global namespace and thus must include\n // the appID and shardNum.\n const {appID, shardNum} = shard;\n const sharded = append(shardNum);\n const schema = id(upstreamSchema(shard));\n\n const triggers = [\n dropEventTriggerStatements(shard.appID, shard.shardNum),\n createEventFunctionStatements(shard),\n ];\n\n // A single ddl_command_start trigger covering all relevant tags.\n triggers.push(/*sql*/ `\nCREATE EVENT TRIGGER ${sharded(`${appID}_ddl_start`)}\n ON ddl_command_start\n WHEN TAG IN (${lit(TAGS)})\n EXECUTE PROCEDURE ${schema}.emit_ddl_start();\n`);\n\n // A per-tag ddl_command_end trigger that dispatches to ${schema}.emit_ddl_end(tag)\n for (const tag of [...TAGS, 'COMMENT']) {\n const tagID = tag.toLowerCase().replace(' ', '_');\n triggers.push(/*sql*/ `\nCREATE OR REPLACE FUNCTION ${schema}.emit_${tagID}() \nRETURNS event_trigger AS $$\nBEGIN\n PERFORM ${schema}.emit_ddl_end(${lit(tag)});\nEND\n$$ LANGUAGE plpgsql;\n\nCREATE EVENT TRIGGER ${sharded(`${appID}_${tagID}`)}\n ON ddl_command_end\n WHEN TAG IN (${lit(tag)})\n EXECUTE PROCEDURE ${schema}.emit_${tagID}();\n`);\n }\n return triggers.join('');\n}\n\n// Exported for testing.\nexport function dropEventTriggerStatements(\n appID: string,\n shardID: string | number,\n) {\n const stmts: string[] = [];\n // A single ddl_command_start trigger covering all relevant tags.\n stmts.push(/*sql*/ `\n DROP EVENT TRIGGER IF EXISTS ${id(`${appID}_ddl_start_${shardID}`)};\n `);\n\n // A per-tag ddl_command_end trigger that dispatches to ${schema}.emit_ddl_end(tag)\n for (const tag of [...TAGS, 'COMMENT']) {\n const tagID = tag.toLowerCase().replace(' ', '_');\n stmts.push(/*sql*/ `\n DROP EVENT TRIGGER IF EXISTS ${id(`${appID}_${tagID}_${shardID}`)};\n `);\n }\n return stmts.join('');\n}\n"],"mappings":";;;;;;AAyBA,IAAa,iBANQ,eAAE,OAAO,EAC5B,SAAS,eAAE,OAAO,EAAC,OAAO,eAAE,QAAQ,EAAC,CAAC,CAAC,KAAK,eAAE,QAAQ,CAAC,EACxD,CAAC,CAIyC,OAAO;CAChD,SAAS,eAAE,QAAA,EAAyB;CACpC,QAAQ;CACT,CAAC;AASF,IAAa,sBAAsB,eAAe,OAAO,EACvD,MAAM,eAAE,QAAQ,WAAW,EAC5B,CAAC;;;;;;;;;;;;;AAgBF,IAAa,uBAAuB,eAAe,OAAO;CACxD,MAAM,eAAE,QAAQ,YAAY;CAC5B,OAAO,eAAE,OAAO,EAAC,KAAK,eAAE,QAAQ,EAAC,CAAC;CACnC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8CF,IAAa,4BAA4B,eAAe,OAAO;CAC7D,MAAM,eAAE,QAAQ,iBAAiB;CACjC,OAAO,eAAE,OAAO,EAAC,KAAK,eAAE,QAAQ,EAAC,CAAC;CACnC,CAAC;AAIF,IAAa,yBAAyB,eAAE,MACtC,qBACA,sBACA,0BACD;AAMD,SAAS,OAAO,UAAkB;AAChC,SAAQ,SAAiB,GAAG,OAAO,MAAM,OAAO,SAAS,CAAC;;;;;;;;;;;;;;;;;;;AAoB5D,SAAS,8BAA8B,OAAoB;CACzD,MAAM,EAAC,OAAO,UAAU,iBAAgB;CACxC,MAAM,SAAS,GAAG,eAAe,MAAM,CAAC;AACxC,QAAe;8BACa,OAAO;;6BAER,OAAO;;;;;;;;;;;6BAWP,OAAO;;;yCAGK,QAAI,SAAS,CAAC;;;;;6BAK1B,OAAO;;;;;;IAMhC,oBAAoB,aAAa,CAAC;IAClC,sBAAsB,aAAa,CAAC;;;;;;;;;6BASX,OAAO;;;;;;WAMzB,OAAO;;;;;;iBAMD,OAAO;;;0CAGkB,QACtC,GAAG,MAAM,GAAG,WACb,CAAC;;;;;6BAKyB,OAAO;;;;;;;;;;;;0BAYV,QAAI,aAAa,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA0D9B,OAAO;;;;;;;gBAOL,OAAO;;;;;;;;;;;;;;;WAeZ,OAAO;;;;;;;iBAOD,OAAO;;;0CAGkB,QACtC,GAAG,MAAM,GAAG,WACb,CAAC;;;;;AAOJ,IAAa,OAAO;CAClB;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAED,SAAgB,6BAA6B,OAAoB;AAE/D,QAAO,MAAM,aAAa,QAAQ,uCAAuC;CAKzE,MAAM,EAAC,OAAO,aAAY;CAC1B,MAAM,UAAU,OAAO,SAAS;CAChC,MAAM,SAAS,GAAG,eAAe,MAAM,CAAC;CAExC,MAAM,WAAW,CACf,2BAA2B,MAAM,OAAO,MAAM,SAAS,EACvD,8BAA8B,MAAM,CACrC;AAGD,UAAS,KAAa;uBACD,QAAQ,GAAG,MAAM,YAAY,CAAC;;iBAEpC,QAAI,KAAK,CAAC;sBACL,OAAO;EAC3B;AAGA,MAAK,MAAM,OAAO,CAAC,GAAG,MAAM,UAAU,EAAE;EACtC,MAAM,QAAQ,IAAI,aAAa,CAAC,QAAQ,KAAK,IAAI;AACjD,WAAS,KAAa;6BACG,OAAO,QAAQ,MAAM;;;YAGtC,OAAO,gBAAgB,QAAI,IAAI,CAAC;;;;uBAIrB,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;;iBAEnC,QAAI,IAAI,CAAC;sBACJ,OAAO,QAAQ,MAAM;EACzC;;AAEA,QAAO,SAAS,KAAK,GAAG;;AAI1B,SAAgB,2BACd,OACA,SACA;CACA,MAAM,QAAkB,EAAE;AAE1B,OAAM,KAAa;mCACc,GAAG,GAAG,MAAM,aAAa,UAAU,CAAC;IACnE;AAGF,MAAK,MAAM,OAAO,CAAC,GAAG,MAAM,UAAU,EAAE;EACtC,MAAM,QAAQ,IAAI,aAAa,CAAC,QAAQ,KAAK,IAAI;AACjD,QAAM,KAAa;qCACc,GAAG,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,CAAC;MAClE;;AAEJ,QAAO,MAAM,KAAK,GAAG"}
1
+ {"version":3,"file":"ddl.js","names":[],"sources":["../../../../../../../../zero-cache/src/services/change-source/pg/schema/ddl.ts"],"sourcesContent":["import {literal as lit} from 'pg-format';\nimport {assert} from '../../../../../../shared/src/asserts.ts';\nimport * as v from '../../../../../../shared/src/valita.ts';\nimport {upstreamSchema, type ShardConfig} from '../../../../types/shards.ts';\nimport {id} from '../../../../types/sql.ts';\nimport {publishedSchema, publishedSchemaQuery} from './published.ts';\n\n// Sent in the 'version' tag of \"ddlStart\" and \"ddlUpdate\" event messages.\n// This is used to ensure that the message constructed in the upstream\n// Trigger function is compatible with the code processing it in the zero-cache.\n//\n// Increment this when changing the format of the contents of the \"ddl\" events.\n// This will allow old / incompatible code to detect the change and abort.\nexport const PROTOCOL_VERSION = 1;\n\nconst triggerEvent = v.object({\n context: v.object({query: v.string()}).rest(v.string()),\n});\n\n// All DDL events contain a snapshot of the current tables and indexes that\n// are published / relevant to the shard.\nexport const ddlEventSchema = triggerEvent.extend({\n version: v.literal(PROTOCOL_VERSION),\n schema: publishedSchema,\n event: v.object({tag: v.string()}),\n});\n\n// The `ddlStart` message is computed before every DDL event, regardless of\n// whether the subsequent event affects the shard. Downstream processing should\n// capture the contained schema information in order to determine the schema\n// changes necessary to apply a subsequent `ddlUpdate` message. Note that a\n// `ddlUpdate` message may not follow, as updates determined to be irrelevant\n// to the shard will not result in a message. However, all `ddlUpdate` messages\n// are guaranteed to be preceded by a `ddlStart` message.\nexport const ddlStartEventSchema = ddlEventSchema.extend({\n type: v.literal('ddlStart'),\n});\n\nexport type DdlStartEvent = v.Infer<typeof ddlStartEventSchema>;\n\n/**\n * The {@link DdlUpdateEvent} contains an updated schema resulting from\n * a particular ddl event. The event type provides information\n * (i.e. constraints) on the difference from the schema of the preceding\n * {@link DdlStartEvent}.\n *\n * Note that in almost all cases (the exception being `CREATE` events),\n * it is possible that there is no relevant difference between the\n * ddl-start schema and the ddl-update schema, as many aspects of the\n * schema (e.g. column constraints) are not relevant to downstream\n * replication.\n */\nexport const ddlUpdateEventSchema = ddlEventSchema.extend({\n type: v.literal('ddlUpdate'),\n});\n\nexport type DdlUpdateEvent = v.Infer<typeof ddlUpdateEventSchema>;\n\n/**\n * The `schemaSnapshot` message is a snapshot of a schema taken in response to\n * a `COMMENT ON PUBLICATION` command, which is a hook recognized by zero\n * to manually emit schema snapshots to support detection of schema changes\n * from `ALTER PUBLICATION` commands on supabase, which does not fire event\n * triggers for them (https://github.com/supabase/supautils/issues/123).\n *\n * The hook is exercised by bookmarking the publication change with\n * `COMMENT ON PUBLICATION` statements within e.g.\n *\n * ```sql\n * BEGIN;\n * COMMENT ON PUBLICATION my_publication IS 'whatever';\n * ALTER PUBLICATION my_publication ...;\n * COMMENT ON PUBLICATION my_publication IS 'whatever';\n * COMMIT;\n * ```\n *\n * The `change-source` will perform the diff between a `schemaSnapshot`\n * events and its preceding `schemaSnapshot` (or `ddlUpdate`) within the\n * transaction.\n *\n * In the case where event trigger support is missing, this results in\n * diffing the `schemaSnapshot`s before and after the `ALTER PUBLICATION`\n * statement, thus effecting the same logic that would have been exercised\n * between the `ddlStart` and `ddlEvent` events fired by a database with\n * fully functional event triggers.\n *\n * Note that if the same transaction is run on a database that *does*\n * support event triggers on `ALTER PUBLICATION` statements, the sequence\n * of emitted messages will be:\n *\n * * `schemaSnapshot`\n * * `ddlStart`\n * * `ddlUpdate`\n * * `schemaSnapshot`\n *\n * Since `schemaSnapshot` messages are diffed with the preceding\n * `schemaSnapshot` or `ddlUpdate` event (if any), there will be no schema\n * difference between the `ddlUpdate` and the second `schemaSnapshot`, and\n * thus the extra `COMMENT` statements will effectively be no-ops.\n */\nexport const schemaSnapshotEventSchema = ddlEventSchema.extend({\n type: v.literal('schemaSnapshot'),\n});\n\nexport type SchemaSnapshotEvent = v.Infer<typeof schemaSnapshotEventSchema>;\n\nexport const replicationEventSchema = v.union(\n ddlStartEventSchema,\n ddlUpdateEventSchema,\n schemaSnapshotEventSchema,\n);\n\nexport type ReplicationEvent = v.Infer<typeof replicationEventSchema>;\n\n// Creates a function that appends `_{shard-num}` to the input and\n// quotes the result to be a valid identifier.\nfunction append(shardNum: number) {\n return (name: string) => id(name + '_' + String(shardNum));\n}\n\n// pg_advisory_xact_lock key for serializing ddl statements in order to\n// produce correct schema change diffs.\nconst DDL_SERIALIZATION_LOCK = 0x3c6b8468f1bac0b0n;\n\n/**\n * Event trigger functions contain the core logic that are invoked by triggers.\n *\n * Note that although many of these functions can theoretically be parameterized and\n * shared across shards, it is advantageous to keep the functions in each shard\n * isolated from each other in order to avoid the complexity of shared-function\n * versioning.\n *\n * In a sense, shards (and their triggers and functions) should be thought of as\n * execution environments that can be updated at different schedules. If per-shard\n * triggers called into shared functions, we would have to consider versioning the\n * functions when changing their behavior, backwards compatibility, removal of\n * unused versions, etc. (not unlike versioning of npm packages).\n *\n * Instead, we opt for the simplicity and isolation of having each shard\n * completely own (and maintain) the entirety of its trigger/function stack.\n */\nfunction createEventFunctionStatements(shard: ShardConfig) {\n const {appID, shardNum, publications} = shard;\n const schema = id(upstreamSchema(shard)); // e.g. \"{APP_ID}_{SHARD_ID}\"\n return /*sql*/ `\nCREATE SCHEMA IF NOT EXISTS ${schema};\n\nCREATE OR REPLACE FUNCTION ${schema}.get_trigger_context()\nRETURNS record AS $$\nDECLARE\n result record;\nBEGIN\n SELECT current_query() AS \"query\" into result;\n RETURN result;\nEND\n$$ LANGUAGE plpgsql;\n\n\nCREATE OR REPLACE FUNCTION ${schema}.notice_ignore(tag TEXT, target record)\nRETURNS void AS $$\nBEGIN\n RAISE NOTICE 'zero(%) ignoring % %', ${lit(shardNum)}, tag, row_to_json(target);\nEND\n$$ LANGUAGE plpgsql;\n\n\nCREATE OR REPLACE FUNCTION ${schema}.schema_specs()\nRETURNS TEXT \nSTABLE\nAS $$\n ${publishedSchemaQuery(publications)}\n$$ LANGUAGE sql;\n\n\nCREATE OR REPLACE FUNCTION ${schema}.emit_ddl_start()\nRETURNS event_trigger AS $$\nDECLARE\n schema_specs TEXT;\n message TEXT;\nBEGIN\n -- serialize DDL statements to compute correct schema change diffs\n PERFORM pg_advisory_xact_lock(${DDL_SERIALIZATION_LOCK});\n\n SELECT ${schema}.schema_specs() INTO schema_specs;\n\n SELECT json_build_object(\n 'type', 'ddlStart',\n 'version', ${PROTOCOL_VERSION},\n 'schema', schema_specs::json,\n 'event', json_build_object('tag', TG_TAG),\n 'context', ${schema}.get_trigger_context()\n ) INTO message;\n\n PERFORM pg_logical_emit_message(true, ${lit(\n `${appID}/${shardNum}`,\n )}, message);\nEND\n$$ LANGUAGE plpgsql;\n\n-- Delete legacy function (and dependent legacy triggers).\nDROP FUNCTION IF EXISTS ${schema}.emit_ddl_end(text) CASCADE;\n\nCREATE OR REPLACE FUNCTION ${schema}.emit_ddl_end()\nRETURNS event_trigger AS $$\nDECLARE\n publications TEXT[];\n target RECORD;\n relevant RECORD;\n schema_specs TEXT;\n message TEXT;\n event TEXT;\n event_type TEXT;\n event_prefix TEXT;\nBEGIN\n publications := ARRAY[${lit(publications)}];\n\n SELECT objid, object_type, object_identity \n FROM pg_event_trigger_ddl_commands() \n LIMIT 1 INTO target;\n\n -- Filter DDL updates that are not relevant to the shard (i.e. publications) when possible.\n SELECT true INTO relevant;\n\n -- Note: ALTER TABLE statements may *remove* the table from the set of published\n -- tables, and there is no way to determine if the table \"used to be\" in the\n -- set. Thus, all ALTER TABLE statements must produce a ddl update, similar to\n -- any DROP * statement.\n IF (target.object_type = 'table' AND TG_TAG != 'ALTER TABLE') \n OR target.object_type = 'table column' THEN\n SELECT ns.nspname AS \"schema\", c.relname AS \"name\" FROM pg_class AS c\n JOIN pg_namespace AS ns ON c.relnamespace = ns.oid\n JOIN pg_publication_tables AS pb ON pb.schemaname = ns.nspname AND pb.tablename = c.relname\n WHERE c.oid = target.objid AND pb.pubname = ANY (publications)\n INTO relevant;\n\n ELSIF target.object_type = 'index' THEN\n SELECT ns.nspname AS \"schema\", c.relname AS \"name\" FROM pg_class AS c\n JOIN pg_namespace AS ns ON c.relnamespace = ns.oid\n JOIN pg_indexes as ind ON ind.schemaname = ns.nspname AND ind.indexname = c.relname\n JOIN pg_publication_tables AS pb ON pb.schemaname = ns.nspname AND pb.tablename = ind.tablename\n WHERE c.oid = target.objid AND pb.pubname = ANY (publications)\n INTO relevant;\n\n ELSIF target.object_type = 'publication relation' THEN\n SELECT pb.pubname FROM pg_publication_rel AS rel\n JOIN pg_publication AS pb ON pb.oid = rel.prpubid\n WHERE rel.oid = target.objid AND pb.pubname = ANY (publications) \n INTO relevant;\n\n ELSIF target.object_type = 'publication namespace' THEN\n SELECT pb.pubname FROM pg_publication_namespace AS ns\n JOIN pg_publication AS pb ON pb.oid = ns.pnpubid\n WHERE ns.oid = target.objid AND pb.pubname = ANY (publications) \n INTO relevant;\n\n ELSIF target.object_type = 'schema' THEN\n SELECT ns.nspname AS \"schema\", c.relname AS \"name\" FROM pg_class AS c\n JOIN pg_namespace AS ns ON c.relnamespace = ns.oid\n JOIN pg_publication_tables AS pb ON pb.schemaname = ns.nspname AND pb.tablename = c.relname\n WHERE ns.oid = target.objid AND pb.pubname = ANY (publications)\n INTO relevant;\n\n ELSIF target.object_type = 'publication' THEN\n SELECT 1 WHERE target.object_identity = ANY (publications)\n INTO relevant;\n\n -- no-op CREATE IF NOT EXIST statements\n ELSIF TG_TAG LIKE 'CREATE %' AND target.object_type IS NULL THEN\n relevant := NULL;\n END IF;\n\n IF relevant IS NULL THEN\n PERFORM ${schema}.notice_ignore(TG_TAG, target);\n RETURN;\n END IF;\n\n IF TG_TAG = 'COMMENT' THEN\n -- Only make schemaSnapshots for COMMENT ON PUBLICATION\n IF target.object_type != 'publication' THEN\n PERFORM ${schema}.notice_ignore(TG_TAG, target);\n RETURN;\n END IF;\n event_type := 'schemaSnapshot';\n event_prefix := '/ddl';\n ELSE\n event_type := 'ddlUpdate';\n event_prefix := ''; -- TODO: Use '/ddl' for both when rollback safe\n END IF;\n\n RAISE INFO 'Creating % for % %', event_type, TG_TAG, row_to_json(target);\n\n SELECT ${schema}.schema_specs() INTO schema_specs;\n\n SELECT json_build_object(\n 'type', event_type,\n 'version', ${PROTOCOL_VERSION},\n 'schema', schema_specs::json,\n 'event', json_build_object('tag', TG_TAG),\n 'context', ${schema}.get_trigger_context()\n ) INTO message;\n\n PERFORM pg_logical_emit_message(true, ${lit(\n `${appID}/${shardNum}`,\n )} || event_prefix, message);\nEND\n$$ LANGUAGE plpgsql;\n`;\n}\n\n// Exported for testing.\nexport const TAGS = [\n 'CREATE TABLE',\n 'ALTER TABLE',\n 'CREATE INDEX',\n 'DROP TABLE',\n 'DROP INDEX',\n 'ALTER PUBLICATION',\n 'ALTER SCHEMA',\n] as const;\n\nexport function createEventTriggerStatements(shard: ShardConfig) {\n // Better to assert here than get a cryptic syntax error from Postgres.\n assert(shard.publications.length, `shard publications must be non-empty`);\n\n // Unlike functions, which are namespaced in shard-specific schemas,\n // EVENT TRIGGER names are in the global namespace and thus must include\n // the appID and shardNum.\n const {appID, shardNum} = shard;\n const sharded = append(shardNum);\n const schema = id(upstreamSchema(shard));\n\n const triggers = [\n dropEventTriggerStatements(shard.appID, shard.shardNum),\n createEventFunctionStatements(shard),\n ];\n\n // A single ddl_command_start trigger covering all relevant tags.\n triggers.push(/*sql*/ `\nCREATE EVENT TRIGGER ${sharded(`${appID}_ddl_start`)}\n ON ddl_command_start\n WHEN TAG IN (${lit(TAGS)})\n EXECUTE PROCEDURE ${schema}.emit_ddl_start();\n\nCREATE EVENT TRIGGER ${sharded(`${appID}_ddl_end`)}\n ON ddl_command_end\n WHEN TAG IN (${lit([...TAGS, 'COMMENT'])})\n EXECUTE PROCEDURE ${schema}.emit_ddl_end();\n`);\n\n // Drop legacy functions / triggers.\n for (const tag of [...TAGS, 'COMMENT']) {\n const tagID = tag.toLowerCase().replace(' ', '_');\n triggers.push(`DROP FUNCTION IF EXISTS ${schema}.emit_${tagID}() CASCADE;`);\n }\n return triggers.join('');\n}\n\n// Exported for testing.\nexport function dropEventTriggerStatements(\n appID: string,\n shardID: string | number,\n) {\n return /*sql*/ `\n DROP EVENT TRIGGER IF EXISTS ${id(`${appID}_ddl_start_${shardID}`)};\n DROP EVENT TRIGGER IF EXISTS ${id(`${appID}_ddl_end_${shardID}`)};\n `;\n}\n"],"mappings":";;;;;;AAqBA,IAAa,iBANQ,eAAE,OAAO,EAC5B,SAAS,eAAE,OAAO,EAAC,OAAO,eAAE,QAAQ,EAAC,CAAC,CAAC,KAAK,eAAE,QAAQ,CAAC,EACxD,CAAC,CAIyC,OAAO;CAChD,SAAS,eAAE,QAAA,EAAyB;CACpC,QAAQ;CACR,OAAO,eAAE,OAAO,EAAC,KAAK,eAAE,QAAQ,EAAC,CAAC;CACnC,CAAC;AASF,IAAa,sBAAsB,eAAe,OAAO,EACvD,MAAM,eAAE,QAAQ,WAAW,EAC5B,CAAC;;;;;;;;;;;;;AAgBF,IAAa,uBAAuB,eAAe,OAAO,EACxD,MAAM,eAAE,QAAQ,YAAY,EAC7B,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8CF,IAAa,4BAA4B,eAAe,OAAO,EAC7D,MAAM,eAAE,QAAQ,iBAAiB,EAClC,CAAC;AAIF,IAAa,yBAAyB,eAAE,MACtC,qBACA,sBACA,0BACD;AAMD,SAAS,OAAO,UAAkB;AAChC,SAAQ,SAAiB,GAAG,OAAO,MAAM,OAAO,SAAS,CAAC;;AAK5D,IAAM,yBAAyB;;;;;;;;;;;;;;;;;;AAmB/B,SAAS,8BAA8B,OAAoB;CACzD,MAAM,EAAC,OAAO,UAAU,iBAAgB;CACxC,MAAM,SAAS,GAAG,eAAe,MAAM,CAAC;AACxC,QAAe;8BACa,OAAO;;6BAER,OAAO;;;;;;;;;;;6BAWP,OAAO;;;yCAGK,QAAI,SAAS,CAAC;;;;;6BAK1B,OAAO;;;;IAIhC,qBAAqB,aAAa,CAAC;;;;6BAIV,OAAO;;;;;;;kCAOF,uBAAuB;;WAE9C,OAAO;;;;;;;iBAOD,OAAO;;;0CAGkB,QACtC,GAAG,MAAM,GAAG,WACb,CAAC;;;;;0BAKsB,OAAO;;6BAEJ,OAAO;;;;;;;;;;;;0BAYV,QAAI,aAAa,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA0D9B,OAAO;;;;;;;gBAOL,OAAO;;;;;;;;;;;;WAYZ,OAAO;;;;;;;iBAOD,OAAO;;;0CAGkB,QACtC,GAAG,MAAM,GAAG,WACb,CAAC;;;;;AAOJ,IAAa,OAAO;CAClB;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAED,SAAgB,6BAA6B,OAAoB;AAE/D,QAAO,MAAM,aAAa,QAAQ,uCAAuC;CAKzE,MAAM,EAAC,OAAO,aAAY;CAC1B,MAAM,UAAU,OAAO,SAAS;CAChC,MAAM,SAAS,GAAG,eAAe,MAAM,CAAC;CAExC,MAAM,WAAW,CACf,2BAA2B,MAAM,OAAO,MAAM,SAAS,EACvD,8BAA8B,MAAM,CACrC;AAGD,UAAS,KAAa;uBACD,QAAQ,GAAG,MAAM,YAAY,CAAC;;iBAEpC,QAAI,KAAK,CAAC;sBACL,OAAO;;uBAEN,QAAQ,GAAG,MAAM,UAAU,CAAC;;iBAElC,QAAI,CAAC,GAAG,MAAM,UAAU,CAAC,CAAC;sBACrB,OAAO;EAC3B;AAGA,MAAK,MAAM,OAAO,CAAC,GAAG,MAAM,UAAU,EAAE;EACtC,MAAM,QAAQ,IAAI,aAAa,CAAC,QAAQ,KAAK,IAAI;AACjD,WAAS,KAAK,2BAA2B,OAAO,QAAQ,MAAM,aAAa;;AAE7E,QAAO,SAAS,KAAK,GAAG;;AAI1B,SAAgB,2BACd,OACA,SACA;AACA,QAAe;mCACkB,GAAG,GAAG,MAAM,aAAa,UAAU,CAAC;mCACpC,GAAG,GAAG,MAAM,WAAW,UAAU,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../../../../../../zero-cache/src/services/change-source/pg/schema/init.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AASjD,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAiB,KAAK,WAAW,EAAC,MAAM,6BAA6B,CAAC;AAa7E;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,UAAU,EACd,EAAE,EAAE,UAAU,EACd,KAAK,EAAE,WAAW,GACjB,OAAO,CAAC,IAAI,CAAC,CAgBf;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,UAAU,EACd,EAAE,EAAE,UAAU,EACd,KAAK,EAAE,WAAW,EAClB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,IAAI,CAAC,CAqBf;AAsLD,eAAO,MAAM,sBAAsB,QAMwB,CAAC;AAE5D,wBAAsB,uBAAuB,CAC3C,EAAE,EAAE,UAAU,EACd,EAAE,EAAE,UAAU,EACd,KAAK,EAAE,WAAW,iBAYnB"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../../../../../../zero-cache/src/services/change-source/pg/schema/init.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AASjD,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAiB,KAAK,WAAW,EAAC,MAAM,6BAA6B,CAAC;AAa7E;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,UAAU,EACd,EAAE,EAAE,UAAU,EACd,KAAK,EAAE,WAAW,GACjB,OAAO,CAAC,IAAI,CAAC,CAgBf;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,UAAU,EACd,EAAE,EAAE,UAAU,EACd,KAAK,EAAE,WAAW,EAClB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,IAAI,CAAC,CAqBf;AA4LD,eAAO,MAAM,sBAAsB,QAMwB,CAAC;AAE5D,wBAAsB,uBAAuB,CAC3C,EAAE,EAAE,UAAU,EACd,EAAE,EAAE,UAAU,EACd,KAAK,EAAE,WAAW,iBAYnB"}
@@ -1,8 +1,8 @@
1
1
  import { assert } from "../../../../../../shared/src/asserts.js";
2
2
  import { parse, valita_exports } from "../../../../../../shared/src/valita.js";
3
3
  import { upstreamSchema } from "../../../../types/shards.js";
4
- import { publishedSchema } from "./published.js";
5
4
  import { id } from "../../../../types/sql.js";
5
+ import { publishedSchema } from "./published.js";
6
6
  import { getMutationsTableDefinition, legacyReplicationSlot, metadataPublicationName, setupTablesAndReplication, setupTriggers } from "./shard.js";
7
7
  import { decommissionShard } from "../decommission.js";
8
8
  import { AutoResetSignal } from "../../../change-streamer/schema/tables.js";
@@ -109,7 +109,7 @@ function getIncrementalMigrations(shard, replicaVersion) {
109
109
  ADD COLUMN "subscriberContext" JSON
110
110
  `;
111
111
  } },
112
- 18: { migrateSchema: async (lc, sql) => {
112
+ 20: { migrateSchema: async (lc, sql) => {
113
113
  const [{ publications }] = await sql`
114
114
  SELECT publications FROM ${sql(shardConfigTable)}`;
115
115
  await setupTriggers(lc, sql, {
@@ -1 +1 @@
1
- {"version":3,"file":"init.js","names":[],"sources":["../../../../../../../../zero-cache/src/services/change-source/pg/schema/init.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {assert} from '../../../../../../shared/src/asserts.ts';\nimport * as v from '../../../../../../shared/src/valita.ts';\nimport {\n getVersionHistory,\n runSchemaMigrations,\n type IncrementalMigrationMap,\n type Migration,\n} from '../../../../db/migration.ts';\nimport type {PostgresDB} from '../../../../types/pg.ts';\nimport {upstreamSchema, type ShardConfig} from '../../../../types/shards.ts';\nimport {id} from '../../../../types/sql.ts';\nimport {AutoResetSignal} from '../../../change-streamer/schema/tables.ts';\nimport {decommissionShard} from '../decommission.ts';\nimport {publishedSchema} from './published.ts';\nimport {\n getMutationsTableDefinition,\n legacyReplicationSlot,\n metadataPublicationName,\n setupTablesAndReplication,\n setupTriggers,\n} from './shard.ts';\n\n/**\n * Ensures that a shard is set up for initial sync.\n */\nexport async function ensureShardSchema(\n lc: LogContext,\n db: PostgresDB,\n shard: ShardConfig,\n): Promise<void> {\n const initialSetup: Migration = {\n migrateSchema: (lc, tx) => setupTablesAndReplication(lc, tx, shard),\n minSafeVersion: 1,\n };\n await runSchemaMigrations(\n lc,\n `upstream-shard-${shard.appID}`,\n upstreamSchema(shard),\n db,\n initialSetup,\n // The incremental migration of any existing replicas will be replaced by\n // the incoming replica being synced, so the replicaVersion here is\n // unnecessary.\n getIncrementalMigrations(shard, 'obsolete'),\n );\n}\n\n/**\n * Updates the schema for an existing shard.\n */\nexport async function updateShardSchema(\n lc: LogContext,\n db: PostgresDB,\n shard: ShardConfig,\n replicaVersion: string,\n): Promise<void> {\n await runSchemaMigrations(\n lc,\n `upstream-shard-${shard.appID}`,\n upstreamSchema(shard),\n db,\n {\n // If the expected existing shard is absent, throw an\n // AutoResetSignal to backtrack and initial sync.\n migrateSchema: () => {\n throw new AutoResetSignal(\n `upstream shard ${upstreamSchema(shard)} is not initialized`,\n );\n },\n },\n getIncrementalMigrations(shard, replicaVersion),\n );\n\n // The decommission check is run in updateShardSchema so that it happens\n // after initial sync, and not when the shard schema is initially set up.\n await decommissionLegacyShard(lc, db, shard);\n}\n\nfunction getIncrementalMigrations(\n shard: ShardConfig,\n replicaVersion?: string,\n): IncrementalMigrationMap {\n const shardConfigTable = `${upstreamSchema(shard)}.shardConfig`;\n\n return {\n 4: {\n migrateSchema: () => {\n throw new AutoResetSignal('resetting to upgrade shard schema');\n },\n minSafeVersion: 3,\n },\n\n // v5: changes the upstream schema organization from \"zero_{SHARD_ID}\" to\n // the \"{APP_ID}_0\". An incremental migration indicates that the previous\n // SHARD_ID was \"0\" and the new APP_ID is \"zero\" (i.e. the default values\n // for those options). In this case, the upstream format is identical, and\n // no migration is necessary. However, the version is bumped to v5 to\n // indicate that it was created with the {APP_ID} configuration and should\n // not be decommissioned as a legacy shard.\n\n 6: {\n migrateSchema: async (lc, sql) => {\n assert(\n replicaVersion,\n `replicaVersion is always passed for incremental migrations`,\n );\n await Promise.all([\n sql`\n ALTER TABLE ${sql(shardConfigTable)} ADD \"replicaVersion\" TEXT`,\n sql`\n UPDATE ${sql(shardConfigTable)} SET ${sql({replicaVersion})}`,\n ]);\n lc.info?.(\n `Recorded replicaVersion ${replicaVersion} in upstream shardConfig`,\n );\n },\n },\n\n // Updates the DDL event trigger protocol to v2, and adds support for\n // ALTER SCHEMA x RENAME TO y\n 7: {\n migrateSchema: async (lc, sql) => {\n const [{publications}] = await sql<{publications: string[]}[]>`\n SELECT publications FROM ${sql(shardConfigTable)}`;\n await setupTriggers(lc, sql, {...shard, publications});\n lc.info?.(`Upgraded to v2 event triggers`);\n },\n },\n\n // Adds support for non-disruptive resyncs, which tracks multiple\n // replicas with different slot names.\n 8: {\n migrateSchema: async (lc, sql) => {\n const legacyShardConfigSchema = v.object({\n replicaVersion: v.string().nullable(),\n initialSchema: publishedSchema.nullable(),\n });\n const result = await sql`\n SELECT \"replicaVersion\", \"initialSchema\" FROM ${sql(shardConfigTable)}`;\n assert(\n result.length === 1,\n () => `Expected exactly one shardConfig row, got ${result.length}`,\n );\n const {replicaVersion, initialSchema} = v.parse(\n result[0],\n legacyShardConfigSchema,\n 'passthrough',\n );\n\n await Promise.all([\n sql`\n CREATE TABLE ${sql(upstreamSchema(shard))}.replicas (\n \"slot\" TEXT PRIMARY KEY,\n \"version\" TEXT NOT NULL,\n \"initialSchema\" JSON NOT NULL\n );\n `,\n sql`\n INSERT INTO ${sql(upstreamSchema(shard))}.replicas ${sql({\n slot: legacyReplicationSlot(shard),\n version: replicaVersion,\n initialSchema,\n })}\n `,\n sql`\n ALTER TABLE ${sql(shardConfigTable)} DROP \"replicaVersion\", DROP \"initialSchema\"\n `,\n ]);\n lc.info?.(`Upgraded schema to support non-disruptive resyncs`);\n },\n },\n\n // v9: Fixes field ordering of compound indexes. This incremental migration\n // only fixes indexes resulting from new schema changes. A full resync is\n // required to fix existing indexes.\n //\n // The migration has been subsumed by the identical logic for migrating\n // to v12 (i.e. a trigger upgrade).\n\n // Adds the `mutations` table used to track mutation results.\n 10: {\n migrateSchema: async (lc, sql) => {\n await sql.unsafe(/*sql*/ `\n ${getMutationsTableDefinition(upstreamSchema(shard))}\n ALTER PUBLICATION ${id(metadataPublicationName(shard.appID, shard.shardNum))} ADD TABLE ${id(upstreamSchema(shard))}.\"mutations\";\n `);\n lc.info?.('Upgraded schema with new mutations table');\n },\n },\n\n // v11: Formerly dropped the schemaVersions table, but restored in the v13\n // migration for rollback safety.\n\n // v12: Upgrade DDL trigger to query schemaOID, needed information for auto-backfill.\n // (subsumed by v14)\n\n // Recreates the legacy schemaVersions table that was prematurely dropped\n // in the (former) v11 migration. It needs to remain present for at least one\n // release in order to be rollback safe.\n //\n // TODO: Drop the table once a release that no longer reads the table has\n // been rolled out.\n 13: {\n migrateSchema: async (_, sql) => {\n await sql`\n CREATE TABLE IF NOT EXISTS ${sql(upstreamSchema(shard))}.\"schemaVersions\" (\n \"minSupportedVersion\" INT4,\n \"maxSupportedVersion\" INT4,\n \"lock\" BOOL PRIMARY KEY DEFAULT true CHECK (lock)\n );`;\n await sql`\n INSERT INTO ${sql(upstreamSchema(shard))}.\"schemaVersions\" \n (\"lock\", \"minSupportedVersion\", \"maxSupportedVersion\")\n VALUES (true, 1, 1)\n ON CONFLICT DO NOTHING;\n `;\n },\n },\n\n // v14: Upgrade DDL trigger to log more info to PG logs.\n // (subsumed by v16)\n\n // Add initialSyncContext column to replicas table.\n 15: {\n migrateSchema: async (_, sql) => {\n await sql`\n ALTER TABLE ${sql(upstreamSchema(shard))}.replicas\n ADD COLUMN \"initialSyncContext\" JSON,\n ADD COLUMN \"subscriberContext\" JSON\n `;\n },\n },\n\n // v16: Upgrade DDL trigger to fire on all ALTER TABLE statements\n // to catch the *removal* of a table from the published set.\n // (subsumed by v17)\n\n // v17: Upgrade DDL triggers to support the COMMENT ON PUBLICATION hook for\n // working around the lack of event trigger support for PUBLICATION\n // changes in supabase.\n //\n // This also adds forwards-compatible support for hierarchical logical\n // message prefixes and unknown ddl event types.\n // (subsued by v18)\n\n // v18: Pure refactoring of event trigger code.\n 18: {\n migrateSchema: async (lc, sql) => {\n const [{publications}] = await sql<{publications: string[]}[]>`\n SELECT publications FROM ${sql(shardConfigTable)}`;\n await setupTriggers(lc, sql, {...shard, publications});\n lc.info?.(`Upgraded DDL event triggers`);\n },\n },\n };\n}\n\n// Referenced in tests.\nexport const CURRENT_SCHEMA_VERSION = Object.keys(\n getIncrementalMigrations({\n appID: 'unused',\n shardNum: 0,\n publications: ['foo'],\n }),\n).reduce((prev, curr) => Math.max(prev, parseInt(curr)), 0);\n\nexport async function decommissionLegacyShard(\n lc: LogContext,\n db: PostgresDB,\n shard: ShardConfig,\n) {\n if (shard.appID !== 'zero') {\n // When migration from non-default shard ids, e.g. \"zero_prod\" => \"prod_0\",\n // clean up the old \"zero_prod\" shard if it is pre-v5. Note that the v5\n // check is important to guard against cleaning up a **new** \"zero_0\" app\n // that coexists with the current App (with app-id === \"0\").\n const versionHistory = await getVersionHistory(db, `zero_${shard.appID}`);\n if (versionHistory !== null && versionHistory.schemaVersion < 5) {\n await decommissionShard(lc, db, 'zero', shard.appID);\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"init.js","names":[],"sources":["../../../../../../../../zero-cache/src/services/change-source/pg/schema/init.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {assert} from '../../../../../../shared/src/asserts.ts';\nimport * as v from '../../../../../../shared/src/valita.ts';\nimport {\n getVersionHistory,\n runSchemaMigrations,\n type IncrementalMigrationMap,\n type Migration,\n} from '../../../../db/migration.ts';\nimport type {PostgresDB} from '../../../../types/pg.ts';\nimport {upstreamSchema, type ShardConfig} from '../../../../types/shards.ts';\nimport {id} from '../../../../types/sql.ts';\nimport {AutoResetSignal} from '../../../change-streamer/schema/tables.ts';\nimport {decommissionShard} from '../decommission.ts';\nimport {publishedSchema} from './published.ts';\nimport {\n getMutationsTableDefinition,\n legacyReplicationSlot,\n metadataPublicationName,\n setupTablesAndReplication,\n setupTriggers,\n} from './shard.ts';\n\n/**\n * Ensures that a shard is set up for initial sync.\n */\nexport async function ensureShardSchema(\n lc: LogContext,\n db: PostgresDB,\n shard: ShardConfig,\n): Promise<void> {\n const initialSetup: Migration = {\n migrateSchema: (lc, tx) => setupTablesAndReplication(lc, tx, shard),\n minSafeVersion: 1,\n };\n await runSchemaMigrations(\n lc,\n `upstream-shard-${shard.appID}`,\n upstreamSchema(shard),\n db,\n initialSetup,\n // The incremental migration of any existing replicas will be replaced by\n // the incoming replica being synced, so the replicaVersion here is\n // unnecessary.\n getIncrementalMigrations(shard, 'obsolete'),\n );\n}\n\n/**\n * Updates the schema for an existing shard.\n */\nexport async function updateShardSchema(\n lc: LogContext,\n db: PostgresDB,\n shard: ShardConfig,\n replicaVersion: string,\n): Promise<void> {\n await runSchemaMigrations(\n lc,\n `upstream-shard-${shard.appID}`,\n upstreamSchema(shard),\n db,\n {\n // If the expected existing shard is absent, throw an\n // AutoResetSignal to backtrack and initial sync.\n migrateSchema: () => {\n throw new AutoResetSignal(\n `upstream shard ${upstreamSchema(shard)} is not initialized`,\n );\n },\n },\n getIncrementalMigrations(shard, replicaVersion),\n );\n\n // The decommission check is run in updateShardSchema so that it happens\n // after initial sync, and not when the shard schema is initially set up.\n await decommissionLegacyShard(lc, db, shard);\n}\n\nfunction getIncrementalMigrations(\n shard: ShardConfig,\n replicaVersion?: string,\n): IncrementalMigrationMap {\n const shardConfigTable = `${upstreamSchema(shard)}.shardConfig`;\n\n return {\n 4: {\n migrateSchema: () => {\n throw new AutoResetSignal('resetting to upgrade shard schema');\n },\n minSafeVersion: 3,\n },\n\n // v5: changes the upstream schema organization from \"zero_{SHARD_ID}\" to\n // the \"{APP_ID}_0\". An incremental migration indicates that the previous\n // SHARD_ID was \"0\" and the new APP_ID is \"zero\" (i.e. the default values\n // for those options). In this case, the upstream format is identical, and\n // no migration is necessary. However, the version is bumped to v5 to\n // indicate that it was created with the {APP_ID} configuration and should\n // not be decommissioned as a legacy shard.\n\n 6: {\n migrateSchema: async (lc, sql) => {\n assert(\n replicaVersion,\n `replicaVersion is always passed for incremental migrations`,\n );\n await Promise.all([\n sql`\n ALTER TABLE ${sql(shardConfigTable)} ADD \"replicaVersion\" TEXT`,\n sql`\n UPDATE ${sql(shardConfigTable)} SET ${sql({replicaVersion})}`,\n ]);\n lc.info?.(\n `Recorded replicaVersion ${replicaVersion} in upstream shardConfig`,\n );\n },\n },\n\n // Updates the DDL event trigger protocol to v2, and adds support for\n // ALTER SCHEMA x RENAME TO y\n 7: {\n migrateSchema: async (lc, sql) => {\n const [{publications}] = await sql<{publications: string[]}[]>`\n SELECT publications FROM ${sql(shardConfigTable)}`;\n await setupTriggers(lc, sql, {...shard, publications});\n lc.info?.(`Upgraded to v2 event triggers`);\n },\n },\n\n // Adds support for non-disruptive resyncs, which tracks multiple\n // replicas with different slot names.\n 8: {\n migrateSchema: async (lc, sql) => {\n const legacyShardConfigSchema = v.object({\n replicaVersion: v.string().nullable(),\n initialSchema: publishedSchema.nullable(),\n });\n const result = await sql`\n SELECT \"replicaVersion\", \"initialSchema\" FROM ${sql(shardConfigTable)}`;\n assert(\n result.length === 1,\n () => `Expected exactly one shardConfig row, got ${result.length}`,\n );\n const {replicaVersion, initialSchema} = v.parse(\n result[0],\n legacyShardConfigSchema,\n 'passthrough',\n );\n\n await Promise.all([\n sql`\n CREATE TABLE ${sql(upstreamSchema(shard))}.replicas (\n \"slot\" TEXT PRIMARY KEY,\n \"version\" TEXT NOT NULL,\n \"initialSchema\" JSON NOT NULL\n );\n `,\n sql`\n INSERT INTO ${sql(upstreamSchema(shard))}.replicas ${sql({\n slot: legacyReplicationSlot(shard),\n version: replicaVersion,\n initialSchema,\n })}\n `,\n sql`\n ALTER TABLE ${sql(shardConfigTable)} DROP \"replicaVersion\", DROP \"initialSchema\"\n `,\n ]);\n lc.info?.(`Upgraded schema to support non-disruptive resyncs`);\n },\n },\n\n // v9: Fixes field ordering of compound indexes. This incremental migration\n // only fixes indexes resulting from new schema changes. A full resync is\n // required to fix existing indexes.\n //\n // The migration has been subsumed by the identical logic for migrating\n // to v12 (i.e. a trigger upgrade).\n\n // Adds the `mutations` table used to track mutation results.\n 10: {\n migrateSchema: async (lc, sql) => {\n await sql.unsafe(/*sql*/ `\n ${getMutationsTableDefinition(upstreamSchema(shard))}\n ALTER PUBLICATION ${id(metadataPublicationName(shard.appID, shard.shardNum))} ADD TABLE ${id(upstreamSchema(shard))}.\"mutations\";\n `);\n lc.info?.('Upgraded schema with new mutations table');\n },\n },\n\n // v11: Formerly dropped the schemaVersions table, but restored in the v13\n // migration for rollback safety.\n\n // v12: Upgrade DDL trigger to query schemaOID, needed information for auto-backfill.\n // (subsumed by v14)\n\n // Recreates the legacy schemaVersions table that was prematurely dropped\n // in the (former) v11 migration. It needs to remain present for at least one\n // release in order to be rollback safe.\n //\n // TODO: Drop the table once a release that no longer reads the table has\n // been rolled out.\n 13: {\n migrateSchema: async (_, sql) => {\n await sql`\n CREATE TABLE IF NOT EXISTS ${sql(upstreamSchema(shard))}.\"schemaVersions\" (\n \"minSupportedVersion\" INT4,\n \"maxSupportedVersion\" INT4,\n \"lock\" BOOL PRIMARY KEY DEFAULT true CHECK (lock)\n );`;\n await sql`\n INSERT INTO ${sql(upstreamSchema(shard))}.\"schemaVersions\" \n (\"lock\", \"minSupportedVersion\", \"maxSupportedVersion\")\n VALUES (true, 1, 1)\n ON CONFLICT DO NOTHING;\n `;\n },\n },\n\n // v14: Upgrade DDL trigger to log more info to PG logs.\n // (subsumed by v16)\n\n // Add initialSyncContext column to replicas table.\n 15: {\n migrateSchema: async (_, sql) => {\n await sql`\n ALTER TABLE ${sql(upstreamSchema(shard))}.replicas\n ADD COLUMN \"initialSyncContext\" JSON,\n ADD COLUMN \"subscriberContext\" JSON\n `;\n },\n },\n\n // v16: Upgrade DDL trigger to fire on all ALTER TABLE statements\n // to catch the *removal* of a table from the published set.\n // (subsumed by v17)\n\n // v17: Upgrade DDL triggers to support the COMMENT ON PUBLICATION hook for\n // working around the lack of event trigger support for PUBLICATION\n // changes in supabase.\n //\n // This also adds forwards-compatible support for hierarchical logical\n // message prefixes and unknown ddl event types.\n // (subsumed by v18)\n\n // v18: Pure refactoring of event trigger code.\n // (subsumed by v19)\n\n // v19: Correctly handle concurrently issued DDL statements.\n // (subsumed by v20)\n\n // v20: Handle nested DDL triggers\n 20: {\n migrateSchema: async (lc, sql) => {\n const [{publications}] = await sql<{publications: string[]}[]>`\n SELECT publications FROM ${sql(shardConfigTable)}`;\n await setupTriggers(lc, sql, {...shard, publications});\n lc.info?.(`Upgraded DDL event triggers`);\n },\n },\n };\n}\n\n// Referenced in tests.\nexport const CURRENT_SCHEMA_VERSION = Object.keys(\n getIncrementalMigrations({\n appID: 'unused',\n shardNum: 0,\n publications: ['foo'],\n }),\n).reduce((prev, curr) => Math.max(prev, parseInt(curr)), 0);\n\nexport async function decommissionLegacyShard(\n lc: LogContext,\n db: PostgresDB,\n shard: ShardConfig,\n) {\n if (shard.appID !== 'zero') {\n // When migration from non-default shard ids, e.g. \"zero_prod\" => \"prod_0\",\n // clean up the old \"zero_prod\" shard if it is pre-v5. Note that the v5\n // check is important to guard against cleaning up a **new** \"zero_0\" app\n // that coexists with the current App (with app-id === \"0\").\n const versionHistory = await getVersionHistory(db, `zero_${shard.appID}`);\n if (versionHistory !== null && versionHistory.schemaVersion < 5) {\n await decommissionShard(lc, db, 'zero', shard.appID);\n }\n }\n}\n"],"mappings":"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"}
@@ -1,7 +1,6 @@
1
1
  import type postgres from 'postgres';
2
2
  import * as v from '../../../../../../shared/src/valita.ts';
3
- export declare function publishedTableQuery(publications: readonly string[]): string;
4
- export declare function indexDefinitionsQuery(publications: readonly string[]): string;
3
+ export declare function publishedSchemaQuery(publications: readonly string[]): string;
5
4
  export declare const publishedSchema: v.Type<{
6
5
  indexes: {
7
6
  isReplicaIdentity?: boolean | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"published.d.ts","sourceRoot":"","sources":["../../../../../../../../zero-cache/src/services/change-source/pg/schema/published.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,QAAQ,MAAM,UAAU,CAAC;AAErC,OAAO,KAAK,CAAC,MAAM,wCAAwC,CAAC;AAS5D,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,UAmFlE;AAED,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,UA6EpE;AAED,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsDxB,CAAC;AAEL,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D,MAAM,MAAM,iCAAiC,GAC3C,eAAe,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC;AAUpC,QAAA,MAAM,wBAAwB;;;;;;cAA6B,CAAC;AAE5D,MAAM,MAAM,eAAe,GAAG,eAAe,GAAG;IAC9C,YAAY,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;CACxD,CAAC;AAEF;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,QAAQ,CAAC,GAAG,EACjB,YAAY,EAAE,MAAM,EAAE,GACrB,OAAO,CAAC,eAAe,CAAC,CAsD1B"}
1
+ {"version":3,"file":"published.d.ts","sourceRoot":"","sources":["../../../../../../../../zero-cache/src/services/change-source/pg/schema/published.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,QAAQ,MAAM,UAAU,CAAC;AAIrC,OAAO,KAAK,CAAC,MAAM,wCAAwC,CAAC;AAS5D,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,UAmKnE;AAED,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsDxB,CAAC;AAEL,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D,MAAM,MAAM,iCAAiC,GAC3C,eAAe,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC;AAUpC,QAAA,MAAM,wBAAwB;;;;;;cAA6B,CAAC;AAE5D,MAAM,MAAM,eAAe,GAAG,eAAe,GAAG;IAC9C,YAAY,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;CACxD,CAAC;AAEF;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,QAAQ,CAAC,GAAG,EACjB,YAAY,EAAE,MAAM,EAAE,GACrB,OAAO,CAAC,eAAe,CAAC,CAmD1B"}
@@ -1,12 +1,14 @@
1
+ import { assert } from "../../../../../../shared/src/asserts.js";
1
2
  import { parse, valita_exports } from "../../../../../../shared/src/valita.js";
2
3
  import { equals } from "../../../../../../shared/src/set-utils.js";
4
+ import { BigIntJSON } from "../../../../../../shared/src/bigint-json.js";
3
5
  import { publishedIndexSpec, publishedTableSpec } from "../../../../db/specs.js";
4
6
  import { liteTableName } from "../../../../types/names.js";
5
7
  import { mapPostgresToLite, mapPostgresToLiteIndex } from "../../../../db/pg-to-lite.js";
6
8
  import { computeZqlSpecsFromLiteSpecs } from "../../../../db/lite-tables.js";
7
9
  import { literal } from "pg-format";
8
10
  //#region ../zero-cache/src/services/change-source/pg/schema/published.ts
9
- function publishedTableQuery(publications) {
11
+ function publishedSchemaQuery(publications) {
10
12
  return `
11
13
  WITH published_columns AS (SELECT
12
14
  pc.oid::int8 AS "oid",
@@ -79,14 +81,10 @@ tables AS (SELECT json_build_object(
79
81
  "publication",
80
82
  jsonb_build_object('rowFilter', "rowFilter")
81
83
  )
82
- ) AS "table" FROM published_columns GROUP BY "schema", "schemaOID", "name", "oid", "replicaIdentity")
83
-
84
- SELECT COALESCE(json_agg("table"), '[]'::json) as "tables" FROM tables
85
- `;
86
- }
87
- function indexDefinitionsQuery(publications) {
88
- return `
89
- WITH indexed_columns AS (SELECT
84
+ ) AS "table" FROM published_columns
85
+ GROUP BY "schema", "schemaOID", "name", "oid", "replicaIdentity"),
86
+
87
+ indexed_columns AS (SELECT
90
88
  pg_indexes.schemaname as "schema",
91
89
  pg_indexes.tablename as "tableName",
92
90
  pg_indexes.indexname as "name",
@@ -142,7 +140,10 @@ function indexDefinitionsQuery(publications) {
142
140
  GROUP BY "schema", "tableName", "name", "unique",
143
141
  "isPrimaryKey", "isReplicaIdentity", "isImmediate")
144
142
 
145
- SELECT COALESCE(json_agg("index"), '[]'::json) as "indexes" FROM indexes
143
+ SELECT json_build_object(
144
+ 'tables', COALESCE((SELECT json_agg("table") FROM tables), '[]'::json),
145
+ 'indexes', COALESCE((SELECT json_agg("index") FROM indexes), '[]'::json)
146
+ ) as "publishedSchema"
146
147
  `;
147
148
  }
148
149
  var publishedSchema = valita_exports.object({
@@ -199,9 +200,7 @@ async function getPublicationInfo(sql, publications) {
199
200
  WHERE pb.pubname IN (${literal(publications)})
200
201
  ORDER BY pubname;
201
202
 
202
- ${publishedTableQuery(publications)};
203
-
204
- ${indexDefinitionsQuery(publications)};
203
+ ${publishedSchemaQuery(publications)};
205
204
  `);
206
205
  const publishedColumns = result[0];
207
206
  for (const { table, publications } of publishedColumns) {
@@ -212,15 +211,13 @@ async function getPublicationInfo(sql, publications) {
212
211
  else if (!equals(expected, cols)) throw new Error(`Table ${table} is exported with different columns: [${[...expected]}] vs [${[...cols]}]`);
213
212
  });
214
213
  }
214
+ assert(result[2][0].publishedSchema, () => `Invalid publishedSchema result ${BigIntJSON.stringify(result[2])}`);
215
215
  return {
216
216
  publications: parse(result[1], publicationsResultSchema),
217
- ...parse({
218
- ...result[2][0],
219
- ...result[3][0]
220
- }, publishedSchema)
217
+ ...parse(result[2][0].publishedSchema, publishedSchema)
221
218
  };
222
219
  }
223
220
  //#endregion
224
- export { getPublicationInfo, indexDefinitionsQuery, publishedSchema, publishedTableQuery };
221
+ export { getPublicationInfo, publishedSchema, publishedSchemaQuery };
225
222
 
226
223
  //# sourceMappingURL=published.js.map