ParameterStatus on SET statements#293
Conversation
DateStyle and IntervalStyle must match the casing sent during startup.
Merge unit tests for parameter_status_key_for_set into single table-driven test. Add handler-level tests that call do_query() and assert ParameterStatus messages appear in MockClient's sent_messages, closing the gap where the actual feed() path was untested. Remove standalone timezone SingleAssignment case from parameter_status_key_for_set (SET TIME ZONE uses SetTimeZone).
|
I fixed the cargo fmt issue. Can you trigger the CI please @sunng87 ? |
sunng87
left a comment
There was a problem hiding this comment.
Thank you @TwistingTwists ! Please check my comments about moving the logic into our QueryHook completely.
datafusion-postgres/src/handlers.rs
Outdated
| { | ||
| if result.is_ok() { | ||
| if let Some((name, value)) = | ||
| set_show::parameter_status_key_for_set(&statement, client) |
There was a problem hiding this comment.
I haven't checked the order of returned packets for SET statements, I wonder if it's possible to add this logic to set_show's try_respond_set_statements. We can modify the definition of client in QueryHook if needed.
There was a problem hiding this comment.
Modifying the client definition to give hooks Sink access would require either:
- Generic methods on
QueryHook→ breaks object safety (Vec<Arc<dyn QueryHook>>) - A wrapper trait over
ClientInfo→ 12+ method delegation, fragile across pgwire upgrades
Instead, I'd propose making HookOutput a #[non_exhaustive] builder struct — hooks return what they want sent (ParameterStatus, and notices - in future), and do_query handles the actual client.feed(). This keeps all SET→ParameterStatus logic in set_show.rs while preserving object safety.
Adding future fields (e.g., notices for warnings) becomes non-breaking. This matches how Tower/Axum middleware returns response data rather than writing to the connection directly.
An impl is here:
359d428
I am open to take the other approach. (will revert this commit)
Let me know your thoughts @sunng87
There was a problem hiding this comment.
hi @TwistingTwists , I think we can just add Sink<PgWireBackendMessage> to our handler implementation and QueryHook definition, because the original SimpleQueryHandler and ExtendedQueryHandler has Sink<PgWireBackendMessage> for the C parameter.
I still hope you can just call client.send() or client.feed() in set_show as a side-effect, instead of returning it as response of QueryHook. Because there can be a chance we will need to deal with all possible messages in HookOutput, then it becomes duplicated information and fragile to maintain.
There was a problem hiding this comment.
hooks take &mut (dyn ClientInfo + Send + Sync). The simplest attempt is to add Sink<PgWireBackendMessage> to that dyn bound.
We get this error.
error[E0225]: only auto traits can be used as additional traits in a trait object
--> datafusion-postgres/src/hooks/mod.rs:34:36
|
34 | ...yn ClientInfo + Sink<PgWireBackendMessage> + ...
| ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^ additional non-auto trait
| |
| first non-auto trait
|I agree that managing HookOutput can be a little fragile.
Alternatively, we can have a supertrait -> https://github.com/datafusion-contrib/datafusion-postgres/pull/293/changes#diff-89ae6cc475596662d15f32aeafc00d7f0122f4398143dc26d481e76f20fe83e1R23
There was a problem hiding this comment.
I see. Thank you for the information. Let me experiment by myself.
There was a problem hiding this comment.
hello @TwistingTwists , could you try if this HookClient will work?
#[async_trait]
pub trait HookClient: ClientInfo + Send + Sync {
async fn send_message(&mut self, item: PgWireBackendMessage) -> PgWireResult<()>;
}
#[async_trait]
impl<S> HookClient for S
where
S: ClientInfo + Sink<PgWireBackendMessage> + Send + Sync + Unpin,
PgWireError: From<<S as Sink<PgWireBackendMessage>>::Error>,
{
async fn send_message(&mut self, item: PgWireBackendMessage) -> PgWireResult<()> {
use futures::SinkExt;
self.send(item).await.map_err(PgWireError::from)
}
}It has auto implementation of ClientInfo and a simple manually implementation for Sink
There was a problem hiding this comment.
Let me give that a spin.
There was a problem hiding this comment.
Thanks for this direction!
@sunng87 : This does remove the generic here: https://github.com/datafusion-contrib/datafusion-postgres/pull/293/changes#diff-9102592acfefe305e66a99d1eae41fff202430bff0f5d7a8c8082255966a604aL121
Should be fine, i guess.
code now works.
Address PR review feedback: ParameterStatus computation now lives in try_respond_set_statements instead of handlers.rs. QueryHook trait returns HookOutput (Response + optional ParameterStatus data), handler just forwards it to the client Sink.
Replace bare type aliases (ParameterStatusChange, HookOutput) with a proper struct supporting named fields and builder pattern. This makes future additions (notices, multiple ParameterStatus) non-breaking.
…ccess Replace the HookOutput return-type approach (where hooks returned ParameterStatus data for the handler to send) with an adapter pattern that lets hooks send messages directly through the client connection. - Add HookClient trait (supertrait of ClientInfo) with send_parameter_status() - Add PgHookClient<C> adapter that bridges generic pgwire client to dyn HookClient - Simplify handler loop: no more post-hook ParameterStatus forwarding - Remove HookOutput struct; hooks return Response directly - Update all hooks (SetShow, Transactions, Permissions) and tests
Use a blanket `impl<S> HookClient for S where S: ClientInfo + Sink<...>` so any pgwire client automatically implements HookClient. This removes the PgHookClient wrapper struct and ~110 lines of manual ClientInfo delegation and Sink forwarding. Also generalizes send_parameter_status to send_message, allowing hooks to send any PgWireBackendMessage.
sunng87
left a comment
There was a problem hiding this comment.
Awesome. Thank you @TwistingTwists
Attempt to fix: #242