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

ARRAY[] function #1264

Closed
wants to merge 18 commits into from
Closed

ARRAY[] function #1264

wants to merge 18 commits into from

Conversation

kivikakk
Copy link
Contributor

@kivikakk kivikakk commented Oct 20, 2017

Fixes #1263.

TODO:

  • Proper testing.
  • Put things where they belong. It should be somewhere pg-specific.
  • Improve the interface. We shouldn't have to use different names for different argument counts.

@kivikakk
Copy link
Contributor Author

kivikakk commented Oct 20, 2017

let value = select(array2::<Int4, _, Int4, _ ,Int4>(1, 2))

This is an eyesore, but I'm not sure how to convince the compiler to infer r as equal(ish?!) to the $arg_struct_name idents …

@kivikakk
Copy link
Contributor Author

I'd be curious on anyone's comments before I proceed with trying to clean this up!

@sgrif
Copy link
Member

sgrif commented Oct 22, 2017

I don't like this approach. I'd much rather we provide a single function called array which takes a tuple.

@kivikakk
Copy link
Contributor Author

I absolutely agree! Hence asking for comments. :) I'll work on that shortly.

@sgrif
Copy link
Member

sgrif commented Oct 26, 2017

So I'd roughly take this approach:

// Not super stoked on this name
pub trait IntoSingleTypeExpressionList<ST> {
    type Expression;

    fn into_single_type_expression_list(self) -> Self::Expression;
}

// This would go in types/impls/tuples.rs
impl<$($T,)+ ST> IntoSingleTypeExpressionList<ST> for ($($T,)+)
where
    $($T: AsExpression<ST>,)+
{
    type Output = ($(AsExprOf<$T, ST>,)+);

    fn into_single_type_expression_list(self) -> Self::Expression {
        ($(self.$idx.into_sql::<ST>(),)+)
    }
}

pub struct Array<T, ST> {
    elements: T,
    _marker: PhantomData<ST>,
}

// Order here is important, it's likely people will sometimes need to specify the sql type, but infer the actual argument being passed.
pub fn array<ST, T>(elements: T) -> Array<T::Expression, ST>
where
    T: IntoSingleTypeExpressionList<ST>,
{
    Array {
        elements: elements.into_single_type_expression_list(),
        _marker: PhantomData,
    }
}

impl<T, ST> Expression for Array<T, ST>
where
    T: Expression,
{
    type SqlType = types::Array<ST>;
}

It's worth noting that a single element array literal would look a little funky, as it'd be passed like: array((foo,)).

@kivikakk
Copy link
Contributor Author

This is now looking better. Thanks for your impeccable guidance there; it slotted right in with few changes.

Does the current positioning of the traits and structs for supporting this look OK?

_marker: PhantomData<ST>,
}

pub fn array<ST, T>(elements: T) -> Array<T::Expression, ST>
Copy link
Member

Choose a reason for hiding this comment

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

It would be great to add some small documentation with an example to this function.

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 point, thank you! Added in bb35442.

/// # }
/// # }
/// #
/// # #[cfg(feature = "postgres")]
Copy link
Member

Choose a reason for hiding this comment

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

Because this lives in diesel/src/pg I think we do not need the #[cfg(feature = "postgres")] annotation.

(Same for the empty main function for the not-pg case)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍

@@ -300,3 +300,9 @@ impl<QS, ST, DB> QueryId for BoxableExpression<QS, DB, SqlType = ST> {

const HAS_STATIC_QUERY_ID: bool = false;
}

pub trait IntoSingleTypeExpressionList<ST> {
Copy link
Member

Choose a reason for hiding this comment

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

I think this trait should also live in diesel/src/pg/expression/array.rs because it is Postgres specific.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍

where
T: Expression,
{
type SqlType = ::pg::types::sql_types::Array<ST>;
Copy link
Member

Choose a reason for hiding this comment

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

This can just be types::Array

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not in this context? types isn't imported here.

Copy link
Member

Choose a reason for hiding this comment

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

Or ::types::Array. Is there a reason for us to not import it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, good point. Fixed.

{
fn walk_ast(&self, mut out: AstPass<DB>) -> ::result::QueryResult<()> {
out.push_sql("ARRAY[");
QueryFragment::walk_ast(&(&self.elements,), out.reborrow())?;
Copy link
Member

Choose a reason for hiding this comment

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

Why the tuple?

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 question! Dropping it.

{
}

impl<T, ST> ::expression::NonAggregate for Array<T, ST>
Copy link
Member

Choose a reason for hiding this comment

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

Can we import this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep.

@@ -1,5 +1,7 @@
#[doc(hidden)]
pub mod array_comparison;
#[doc(hidden)]
pub mod array;
Copy link
Member

Choose a reason for hiding this comment

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

Does this need to be public?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Now that IntoSingleTypeExpressionList is in that module, yes.

Copy link
Member

Choose a reason for hiding this comment

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

Can we have a #[doc(hidden)] reexport of this from somewhere public and make the module private?

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'm not exactly sure what shape this should take, having tried a few ways. I've tried something in 99783c0, but it feels janky and wrong. Some documentation added too.

@sgrif
Copy link
Member

sgrif commented Nov 17, 2017

This needs some compile-fail tests as well, probably checking that: only usable with pg, expressions have to be the right type, expressions have to have the same type

@kivikakk
Copy link
Contributor Author

Good call! Done.

}

use query_builder::{AstPass, QueryFragment};
use backend::Backend;
Copy link
Member

Choose a reason for hiding this comment

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

Can we keep all the use statements at the top?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, done.

@sgrif sgrif added this to the 1.1 milestone Dec 3, 2017
}

#[derive(Debug)]
pub struct Array<T, ST> {
Copy link
Member

Choose a reason for hiding this comment

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

What do you think about naming this ArrayLiteral so it's clear what the distinction from types::Array is?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍

fn into_single_type_expression_list(self) -> Self::Expression;
}

#[derive(Debug)]
Copy link
Member

Choose a reason for hiding this comment

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

This should implement Clone and Copy.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍

_marker: PhantomData<ST>,
}

/// Creates an `ARRAY[...]` expression. The argument should be a tuple of
Copy link
Member

Choose a reason for hiding this comment

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

The first sentence should be its own paragraph.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍

/// // An array is returned as a Vec.
/// assert_eq!(Ok(vec![1, 2]), ints);
///
/// // The type of `id` is known so we don't have to specify the SQL type here.
Copy link
Member

Choose a reason for hiding this comment

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

This doc comment mentions type inference more than anything else. Do you think this is likely to be used in contexts where the type can't be inferred enough to warrant this much attention?

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 noticed behaviour where it wasn't, but I think that was in a previous iteration. I'll drop the excess.

}
}

