Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add Commands enum to decode prost messages to strong type #3887

Merged
merged 17 commits into from
Apr 3, 2023

Conversation

stuartcarnie
Copy link
Contributor

@stuartcarnie stuartcarnie commented Mar 19, 2023

Which issue does this PR close?

Closes #3874

Rationale for this change

Per the issue, this PR introduces a Commands enum to allow client to match on all supported messages.

The following minor improvements were made to the prost_message_ext macro:

1. Generate a constant for the type URLs:

const ACTION_CLOSE_PREPARED_STATEMENT_REQUEST_TYPE_URL: &'static str = concat!( "type.googleapis.com/arrow.flight.protocol.sql.", stringify!( ActionClosePreparedStatementRequest ) );
const ACTION_CREATE_PREPARED_STATEMENT_REQUEST_TYPE_URL: &'static str = concat!( "type.googleapis.com/arrow.flight.protocol.sql.", stringify!( ActionCreatePreparedStatementRequest ) );
// Remainder omitted for brevity

2. Commands enum to unpack any of the supported FlightSQL commands:

pub enum Commands {
    ActionClosePreparedStatementRequest(ActionClosePreparedStatementRequest),
    ActionCreatePreparedStatementRequest(ActionCreatePreparedStatementRequest),
    // Remainder omitted for brevity
}

with an associated unpack function:

impl Commands {
    pub fn unpack(any: Any) -> Result<Commands, ArrowError> {
        match any.type_url.as_str() {
            ACTION_CLOSE_PREPARED_STATEMENT_REQUEST_TYPE_URL => {
                let m: ActionClosePreparedStatementRequest = Message::decode(&*any.value).map_err(|err| {
                    ArrowError::ParseError({
                        let res = ::alloc::fmt::format(format_args!("Unable to decode Any value: {err}", err = err));
                        res
                    })
                })?;
                Ok(Self::ActionClosePreparedStatementRequest(m))
            }
            ACTION_CREATE_PREPARED_STATEMENT_REQUEST_TYPE_URL => {
                let m: ActionCreatePreparedStatementRequest = Message::decode(&*any.value).map_err(|err| {
                    ArrowError::ParseError({
                        let res = ::alloc::fmt::format(format_args!("Unable to decode Any value: {err}", err = err));
                        res
                    })
                })?;
                Ok(Self::ActionCreatePreparedStatementRequest(m))
            }

            // Remainder omitted for brevity

            _ => Err(ArrowError::ParseError({
                let res = ::alloc::fmt::format(IntellijRustDollarCrate::__export::format_args!("Unable to decode Any value: {}", any.type_url));
                res
            }))
        }
    }
}

3. Update other generated code to use the shared constant for the type URL:

impl ProstMessageExt for ActionClosePreparedStatementRequest {
    fn type_url() -> &'static str {
        ACTION_CLOSE_PREPARED_STATEMENT_REQUEST_TYPE_URL
    }

    fn as_any(&self) -> Any {
        Any {
            type_url: <ActionClosePreparedStatementRequest>::type_url().to_string(),
            value: self.encode_to_vec().into(),
        }
    }
}
impl ProstMessageExt for ActionCreatePreparedStatementRequest {
    fn type_url() -> &'static str {
        ACTION_CREATE_PREPARED_STATEMENT_REQUEST_TYPE_URL
    }

    fn as_any(&self) -> Any {
        Any {
            type_url: <ActionCreatePreparedStatementRequest>::type_url().to_string(),
            value: self.encode_to_vec().into(),
        }
    }
}
// Remainder omitted for brevity

What changes are included in this PR?

Are there any user-facing changes?

API improvements for users of the FlightSQL module.

@github-actions github-actions bot added arrow Changes to the arrow crate arrow-flight Changes to the arrow-flight crate labels Mar 19, 2023
@stuartcarnie stuartcarnie force-pushed the sgc/issue/prost_3874 branch from 8c0521a to 727e420 Compare March 20, 2023 00:03
@alamb
Copy link
Contributor

alamb commented Mar 20, 2023

I plan to review this shortly

Copy link
Contributor

@alamb alamb 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 @stuartcarnie -- this is very helpful ❤️ -- I really like it

