Skip to content

ParameterStatus on SET statements#293

Merged
sunng87 merged 14 commits intodatafusion-contrib:masterfrom
TwistingTwists:feat/parameter-status-on-set
Feb 26, 2026
Merged

ParameterStatus on SET statements#293
sunng87 merged 14 commits intodatafusion-contrib:masterfrom
TwistingTwists:feat/parameter-status-on-set

Conversation

@TwistingTwists
Copy link
Contributor

Attempt to fix: #242

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).
@TwistingTwists
Copy link
Contributor Author

I fixed the cargo fmt issue. Can you trigger the CI please @sunng87 ?

Copy link
Member

@sunng87 sunng87 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @TwistingTwists ! Please check my comments about moving the logic into our QueryHook completely.

{
if result.is_ok() {
if let Some((name, value)) =
set_show::parameter_status_key_for_set(&statement, client)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor Author

@TwistingTwists TwistingTwists Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor Author

@TwistingTwists TwistingTwists Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Thank you for the information. Let me experiment by myself.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me give that a spin.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.
Copy link
Member

@sunng87 sunng87 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome. Thank you @TwistingTwists

@sunng87 sunng87 merged commit 718a833 into datafusion-contrib:master Feb 26, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Send ParameterStatus on SET statements

2 participants