(&self, client: &mut C, _done: CopyDone)
| 126 | } |
| 127 | |
| 128 | async fn on_copy_done<C>(&self, client: &mut C, _done: CopyDone) -> PgWireResult<()> |
| 129 | where |
| 130 | C: ClientInfo + Sink<PgWireBackendMessage> + Unpin + Send + Sync, |
| 131 | C::Error: Debug, |
| 132 | PgWireError: From<<C as Sink<PgWireBackendMessage>>::Error>, |
| 133 | { |
| 134 | let id = conn_id(&client.socket_addr()); |
| 135 | let pending = self.restore_state.take(id).ok_or_else(|| { |
| 136 | sqlstate( |
| 137 | ss::FEATURE_NOT_SUPPORTED, |
| 138 | "no restore pending on this connection", |
| 139 | ) |
| 140 | })?; |
| 141 | let stats = backup::restore_tenant( |
| 142 | &self.state, |
| 143 | pending.tenant_id, |
| 144 | &pending.bytes, |
| 145 | pending.dry_run, |
| 146 | ) |
| 147 | .await |
| 148 | .map_err(internal)?; |
| 149 | // pgwire does not auto-send CommandComplete after `on_copy_done` |
| 150 | // returns Ok — the trait contract leaves message construction to |
| 151 | // the handler. Send a `RESTORE TENANT N <op-count>` tag so the |
| 152 | // client's COPY IN sink can complete. |
| 153 | let rows = |
| 154 | stats.documents + stats.kv_tables + stats.vectors + stats.timeseries + stats.edges; |
| 155 | let tag = Tag::new("RESTORE TENANT").with_rows(rows); |
| 156 | client |
| 157 | .send(PgWireBackendMessage::CommandComplete(tag.into())) |
| 158 | .await |
| 159 | .map_err(|e| { |
| 160 | sqlstate( |
| 161 | ss::INTERNAL_ERROR, |
| 162 | &format!("CommandComplete send failed: {e:?}"), |
| 163 | ) |
| 164 | })?; |
| 165 | // Leave the COPY-in-progress state so the next Sync from the |
| 166 | // client gets dispatched normally. pgwire's `process_message` |
| 167 | // only routes Sync via the `AwaitingSync` arm. |
| 168 | client.set_state(PgWireConnectionState::AwaitingSync); |
| 169 | Ok(()) |
| 170 | } |
| 171 | } |
| 172 | |
| 173 | fn conn_id(addr: &SocketAddr) -> u64 { |
nothing calls this directly
no test coverage detected