Skip to content

Support named query parameters #8384

New issue

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

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

Already on GitHub? # to your account

Merged
merged 29 commits into from
Dec 4, 2023
Merged

Support named query parameters #8384

merged 29 commits into from
Dec 4, 2023

Conversation

Asura7969
Copy link
Contributor

@Asura7969 Asura7969 commented Dec 1, 2023

Which issue does this PR close?

Closes #8245.

Rationale for this change

it might be clearer to use named parameters

What changes are included in this PR?

  • change the with_param_values API
  • add ParamValues struct
  • add test case

Are these changes tested?

test_named_query_parameters

Are there any user-facing changes?

Add named query,like:

let results = ctx
        .sql("SELECT c1, c2 FROM test WHERE c1 > $coo AND c1 < $foo")
        .await?
        .with_param_values(vec![
            ("foo", ScalarValue::UInt32(Some(3))),
            ("coo", ScalarValue::UInt32(Some(0))),
        ])?
        .collect()
        .await?;

@github-actions github-actions bot added sql SQL Planner logical-expr Logical plan and expressions core Core DataFusion crate labels Dec 1, 2023

/// The parameter value corresponding to the placeholder
#[derive(Debug, Clone)]
pub enum ParamValues {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think it is more appropriate to encapsulate it into a struct

match self {
LogicalPlan::Prepare(prepare_lp) => {
// Verify if the number of params matches the number of values
Copy link
Contributor Author

Choose a reason for hiding this comment

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

move to ParamValues(verify method)

) -> Result<Expr> {
expr.transform(&|expr| {
match &expr {
Expr::Placeholder(Placeholder { id, data_type }) => {
if id.is_empty() || id == "$0" {
Copy link
Contributor Author

@Asura7969 Asura7969 Dec 1, 2023

Choose a reason for hiding this comment

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

move to ParamValues(get_placeholders_with_values method)

@alamb
Copy link
Contributor

alamb commented Dec 1, 2023

Thanks @Asura7969 -- I hope to review this later today or tomorrow

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 @Asura7969 -- this looks great 🙏 ❤️ -- it is embarassing that it wasn't immediately clear that this API can already handle named parameters.

I had a few comment suggestions to make it even more obvious, but I also think this PR could be merged as is and those comments done as follow ons.

Thanks again for the contribution

@@ -126,8 +126,24 @@ impl From<Vec<ScalarValue>> for ParamValues {
}
}

impl From<HashMap<String, ScalarValue>> for ParamValues {
fn from(value: HashMap<String, ScalarValue>) -> Self {
impl<K> From<Vec<(K, ScalarValue)>> for ParamValues
Copy link
Contributor

Choose a reason for hiding this comment

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

👌 very nice

@@ -1215,7 +1215,7 @@ impl DataFrame {
/// .with_param_values(vec![
/// // value at index 0 --> $1
/// ScalarValue::from(2i64)
/// ].into())?
/// ])?
/// .collect()
/// .await?;
/// assert_batches_eq!(
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you please also add a demonstration of how to use the hashmap version too so that it is easier to discover by a casual user?

Perhaps add this to the example:

    /// // Note you can also provide named parameters
    /// let results = ctx
    ///   .sql("SELECT a FROM example WHERE b = $my_param")
    ///   .await?
    ///    // replace $my_param with value 2
    ///    // Note you can also use a HashMap as well
    ///   .with_param_values(vec![
    ///      "my_param",
    ///      ScalarValue::from(2i64)
    ///    ])?
    ///   .collect()
    ///   .await?;
    /// assert_batches_eq!(

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 your comment, I'm done

@alamb
Copy link
Contributor

alamb commented Dec 4, 2023

Thanks @Asura7969 !

@alamb alamb merged commit 37bbd66 into apache:main Dec 4, 2023
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 again @Asura7969 -- I also proposed a small follow on PR #8418 to add an example to LogicalPlan::with_parameters as well

@Asura7969 Asura7969 deleted the query_params branch December 5, 2023 01:03
appletreeisyellow pushed a commit to appletreeisyellow/datafusion that referenced this pull request Dec 15, 2023
* Minor: Improve the document format of JoinHashMap

* support named query parameters

* cargo fmt

* add `ParamValues` conversion

* improve doc
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
core Core DataFusion crate logical-expr Logical plan and expressions sql SQL Planner
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support named query parameters
2 participants