Skip to content

Commit

Permalink
implement single column queries to vec
Browse files Browse the repository at this point in the history
  • Loading branch information
rkusa committed May 6, 2024
1 parent 8d1e646 commit fc2360a
Show file tree
Hide file tree
Showing 15 changed files with 476 additions and 137 deletions.
4 changes: 3 additions & 1 deletion postgres-macros/src/from_row_derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ pub fn expand_derive_from_row(input: DeriveInput) -> syn::Result<TokenStream> {
let (ty, kind) = extract_inner_type(&f.ty)?;

let name = const_name(&name);
struct_columns.push(parse_quote!(::sqlm_postgres::types::StructColumn<<#ty as ::sqlm_postgres::SqlType>::Type, #name>));
struct_columns.push(parse_quote!(
::sqlm_postgres::types::StructColumn<<#ty as ::sqlm_postgres::internal::AsSqlType>::SqlType, #name>
));

// Forward only certain args
let attrs = f
Expand Down
112 changes: 68 additions & 44 deletions postgres-macros/src/sql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,14 +222,14 @@ pub fn sql(item: TokenStream) -> TokenStream {
let type_check = if is_array {
quote! {
{
const fn assert_type<T: ::sqlm_postgres::SqlType<Type = #enum_struct>>(_: &[T]) {}
const fn assert_type<T: ::sqlm_postgres::internal::AsSqlType<SqlType = #enum_struct>>(_: &[T]) {}
assert_type(&(#param));
}
}
} else {
quote! {
{
const fn assert_type<T: ::sqlm_postgres::SqlType<Type = #enum_struct>>(_: &T) {}
const fn assert_type<T: ::sqlm_postgres::internal::AsSqlType<SqlType = #enum_struct>>(_: &T) {}
assert_type(&(#param));
}
}
Expand All @@ -244,7 +244,7 @@ pub fn sql(item: TokenStream) -> TokenStream {
continue;
}

let Some((ty_owned, ty_borrowed)) = postgres_to_rust_type(ty) else {
let Some((ty_owned, ty_borrowed, _)) = postgres_to_rust_type(ty) else {
return syn::Error::new(
input.query.span(),
format!("unsupporte postgres type: {ty:?}"),
Expand All @@ -255,18 +255,18 @@ pub fn sql(item: TokenStream) -> TokenStream {

// `Option::from` is used to allow parameters to be an Option
typed_parameters.push(quote! {
{
{
{
const fn assert_type<T, S>(_: &T)
where
T: ::sqlm_postgres::SqlType<Type = S>,
for<'a> ::sqlm_postgres::internal::Valid<'a, #ty_borrowed, #ty_owned>: From<S>
{}
assert_type(&(#param));
}
&(#param)
const fn assert_type<T, S>(_: &T)
where
T: ::sqlm_postgres::internal::AsSqlType<SqlType = S>,
for<'a> ::sqlm_postgres::internal::Valid<'a, #ty_borrowed, #ty_owned>: From<S>
{}
assert_type(&(#param));
}
});
&(#param)
}
});
}

let col_count = stmt.columns().len();
Expand Down Expand Up @@ -294,13 +294,13 @@ pub fn sql(item: TokenStream) -> TokenStream {
}

let enum_struct = if is_array {
quote! { Vec<::sqlm_postgres::types::Enum<(#(#enum_variants,)*)>> }
quote! { ::sqlm_postgres::types::Array<Vec<::sqlm_postgres::types::Enum<(#(#enum_variants,)*)>>> }
} else {
quote! { ::sqlm_postgres::types::Enum<(#(#enum_variants,)*)> }
quote! { ::sqlm_postgres::types::Primitive<::sqlm_postgres::types::Enum<(#(#enum_variants,)*)>> }
};
return quote! {
{
::sqlm_postgres::Sql::<'_, ::sqlm_postgres::types::Primitive<#enum_struct>, _> {
::sqlm_postgres::Sql::<'_, #enum_struct, _> {
query: #result,
parameters: &[#(&(#typed_parameters),)*],
transaction: None,
Expand All @@ -310,19 +310,34 @@ pub fn sql(item: TokenStream) -> TokenStream {
}
}
.into();
} else if let Some((ty, _)) = postgres_to_rust_type(ty) {
return quote! {
{
::sqlm_postgres::Sql::<'_, ::sqlm_postgres::types::Primitive<#ty>, _> {
query: #result,
parameters: &[#(&(#typed_parameters),)*],
transaction: None,
connection: None,
marker: ::std::marker::PhantomData,
} else if let Some((ty, _, is_array)) = postgres_to_rust_type(ty) {
if is_array {
return quote! {
{
::sqlm_postgres::Sql::<'_, ::sqlm_postgres::types::Array<#ty>, _> {
query: #result,
parameters: &[#(&(#typed_parameters),)*],
transaction: None,
connection: None,
marker: ::std::marker::PhantomData,
}
}
}
.into();
} else {
return quote! {
{
::sqlm_postgres::Sql::<'_, ::sqlm_postgres::types::Primitive<#ty>, _> {
query: #result,
parameters: &[#(&(#typed_parameters),)*],
transaction: None,
connection: None,
marker: ::std::marker::PhantomData,
}
}
}
.into();
}
.into();
} else {
return syn::Error::new(
input.query.span(),
Expand Down Expand Up @@ -352,7 +367,7 @@ pub fn sql(item: TokenStream) -> TokenStream {
} else {
struct_columns.push(parse_quote!(::sqlm_postgres::types::StructColumn<::sqlm_postgres::types::Enum<(#(#enum_variants,)*)>, #name>));
}
} else if let Some((ty, _)) = postgres_to_rust_type(ty) {
} else if let Some((ty, _, _)) = postgres_to_rust_type(ty) {
struct_columns.push(parse_quote!(::sqlm_postgres::types::StructColumn<#ty, #name>));
} else {
return syn::Error::new(
Expand Down Expand Up @@ -381,7 +396,7 @@ pub fn sql(item: TokenStream) -> TokenStream {

fn postgres_to_rust_type(
ty: &postgres::types::Type,
) -> Option<(proc_macro2::TokenStream, proc_macro2::TokenStream)> {
) -> Option<(proc_macro2::TokenStream, proc_macro2::TokenStream, bool)> {
use postgres::types::{FromSql, Kind};

if let Kind::Array(ty) = ty.kind() {
Expand All @@ -390,48 +405,57 @@ fn postgres_to_rust_type(
return None;
}

return postgres_to_rust_type(ty).map(|(ty, _)| (quote!(Vec<#ty>), quote!([#ty])));
return postgres_to_rust_type(ty).map(|(ty, _, _)| (quote!(Vec<#ty>), quote!([#ty]), true));
}

match ty {
ty if <String as FromSql>::accepts(ty) => Some((quote!(String), quote!(str))),
ty if <i64 as FromSql>::accepts(ty) => Some((quote!(i64), quote!(i64))),
ty if <i32 as FromSql>::accepts(ty) => Some((quote!(i32), quote!(i32))),
ty if <f64 as FromSql>::accepts(ty) => Some((quote!(f64), quote!(f64))),
ty if <f32 as FromSql>::accepts(ty) => Some((quote!(f32), quote!(f32))),
ty if <bool as FromSql>::accepts(ty) => Some((quote!(bool), quote!(bool))),
ty if <Vec<u8> as FromSql>::accepts(ty) => Some((quote!(Vec<u8>), quote!([u8]))),
ty if <String as FromSql>::accepts(ty) => Some((quote!(String), quote!(str), false)),
ty if <i64 as FromSql>::accepts(ty) => Some((quote!(i64), quote!(i64), false)),
ty if <i32 as FromSql>::accepts(ty) => Some((quote!(i32), quote!(i32), false)),
ty if <f64 as FromSql>::accepts(ty) => Some((quote!(f64), quote!(f64), false)),
ty if <f32 as FromSql>::accepts(ty) => Some((quote!(f32), quote!(f32), false)),
ty if <bool as FromSql>::accepts(ty) => Some((quote!(bool), quote!(bool), false)),
ty if <Vec<u8> as FromSql>::accepts(ty) => Some((
quote!(::sqlm_postgres::types::Bytea),
quote!(::sqlm_postgres::types::Bytea),
false,
)),

// serde_json::Value
#[cfg(feature = "json")]
ty if <::serde_json::Value as FromSql>::accepts(ty) => {
Some((quote!(::serde_json::Value), quote!(::serde_json::Value)))
}
ty if <::serde_json::Value as FromSql>::accepts(ty) => Some((
quote!(::serde_json::Value),
quote!(::serde_json::Value),
false,
)),

// time::Date
#[cfg(feature = "time")]
ty if <::time::Date as FromSql>::accepts(ty) => {
Some((quote!(::time::Date), quote!(::time::Date)))
Some((quote!(::time::Date), quote!(::time::Date), false))
}

// time::OffsetDateTime
#[cfg(feature = "time")]
ty if <::time::OffsetDateTime as FromSql>::accepts(ty) => Some((
quote!(::time::OffsetDateTime),
quote!(::time::OffsetDateTime),
false,
)),

// uuid::Uuid
#[cfg(feature = "uuid")]
ty if <::uuid::Uuid as FromSql>::accepts(ty) => {
Some((quote!(::uuid::Uuid), quote!(::uuid::Uuid)))
Some((quote!(::uuid::Uuid), quote!(::uuid::Uuid), false))
}

// pgvector::Vector
#[cfg(feature = "pgvector")]
ty if <::pgvector::Vector as FromSql>::accepts(ty) => {
Some((quote!(::pgvector::Vector), quote!(::pgvector::Vector)))
}
ty if <::pgvector::Vector as FromSql>::accepts(ty) => Some((
quote!(::pgvector::Vector),
quote!(::pgvector::Vector),
false,
)),

// Unsupported
_ => None,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
error[E0271]: type mismatch resolving `<Role as SqlType>::Type == Vec<Enum<(EnumVariant<"admin">, EnumVariant<"user">)>>`
error[E0277]: the trait bound `Role: Query<sqlm_postgres::types::Array<Vec<sqlm_postgres::types::Enum<(EnumVariant<"admin">, EnumVariant<"user">)>>>>` is not satisfied
--> tests/fail-nightly/enum_array_to_enum_literal.rs:17:10
|
17 | .await
| ^^^^^ expected `Enum<(EnumVariant<"admin">, ...)>`, found `Vec<Enum<(..., ...)>>`
| -^^^^^
| ||
| |the trait `Query<sqlm_postgres::types::Array<Vec<sqlm_postgres::types::Enum<(EnumVariant<"admin">, EnumVariant<"user">)>>>>` is not implemented for `Role`, which is required by `Sql<'_, sqlm_postgres::types::Array<Vec<sqlm_postgres::types::Enum<(EnumVariant<"admin">, EnumVariant<"user">)>>>, _>: IntoFuture`
| help: remove the `.await`
|
= note: expected struct `sqlm_postgres::types::Enum<_>`
found struct `Vec<sqlm_postgres::types::Enum<_>>`
= note: required for `Sql<'_, Primitive<Vec<sqlm_postgres::types::Enum<(EnumVariant<"admin">, EnumVariant<"user">)>>>, Role>` to implement `IntoFuture`
= help: the following other types implement trait `Query<Cols>`:
<() as Query<()>>
<Option<T> as Query<Primitive<<T as SqlType>::Type>>>
<Option<T> as Query<Struct<Cols>>>
<Option<Vec<u8>> as Query<Primitive<Bytea>>>
<Vec<T> as Query<Primitive<<T as SqlType>::Type>>>
<Vec<T> as Query<Struct<Cols>>>
<Vec<T> as Query<sqlm_postgres::types::Array<Vec<<T as SqlType>::Type>>>>
<Vec<Vec<u8>> as Query<Primitive<Bytea>>>
and $N others
= note: required for `Sql<'_, sqlm_postgres::types::Array<Vec<sqlm_postgres::types::Enum<(EnumVariant<"admin">, EnumVariant<"user">)>>>, Role>` to implement `IntoFuture`
10 changes: 8 additions & 2 deletions postgres-macros/tests/fail-nightly/enum_extra_variant.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ error[E0277]: the trait bound `Vec<User>: Query<Struct<(StructColumn<i64, "id">,
| |the trait `Query<Struct<(StructColumn<i64, "id">, StructColumn<sqlm_postgres::types::Enum<(EnumVariant<"admin">, EnumVariant<"user">)>, "role">)>>` is not implemented for `Vec<User>`, which is required by `Sql<'_, Struct<(StructColumn<i64, "id">, StructColumn<sqlm_postgres::types::Enum<(EnumVariant<"admin">, EnumVariant<"user">)>, "role">)>, _>: IntoFuture`
| help: remove the `.await`
|
= help: the trait `Query<Struct<(StructColumn<i64, "id">, StructColumn<sqlm_postgres::types::Enum<(EnumVariant<"admin">, EnumVariant<"moderator">, EnumVariant<"user">)>, "role">)>>` is implemented for `Vec<User>`
= help: the following other types implement trait `Query<Cols>`:
<Vec<T> as Query<Primitive<<T as SqlType>::Type>>>
<Vec<T> as Query<Struct<Cols>>>
<Vec<T> as Query<sqlm_postgres::types::Array<Vec<<T as SqlType>::Type>>>>
<Vec<Vec<u8>> as Query<Primitive<Bytea>>>
<Vec<Vec<u8>> as Query<sqlm_postgres::types::Array<Vec<Bytea>>>>
<Vec<u8> as Query<Primitive<Bytea>>>
= note: required for `Sql<'_, Struct<(StructColumn<i64, "id">, StructColumn<Enum<(EnumVariant<"admin">, ...)>, "role">)>, ...>` to implement `IntoFuture`
= note: the full name for the type has been written to '$WORKSPACE/target/tests/trybuild/aarch64-apple-darwin/debug/deps/$CRATE-f72977ab11c336b2.long-type-477280797366746876.txt'
= note: the full name for the type has been written to '$WORKSPACE/target/tests/trybuild/aarch64-apple-darwin/debug/deps/$CRATE-4ffcc67694e213f1.long-type-11745197285905521998.txt'
= note: consider using `--verbose` to print the full type name to the console
10 changes: 8 additions & 2 deletions postgres-macros/tests/fail-nightly/enum_missing_variant.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ error[E0277]: the trait bound `Vec<User>: Query<Struct<(StructColumn<i64, "id">,
| |the trait `Query<Struct<(StructColumn<i64, "id">, StructColumn<sqlm_postgres::types::Enum<(EnumVariant<"admin">, EnumVariant<"user">)>, "role">)>>` is not implemented for `Vec<User>`, which is required by `Sql<'_, Struct<(StructColumn<i64, "id">, StructColumn<sqlm_postgres::types::Enum<(EnumVariant<"admin">, EnumVariant<"user">)>, "role">)>, _>: IntoFuture`
| help: remove the `.await`
|
= help: the trait `Query<Struct<(StructColumn<i64, "id">, StructColumn<sqlm_postgres::types::Enum<(EnumVariant<"user">,)>, "role">)>>` is implemented for `Vec<User>`
= help: the following other types implement trait `Query<Cols>`:
<Vec<T> as Query<Primitive<<T as SqlType>::Type>>>
<Vec<T> as Query<Struct<Cols>>>
<Vec<T> as Query<sqlm_postgres::types::Array<Vec<<T as SqlType>::Type>>>>
<Vec<Vec<u8>> as Query<Primitive<Bytea>>>
<Vec<Vec<u8>> as Query<sqlm_postgres::types::Array<Vec<Bytea>>>>
<Vec<u8> as Query<Primitive<Bytea>>>
= note: required for `Sql<'_, Struct<(StructColumn<i64, "id">, StructColumn<Enum<(EnumVariant<"admin">, ...)>, "role">)>, ...>` to implement `IntoFuture`
= note: the full name for the type has been written to '$WORKSPACE/target/tests/trybuild/aarch64-apple-darwin/debug/deps/$CRATE-fdec792f290b819a.long-type-8833358810865802699.txt'
= note: the full name for the type has been written to '$WORKSPACE/target/tests/trybuild/aarch64-apple-darwin/debug/deps/$CRATE-6f9d778168733bb2.long-type-11135974624108269593.txt'
= note: consider using `--verbose` to print the full type name to the console
10 changes: 8 additions & 2 deletions postgres-macros/tests/fail-nightly/enum_variant_mismatch.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ error[E0277]: the trait bound `Vec<User>: Query<Struct<(StructColumn<i64, "id">,
| |the trait `Query<Struct<(StructColumn<i64, "id">, StructColumn<sqlm_postgres::types::Enum<(EnumVariant<"admin">, EnumVariant<"user">)>, "role">)>>` is not implemented for `Vec<User>`, which is required by `Sql<'_, Struct<(StructColumn<i64, "id">, StructColumn<sqlm_postgres::types::Enum<(EnumVariant<"admin">, EnumVariant<"user">)>, "role">)>, _>: IntoFuture`
| help: remove the `.await`
|
= help: the trait `Query<Struct<(StructColumn<i64, "id">, StructColumn<sqlm_postgres::types::Enum<(EnumVariant<"moderator">, EnumVariant<"user">)>, "role">)>>` is implemented for `Vec<User>`
= help: the following other types implement trait `Query<Cols>`:
<Vec<T> as Query<Primitive<<T as SqlType>::Type>>>
<Vec<T> as Query<Struct<Cols>>>
<Vec<T> as Query<sqlm_postgres::types::Array<Vec<<T as SqlType>::Type>>>>
<Vec<Vec<u8>> as Query<Primitive<Bytea>>>
<Vec<Vec<u8>> as Query<sqlm_postgres::types::Array<Vec<Bytea>>>>
<Vec<u8> as Query<Primitive<Bytea>>>
= note: required for `Sql<'_, Struct<(StructColumn<i64, "id">, StructColumn<Enum<(EnumVariant<"admin">, ...)>, "role">)>, ...>` to implement `IntoFuture`
= note: the full name for the type has been written to '$WORKSPACE/target/tests/trybuild/aarch64-apple-darwin/debug/deps/$CRATE-c84c4e8ec0844e24.long-type-17999743074283884113.txt'
= note: the full name for the type has been written to '$WORKSPACE/target/tests/trybuild/aarch64-apple-darwin/debug/deps/$CRATE-108a3d1f17e7e28e.long-type-6590895828837905940.txt'
= note: consider using `--verbose` to print the full type name to the console
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
error[E0271]: type mismatch resolving `<Role as SqlType>::Type == Vec<Enum<(EnumVariant<5755620910692865178>, EnumVariant<10465144470622129318>)>>`
error[E0277]: the trait bound `Role: Query<sqlm_postgres::types::Array<Vec<sqlm_postgres::types::Enum<(EnumVariant<5755620910692865178>, EnumVariant<10465144470622129318>)>>>>` is not satisfied
--> tests/fail-stable/enum_array_to_enum_literal.rs:17:10
|
17 | .await
| ^^^^^ expected `Enum<(..., ...)>`, found `Vec<Enum<(..., ...)>>`
| -^^^^^
| ||
| |the trait `Query<sqlm_postgres::types::Array<Vec<sqlm_postgres::types::Enum<(EnumVariant<5755620910692865178>, EnumVariant<10465144470622129318>)>>>>` is not implemented for `Role`, which is required by `Sql<'_, sqlm_postgres::types::Array<Vec<sqlm_postgres::types::Enum<(EnumVariant<5755620910692865178>, EnumVariant<10465144470622129318>)>>>, _>: IntoFuture`
| help: remove the `.await`
|
= note: expected struct `sqlm_postgres::types::Enum<_>`
found struct `Vec<sqlm_postgres::types::Enum<_>>`
= note: required for `Sql<'_, Primitive<Vec<sqlm_postgres::types::Enum<(EnumVariant<5755620910692865178>, EnumVariant<10465144470622129318>)>>>, Role>` to implement `IntoFuture`
= help: the following other types implement trait `Query<Cols>`:
<Vec<u8> as Query<Primitive<Bytea>>>
<Vec<Vec<u8>> as Query<Primitive<Bytea>>>
<Vec<Vec<u8>> as Query<sqlm_postgres::types::Array<Vec<Bytea>>>>
<Vec<T> as Query<Struct<Cols>>>
<Vec<T> as Query<Primitive<<T as SqlType>::Type>>>
<Vec<T> as Query<sqlm_postgres::types::Array<Vec<<T as SqlType>::Type>>>>
<Option<Vec<u8>> as Query<Primitive<Bytea>>>
<Option<T> as Query<Struct<Cols>>>
and $N others
= note: required for `Sql<'_, Array<Vec<Enum<(EnumVariant<5755620910692865178>, EnumVariant<10465144470622129318>)>>>, Role>` to implement `IntoFuture`
= note: the full name for the type has been written to '$WORKSPACE/target/tests/trybuild/aarch64-apple-darwin/debug/deps/$CRATE-4bc7215b1b514162.long-type-14335032845333733167.txt'
= note: consider using `--verbose` to print the full type name to the console
Loading

0 comments on commit fc2360a

Please sign in to comment.