Some things I noticed while playing with this:

  1. In order to make this code more "discoverable" I think it would be great to add some additional documentation / examples and update the examples. I am working on a PR to do so
  2. . It would be nice if the FlightSQL implementation used FlightError rather than ArrowError (not caused by this PR). I will file a follow on PR to track this

)*

as_item! {
pub enum Commands {
Copy link
Contributor

@alamb alamb Mar 20, 2023

Choose a reason for hiding this comment

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

This is a little sparse and I think it would be hard for users to figure out how to use this

Screenshot 2023-03-20 at 4 40 12 PM

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good call – do you think adding some docs to the enum and then describing using the TryFrom<Any> trait to decoding them?

type_url: <$name>::type_url().to_string(),
value: self.encode_to_vec().into(),
impl Commands {
pub fn unpack(any: Any) -> Result<Commands, ArrowError> {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think a TryFrom impl would also be the standard way to expose this function. I will propose one.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed – I've removed unpack completely, as Command::try_from or any.try_into() is much better 👍🏻

arrow-flight/src/sql/mod.rs Show resolved Hide resolved
@alamb
Copy link
Contributor

alamb commented Mar 20, 2023

Here are some suggested improvements: stuartcarnie#2 (docs + revamp some of the server.rs code to use the new dispatch logic 👍 )

Copy link
Contributor

@alamb alamb left a comment

Choose a reason for hiding this comment

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

Thanks @stuartcarnie -- I think this is looking great

One thing I would really like to do is to translate

    async fn do_get(

in arrow-flight/src/sql/server.rs to use this new pattern too (mostly as an exercise in trying out the API). I ran out of time last night. I think we could do it in a follow on PR too

Any {
type_url: <$name>::type_url().to_string(),
value: self.encode_to_vec().into(),
/// Get the URL for the command.
Copy link
Contributor

Choose a reason for hiding this comment

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

👍 this is great

| Command::TicketStatementQuery(_)
| Command::ActionCreatePreparedStatementResult(_) => {
Err(Status::unimplemented(format!(
"get_flight_info: The defined request is invalid: {command:?}",
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
"get_flight_info: The defined request is invalid: {command:?}",
"get_flight_info: The defined request is invalid: {}", command.type_url(),

@alamb
Copy link
Contributor

alamb commented Mar 21, 2023

I pushed a few minor doc updates directly to this branch.

@alamb
Copy link
Contributor

alamb commented Mar 21, 2023

I found an issue with this approach while trying to use this Command structure: stuartcarnie#3 would love to know your thoughts @stuartcarnie

* Updated `do_get` and `do_put` functions to use `Command` enum
* Added test for Unknown variant
@stuartcarnie
Copy link
Contributor Author

@alamb see ea18fa9 for the addition of the Unknown(Any) variant, which was a great suggestion. I've also updated do_get and do_put, and the simple test now passes ✅

Comment on lines +353 to +356
cmd => Err(Status::unimplemented(format!(
"get_flight_info: The defined request is invalid: {}",
cmd.type_url()
))),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@alamb I combined all remaining variants into a single error. I left it as unimplemented, as it is unclear to me if there are additional commands that may be implemented in the future. If not, perhaps invalid_argument is a more appropriate response, however, given that is a change that could break downstream clients, I've left it.

@alamb
Copy link
Contributor

alamb commented Mar 23, 2023

Thanks -- I plan to come back to this PR over the weekend but got overloaded with other things this week

# Conflicts:
#	arrow-flight/src/sql/server.rs
@alamb
Copy link
Contributor

alamb commented Apr 3, 2023

I am sorry for the delay -- I will get this done / merged this week

Copy link
Contributor

@alamb alamb left a comment

Choose a reason for hiding this comment

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

Thanks @stuartcarnie -- I reviewed this again and I think it looks great. I merged up from main to resolve a conflict and plan to merge this PR in when it passes CI


// Unknown variant

let any = Any {
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

@alamb
Copy link
Contributor

alamb commented Apr 3, 2023

Github seems to have some issue -- restarting

Screenshot 2023-04-03 at 3 05 12 PM

@alamb
Copy link
Contributor

alamb commented Apr 3, 2023

I propose some additional documentation for this feature here #4012

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
arrow Changes to the arrow crate arrow-flight Changes to the arrow-flight crate
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Make it easier to match FlightSQL messages
2 participants