impl_query_id!(Array<T, ST>);
Copy link
Member

Choose a reason for hiding this comment

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

Note: If #1391 is merged before this, this needs to get updated.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Understood.

@@ -1,5 +1,7 @@
#[doc(hidden)]
pub mod array_comparison;
#[doc(hidden)]
pub mod array;
Copy link
Member

Choose a reason for hiding this comment

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

Can we have a #[doc(hidden)] reexport of this from somewhere public and make the module private?

@@ -250,6 +250,18 @@ macro_rules! tuple_impls {
($($T,)+ next)
}
}

#[cfg(feature = "postgres")]
impl<$($T,)+ ST> $crate::pg::expression::array::IntoSingleTypeExpressionList<ST> for ($($T,)+) where
Copy link
Member

Choose a reason for hiding this comment

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

There's no need to use fully qualified paths vs just useing the trait here. This macro doesn't get called from user code.

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 connection = PgConnection::establish("").unwrap();
select(array((1, 3))).get_result::<Vec<i32>>(&connection);
select(array((1f64, 3f64))).get_result::<Vec<i32>>(&connection);
//~^ ERROR E0277
Copy link
Member

Choose a reason for hiding this comment

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

Can we be specific on at least one of these? (Also why are there so many?)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

They're like this:

unexpected errors (from JSON output): [
    Error {
        line_num: 10,
        kind: Some(
            Error
        ),
        msg: "10:5: 10:11: the trait bound `f64: diesel::SelectableExpression<()>` is not satisfied [E0277]"
    },
    Error {
        line_num: 10,
        kind: Some(
            Error
        ),
        msg: "10:33: 10:43: the trait bound `f64: diesel::expression::NonAggregate` is not satisfied [E0277]"
    },
    Error {
        line_num: 10,
        kind: Some(
            Error
        ),
        msg: "10:33: 10:43: the trait bound `f64: diesel::SelectableExpression<()>` is not satisfied [E0277]"
    },
    Error {
        line_num: 10,
        kind: Some(
            Error
        ),
        msg: "10:33: 10:43: the trait bound `f64: diesel::query_builder::QueryId` is not satisfied [E0277]"
    },
    Error {
        line_num: 10,
        kind: Some(
            Error
        ),
        msg: "10:12: 10:17: the trait bound `f64: diesel::Expression` is not satisfied [E0277]"
    },
    Error {
        line_num: 10,
        kind: Some(
            Error
        ),
        msg: "10:33: 10:43: the trait bound `f64: diesel::query_builder::QueryFragment<_>` is not satisfied [E0277]"
    },
    Error {
        line_num: 10,
        kind: Some(
            Error
        ),
        msg: "10:12: 10:31: the trait bound `f64: diesel::Expression` is not satisfied [E0277]"
    }
]

I'll add some specifics.

Copy link
Member

@sgrif sgrif Jan 3, 2018

Choose a reason for hiding this comment

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

Ughhhhh actually add none of those. Seriously Rust. Just fucking say "f64: AsExpression<Integer> is not satisfied" these errors are ridiculous.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Got it.

@sgrif
Copy link
Member

sgrif commented Jan 4, 2018

Thank you for all your hard work, this is an excellent PR! <3

@sgrif
Copy link
Member

sgrif commented Jan 12, 2018

I manually rebased, and made additional changes. This was merged in 8fb0429

@sgrif sgrif closed this Jan 12, 2018
sgrif added a commit that referenced this pull request Jan 12, 2018
@kivikakk kivikakk deleted the array-fn branch January 12, 2018 22:05
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.

None yet

3 participants