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
ARRAY[] function #1264
Conversation
diesel/diesel_tests/tests/expressions/mod.rs Line 364 in a0fce39
This is an eyesore, but I'm not sure how to convince the compiler to infer |
I'd be curious on anyone's comments before I proceed with trying to clean this up! |
I don't like this approach. I'd much rather we provide a single function called |
I absolutely agree! Hence asking for comments. :) I'll work on that shortly. |
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: |
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? |
diesel/src/pg/expression/array.rs
Outdated
_marker: PhantomData<ST>, | ||
} | ||
|
||
pub fn array<ST, T>(elements: T) -> Array<T::Expression, ST> |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
diesel/src/pg/expression/array.rs
Outdated
/// # } | ||
/// # } | ||
/// # | ||
/// # #[cfg(feature = "postgres")] |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
diesel/src/expression/mod.rs
Outdated
@@ -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> { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
diesel/src/pg/expression/array.rs
Outdated
where | ||
T: Expression, | ||
{ | ||
type SqlType = ::pg::types::sql_types::Array<ST>; |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, good point. Fixed.
diesel/src/pg/expression/array.rs
Outdated
{ | ||
fn walk_ast(&self, mut out: AstPass<DB>) -> ::result::QueryResult<()> { | ||
out.push_sql("ARRAY["); | ||
QueryFragment::walk_ast(&(&self.elements,), out.reborrow())?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the tuple?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question! Dropping it.
diesel/src/pg/expression/array.rs
Outdated
{ | ||
} | ||
|
||
impl<T, ST> ::expression::NonAggregate for Array<T, ST> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we import this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep.
diesel/src/pg/expression/mod.rs
Outdated
@@ -1,5 +1,7 @@ | |||
#[doc(hidden)] | |||
pub mod array_comparison; | |||
#[doc(hidden)] | |||
pub mod array; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
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 |
Good call! Done. |
diesel/src/pg/expression/array.rs
Outdated
} | ||
|
||
use query_builder::{AstPass, QueryFragment}; | ||
use backend::Backend; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, done.
diesel/src/pg/expression/array.rs
Outdated
} | ||
|
||
#[derive(Debug)] | ||
pub struct Array<T, ST> { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
diesel/src/pg/expression/array.rs
Outdated
fn into_single_type_expression_list(self) -> Self::Expression; | ||
} | ||
|
||
#[derive(Debug)] |
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
diesel/src/pg/expression/array.rs
Outdated
_marker: PhantomData<ST>, | ||
} | ||
|
||
/// Creates an `ARRAY[...]` expression. The argument should be a tuple of |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
diesel/src/pg/expression/array.rs
Outdated
/// // 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. |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
diesel/src/pg/expression/array.rs
Outdated
} | ||
} | ||
|
||
impl_query_id!(Array<T, ST>); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Understood.
diesel/src/pg/expression/mod.rs
Outdated
@@ -1,5 +1,7 @@ | |||
#[doc(hidden)] | |||
pub mod array_comparison; | |||
#[doc(hidden)] | |||
pub mod array; |
There was a problem hiding this comment.
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?
diesel/src/types/impls/tuples.rs
Outdated
@@ -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 |
There was a problem hiding this comment.
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 use
ing the trait here. This macro doesn't get called from user code.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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?)
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it.
Thank you for all your hard work, this is an excellent PR! <3 |
I manually rebased, and made additional changes. This was merged in 8fb0429 |
Fixes #1263.
TODO: