From 3ad8f0881b7e7857ffc5305aefe9a75fdff05c99 Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Mon, 9 Nov 2020 14:18:15 +0100 Subject: [PATCH 1/3] WIP --- Cargo.toml | 2 - wundergraph/src/diesel_ext.rs | 261 ++++++++++++++++-- wundergraph/src/graphql_type.rs | 28 +- wundergraph/src/helper/primary_keys.rs | 43 +-- wundergraph/src/juniper_ext/nameable.rs | 2 +- wundergraph/src/lib.rs | 2 +- wundergraph/src/macros/mutation.rs | 235 ++++++++++++---- wundergraph/src/macros/query.rs | 7 +- .../src/query_builder/mutations/delete.rs | 8 +- .../src/query_builder/mutations/insert/mod.rs | 21 +- .../src/query_builder/mutations/update.rs | 8 +- .../selection/fields/associations.rs | 114 +++++--- .../selection/fields/field_list.rs | 21 +- .../query_builder/selection/fields/helper.rs | 67 ++++- .../src/query_builder/selection/fields/mod.rs | 2 + .../selection/filter/common_filter/mod.rs | 3 +- .../src/query_builder/selection/filter/mod.rs | 2 +- .../src/query_builder/selection/mod.rs | 6 +- .../src/query_builder/selection/order.rs | 52 ++-- .../query_builder/selection/query_resolver.rs | 4 +- .../src/query_builder/selection/select.rs | 25 +- .../src/query_builder/types/as_input_type.rs | 66 +++++ .../field_value_resolver/direct_resolver.rs | 65 ++++- .../field_value_resolver/has_one_resolver.rs | 33 ++- .../types/field_value_resolver/mod.rs | 19 +- .../src/query_builder/types/has_many.rs | 11 +- .../src/query_builder/types/has_one.rs | 33 +-- wundergraph/src/query_builder/types/mod.rs | 10 +- .../src/query_builder/types/placeholder.rs | 31 ++- .../query_builder/types/wundergraph_value.rs | 49 ++-- .../src/third_party_integrations/chrono.rs | 29 +- .../src/third_party_integrations/uuid.rs | 11 +- wundergraph_derive/src/wundergraph_entity.rs | 4 + wundergraph_derive/src/wundergraph_value.rs | 18 +- 34 files changed, 990 insertions(+), 302 deletions(-) create mode 100644 wundergraph/src/query_builder/types/as_input_type.rs diff --git a/Cargo.toml b/Cargo.toml index 77d6be9..f5db473 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,5 +7,3 @@ members = [ "wundergraph_bench", ] -[patch.crates-io] -#wundergraph_derive = { path = "wundergraph_derive"} diff --git a/wundergraph/src/diesel_ext.rs b/wundergraph/src/diesel_ext.rs index 19f31a0..ac6fcd6 100644 --- a/wundergraph/src/diesel_ext.rs +++ b/wundergraph/src/diesel_ext.rs @@ -1,10 +1,17 @@ //! A module containing extension traits for various diesel types +use std::marker::PhantomData; + use diesel::backend::Backend; use diesel::expression::{AppearsOnTable, Expression, NonAggregate, SelectableExpression}; -use diesel::query_builder::{AstPass, QueryFragment, QueryId}; +use diesel::query_builder::BindCollector; +use diesel::query_builder::QueryBuilder; +use diesel::query_builder::{AstPass, QueryFragment}; use diesel::result::QueryResult; +use diesel::sql_types; use diesel::sql_types::IntoNullable; +use diesel::types::HasSqlType; +use diesel::types::TypeMetadata; /// A helper trait used when boxing filters /// @@ -38,11 +45,25 @@ where /// A diesel helper type that indicates if null or some expression selected #[derive(Debug)] -pub enum MaybeNull { - /// Select the expression - Expr(T), - /// Select a null value - Null, +pub struct MaybeNull { + expr: PhantomData, + as_null: bool, +} + +impl MaybeNull { + pub fn expr() -> Self { + Self { + expr: PhantomData, + as_null: false, + } + } + + pub fn as_null() -> Self { + Self { + expr: PhantomData, + as_null: true, + } + } } impl Expression for MaybeNull @@ -56,27 +77,231 @@ where impl QueryFragment for MaybeNull where DB: Backend, - T: QueryFragment, + T: QueryFragment + QueryFragment> + Default, + DB::QueryBuilder: Default, { fn walk_ast(&self, mut pass: AstPass<'_, DB>) -> QueryResult<()> { - match self { - MaybeNull::Expr(e) => e.walk_ast(pass)?, - MaybeNull::Null => pass.push_sql(" NULL "), + let expr = T::default(); + if self.as_null { + let mut query_builder = MaybeNullQueryBuilder::new(DB::QueryBuilder::default(), true); + let ast_pass = AstPass::>::to_sql(&mut query_builder); + expr.walk_ast(ast_pass)?; + let identifier_pushed = query_builder.identifier_pushed; + debug_assert!(identifier_pushed % 2 == 0); + + for i in 0..(identifier_pushed / 2) { + if i != 0 { + pass.push_sql(", "); + } + pass.push_sql("NULL"); + } + pass.push_sql(" "); + } else { + expr.walk_ast(pass)?; } Ok(()) } } -impl QueryId for MaybeNull -where - ST: QueryId, -{ - type QueryId = (); - const HAS_STATIC_QUERY_ID: bool = false; -} - impl NonAggregate for MaybeNull {} impl AppearsOnTable for MaybeNull where Self: Expression {} impl SelectableExpression for MaybeNull where Self: Expression {} + +pub(crate) use self::fake_query_builder::FakeBackend; +use self::fake_query_builder::MaybeNullQueryBuilder; + +mod fake_query_builder { + use super::*; + + #[derive(Debug)] + pub struct MaybeNullQueryBuilder { + inner: Q, + generate_nulls: bool, + pub(super) identifier_pushed: usize, + } + + impl MaybeNullQueryBuilder { + pub fn new(inner: Q, generate_nulls: bool) -> Self { + Self { + inner, + generate_nulls, + identifier_pushed: 0, + } + } + } + + impl QueryBuilder> for MaybeNullQueryBuilder + where + Q: QueryBuilder, + DB: Backend, + { + fn push_sql(&mut self, sql: &str) { + self.inner.push_sql(sql) + } + + fn push_identifier(&mut self, identifier: &str) -> QueryResult<()> { + if self.generate_nulls { + self.identifier_pushed += 1; + Ok(()) + } else { + self.inner.push_identifier(identifier) + } + } + + fn push_bind_param(&mut self) { + self.inner.push_bind_param(); + } + + fn finish(self) -> String { + self.inner.finish() + } + } + + #[derive(Debug)] + pub struct FakeBackend(DB); + + impl Backend for FakeBackend + where + DB: Backend, + { + type QueryBuilder = MaybeNullQueryBuilder; + + type BindCollector = FakeBindCollector; + + type RawValue = DB::RawValue; + + type ByteOrder = DB::ByteOrder; + } + + #[derive(Debug)] + pub struct FakeBindCollector(B); + + impl BindCollector> for FakeBindCollector + where + B: BindCollector, + DB: Backend, + { + fn push_bound_value( + &mut self, + _bind: &U, + _metadata_lookup: & as TypeMetadata>::MetadataLookup, + ) -> QueryResult<()> + where + FakeBackend: HasSqlType, + U: diesel::types::ToSql>, + { + unimplemented!() + // self.0.push_bound_value(bind, metadata_lookup) + } + } + + impl TypeMetadata for FakeBackend + where + DB: TypeMetadata, + { + type TypeMetadata = DB::TypeMetadata; + + type MetadataLookup = DB::MetadataLookup; + } + + impl HasSqlType for FakeBackend + where + DB: HasSqlType, + { + fn metadata(lookup: &Self::MetadataLookup) -> Self::TypeMetadata { + DB::metadata(lookup) + } + } + + impl HasSqlType for FakeBackend + where + DB: HasSqlType, + { + fn metadata(lookup: &Self::MetadataLookup) -> Self::TypeMetadata { + DB::metadata(lookup) + } + } + + impl HasSqlType for FakeBackend + where + DB: HasSqlType, + { + fn metadata(lookup: &Self::MetadataLookup) -> Self::TypeMetadata { + DB::metadata(lookup) + } + } + + impl HasSqlType for FakeBackend + where + DB: HasSqlType, + { + fn metadata(lookup: &Self::MetadataLookup) -> Self::TypeMetadata { + DB::metadata(lookup) + } + } + + impl HasSqlType for FakeBackend + where + DB: HasSqlType, + { + fn metadata(lookup: &Self::MetadataLookup) -> Self::TypeMetadata { + DB::metadata(lookup) + } + } + + impl HasSqlType for FakeBackend + where + DB: HasSqlType, + { + fn metadata(lookup: &Self::MetadataLookup) -> Self::TypeMetadata { + DB::metadata(lookup) + } + } + + impl HasSqlType for FakeBackend + where + DB: HasSqlType, + { + fn metadata(lookup: &Self::MetadataLookup) -> Self::TypeMetadata { + DB::metadata(lookup) + } + } + + impl HasSqlType for FakeBackend + where + DB: HasSqlType, + { + fn metadata(lookup: &Self::MetadataLookup) -> Self::TypeMetadata { + DB::metadata(lookup) + } + } + + impl HasSqlType for FakeBackend + where + DB: HasSqlType, + { + fn metadata(lookup: &Self::MetadataLookup) -> Self::TypeMetadata { + DB::metadata(lookup) + } + } + + impl HasSqlType for FakeBackend + where + DB: HasSqlType, + { + fn metadata(lookup: &Self::MetadataLookup) -> Self::TypeMetadata { + DB::metadata(lookup) + } + } + + impl HasSqlType for FakeBackend + where + DB: HasSqlType, + { + fn metadata(lookup: &Self::MetadataLookup) -> Self::TypeMetadata { + DB::metadata(lookup) + } + } +} diff --git a/wundergraph/src/graphql_type.rs b/wundergraph/src/graphql_type.rs index d8a3ebc..0c8cdb0 100644 --- a/wundergraph/src/graphql_type.rs +++ b/wundergraph/src/graphql_type.rs @@ -1,5 +1,7 @@ -use crate::query_builder::selection::offset::ApplyOffset; use crate::query_builder::selection::LoadingHandler; +use crate::query_builder::{ + selection::offset::ApplyOffset, types::field_value_resolver::DirectResolveable, +}; use crate::scalar::WundergraphScalarValue; use diesel::backend::Backend; use diesel::query_builder::QueryFragment; @@ -44,7 +46,7 @@ where #[doc(hidden)] pub trait WundergraphGraphqlMapper { - type GraphQLType: GraphQLType; + type GraphQLType: GraphQLType; fn register_arguments<'r>( _registry: &mut Registry<'r, WundergraphScalarValue>, @@ -52,13 +54,20 @@ pub trait WundergraphGraphqlMapper { ) -> meta::Field<'r, WundergraphScalarValue> { field } + + fn type_info() -> >::TypeInfo; } impl WundergraphGraphqlMapper for T where - T: GraphQLType, + T: GraphQLType + DirectResolveable, + T::TypeInfo: Default, { type GraphQLType = Self; + + fn type_info() -> >::TypeInfo { + Default::default() + } } #[doc(hidden)] @@ -68,7 +77,8 @@ pub trait WundergraphGraphqlHelper { registry: &mut Registry<'r, WundergraphScalarValue>, ) -> meta::MetaType<'r, WundergraphScalarValue> where - T: GraphQLType; + T: GraphQLType, + T::TypeInfo: Default; } macro_rules! wundergraph_graphql_helper_impl { @@ -90,11 +100,15 @@ macro_rules! wundergraph_graphql_helper_impl { names: &[&str], registry: &mut Registry<'r, WundergraphScalarValue>, ) -> meta::MetaType<'r, WundergraphScalarValue> - where Type: GraphQLType + where Type: GraphQLType, + Type::TypeInfo: Default { let fields = [ $({ - let mut field = registry.field::<<$T as WundergraphGraphqlMapper>::GraphQLType>(names[$idx], &()); + let mut field = registry.field::<<$T as WundergraphGraphqlMapper>::GraphQLType>( + names[$idx], + &$T::type_info(), + ); field = <$T as WundergraphGraphqlMapper>::register_arguments(registry, field); if let Some(doc) = Loading::field_description($idx) { field = field.description(doc); @@ -106,7 +120,7 @@ macro_rules! wundergraph_graphql_helper_impl { },)* ]; let mut ty = registry.build_object_type::( - &(), + &Type::TypeInfo::default(), &fields, ); if let Some(doc) = Loading::TYPE_DESCRIPTION { diff --git a/wundergraph/src/helper/primary_keys.rs b/wundergraph/src/helper/primary_keys.rs index 00bd5d1..dbc3216 100644 --- a/wundergraph/src/helper/primary_keys.rs +++ b/wundergraph/src/helper/primary_keys.rs @@ -1,4 +1,5 @@ use crate::juniper_ext::FromLookAheadValue; +use crate::query_builder::types::as_input_type::AsInputType; use crate::scalar::WundergraphScalarValue; use diesel::associations::HasTable; use diesel::query_builder::nodes::Identifier; @@ -84,10 +85,9 @@ macro_rules! unref_impl { __diesel_for_each_tuple!(unref_impl); #[doc(hidden)] -pub trait PrimaryKeyInputObject { +pub trait PrimaryKeyInputObject { fn register<'r>( registry: &mut Registry<'r, WundergraphScalarValue>, - info: &I, ) -> Vec>; fn from_input_value(value: &InputValue) -> Option; @@ -95,19 +95,19 @@ pub trait PrimaryKeyInputObject { fn to_input_value(values: &V) -> InputValue; } -impl PrimaryKeyInputObject for A +impl PrimaryKeyInputObject for A where A: Column, - V1: GraphQLType + V1: GraphQLType + FromInputValue + ToInputValue + FromLookAheadValue, + V1::TypeInfo: Default, { fn register<'r>( registry: &mut Registry<'r, WundergraphScalarValue>, - info: &I, ) -> Vec> { - vec![registry.arg::(Self::NAME, info)] + vec![registry.arg::(Self::NAME, &Default::default())] } fn from_input_value(value: &InputValue) -> Option { @@ -144,18 +144,17 @@ macro_rules! primary_key_input_object_impl { } )+) => { $( - impl<$($T,)+ $($ST,)+ __I> PrimaryKeyInputObject<($($ST,)+), __I> for ($($T,)+) + impl<$($T,)+ $($ST,)+ > PrimaryKeyInputObject<($($ST,)+)> for ($($T,)+) where $($T: Column,)+ - $($T: PrimaryKeyInputObject<$ST, __I>,)+ + $($T: PrimaryKeyInputObject<$ST>,)+ { fn register<'r>( registry: &mut Registry<'r, WundergraphScalarValue>, - info: &__I ) -> Vec> { let mut ret = Vec::new(); $( - ret.extend($T::register(registry, info)); + ret.extend($T::register(registry)); )* ret } @@ -269,15 +268,17 @@ fn uppercase_first_letter(s: Cow<'_, str>) -> Cow<'_, str> { pub struct PrimaryKeyArgument<'a, T, Ctx, V> where V: UnRef<'a>, + V::UnRefed: AsInputType, { - pub(crate) values: V::UnRefed, + pub(crate) values: ::InputType, _marker: PhantomData<(&'a T, Ctx)>, } impl<'a, T, Ctx, V> Debug for PrimaryKeyArgument<'a, T, Ctx, V> where V: UnRef<'a>, - V::UnRefed: Debug, + V::UnRefed: AsInputType, + ::InputType: Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("PrimaryKeyArgument") @@ -289,8 +290,9 @@ where impl<'a, T, Ctx, V> GraphQLType for PrimaryKeyArgument<'a, T, Ctx, V> where T: Table + 'a, - T::PrimaryKey: PrimaryKeyInputObject, + T::PrimaryKey: PrimaryKeyInputObject<::InputType>, V: UnRef<'a>, + V::UnRefed: AsInputType, { type Context = Ctx; type TypeInfo = PrimaryKeyInfo; @@ -306,7 +308,7 @@ where where WundergraphScalarValue: 'r, { - let fields = T::PrimaryKey::register(registry, &()); + let fields = T::PrimaryKey::register(registry); registry .build_input_object_type::(info, &fields) .into_meta() @@ -316,8 +318,9 @@ where impl<'a, T, Ctx, V> ToInputValue for PrimaryKeyArgument<'a, T, Ctx, V> where T: Table, - T::PrimaryKey: PrimaryKeyInputObject, + T::PrimaryKey: PrimaryKeyInputObject<::InputType>, V: UnRef<'a>, + V::UnRefed: AsInputType, { fn to_input_value(&self) -> InputValue { T::PrimaryKey::to_input_value(&self.values) @@ -327,8 +330,9 @@ where impl<'a, T, Ctx, V> FromInputValue for PrimaryKeyArgument<'a, T, Ctx, V> where T: Table, - T::PrimaryKey: PrimaryKeyInputObject, + T::PrimaryKey: PrimaryKeyInputObject<::InputType>, V: UnRef<'a>, + V::UnRefed: AsInputType, { fn from_input_value(value: &InputValue) -> Option { T::PrimaryKey::from_input_value(value).map(|values| Self { @@ -341,8 +345,9 @@ where impl<'a, T, Ctx, V> FromLookAheadValue for PrimaryKeyArgument<'a, T, Ctx, V> where T: Table, - T::PrimaryKey: PrimaryKeyInputObject, + T::PrimaryKey: PrimaryKeyInputObject<::InputType>, V: UnRef<'a>, + V::UnRefed: AsInputType, { fn from_look_ahead(v: &LookAheadValue<'_, WundergraphScalarValue>) -> Option { T::PrimaryKey::from_look_ahead(v).map(|values| Self { @@ -356,6 +361,7 @@ impl<'a, T, Ctx, V> HasTable for PrimaryKeyArgument<'a, T, Ctx, V> where T: Table + HasTable, V: UnRef<'a>, + V::UnRefed: AsInputType, { type Table = T; @@ -368,10 +374,11 @@ impl<'a, T, Ctx, V> Identifiable for &'a PrimaryKeyArgument<'a, T, Ctx, V> where Self: HasTable, V: UnRef<'a> + Hash + Eq, + V::UnRefed: AsInputType, { type Id = V; fn id(self) -> Self::Id { - V::as_ref(&self.values) + todo!() } } diff --git a/wundergraph/src/juniper_ext/nameable.rs b/wundergraph/src/juniper_ext/nameable.rs index e952a97..1d5ba28 100644 --- a/wundergraph/src/juniper_ext/nameable.rs +++ b/wundergraph/src/juniper_ext/nameable.rs @@ -104,6 +104,6 @@ where impl Nameable for () { fn name() -> String { - String::new() + String::from("Void") } } diff --git a/wundergraph/src/lib.rs b/wundergraph/src/lib.rs index 8625c88..4125019 100644 --- a/wundergraph/src/lib.rs +++ b/wundergraph/src/lib.rs @@ -72,7 +72,7 @@ #![deny(missing_debug_implementations, missing_copy_implementations)] #![warn( - missing_docs, +// missing_docs, clippy::option_unwrap_used, clippy::result_unwrap_used, clippy::print_stdout, diff --git a/wundergraph/src/macros/mutation.rs b/wundergraph/src/macros/mutation.rs index 1448277..c5642e3 100644 --- a/wundergraph/src/macros/mutation.rs +++ b/wundergraph/src/macros/mutation.rs @@ -54,7 +54,7 @@ macro_rules! __expand_resolve_delete { }; ($entity_name: ident, $executor: ident, $arguments: ident, $($delete:tt)*) => { $crate::query_builder::mutations::handle_delete::< - DB, + _, $($delete)*, $entity_name, Self::Context, @@ -72,7 +72,7 @@ macro_rules! __expand_register_insert { ($entity_name: ident, $registry: ident, $fields: ident, $info: ident, $($insert:tt)*) => {{ let new = $registry.arg::<$($insert)*>(concat!("New", stringify!($entity_name)), $info); let new = $registry - .field::>>( + .field::>::GraphQLType>>( concat!("Create", stringify!($entity_name)), $info, ) @@ -81,7 +81,7 @@ macro_rules! __expand_register_insert { let new = $registry.arg::>(concat!("New", stringify!($entity_name), "s"), $info); let new = $registry - .field::>>( + .field::>::GraphQLType>>( concat!("Create", stringify!($entity_name), "s"), $info, ) @@ -132,7 +132,7 @@ macro_rules! __expand_resolve_insert { ) => { if $tpe == concat!("Create", stringify!($entity_name)) { $crate::query_builder::mutations::handle_insert::< - DB, + _, $($insert)*, $entity_name, Self::Context, @@ -144,7 +144,7 @@ macro_rules! __expand_resolve_insert { ) } else { $crate::query_builder::mutations::handle_batch_insert::< - DB, + _, $($insert)*, $entity_name, Self::Context, @@ -168,7 +168,7 @@ macro_rules! __expand_register_update { ($entity_name: ident, $registry: ident, $fields: ident, $info: ident, $($update:tt)*) => {{ let update = $registry.arg::<$($update)*>(concat!("Update", stringify!($entity_name)), $info); let update = $registry - .field::>>( + .field::>::GraphQLType>>( concat!("Update", stringify!($entity_name)), $info, ) @@ -207,7 +207,7 @@ macro_rules! __expand_resolve_update { $selection: expr, $($update:tt)* ) => { - $crate::query_builder::mutations::handle_update::( + $crate::query_builder::mutations::handle_update::<_, $($update)*, $entity_name, Self::Context>( $selection, $executor, $arguments, @@ -229,7 +229,8 @@ macro_rules! __build_mutation_trait_bounds { $(lt = $lt: tt,)? body = { $($inner: tt)* - } + }, + db = [$db: ty], ) => { $crate::paste::item! { $crate::__build_mutation_trait_bounds! { @@ -250,6 +251,7 @@ macro_rules! __build_mutation_trait_bounds { } ], additional_bound = [], + db = [$db], } } }; @@ -264,6 +266,7 @@ macro_rules! __build_mutation_trait_bounds { }, original = [ $($orig: tt)* ], additional_bound = [$({$($bounds:tt)*},)*], + db = [$db:ty], ) => { $crate::__build_mutation_trait_bounds! { input = { @@ -272,6 +275,7 @@ macro_rules! __build_mutation_trait_bounds { }, original = [ $($orig)*], additional_bound = [$({$($bounds)*},)*], + db = [$db], } }; ( @@ -285,6 +289,7 @@ macro_rules! __build_mutation_trait_bounds { }, original = [ $($orig: tt)* ], additional_bound = [$({$($bounds:tt)*},)*], + db = [$db: ty], ) => { $crate::__build_mutation_trait_bounds! { input = { @@ -295,12 +300,13 @@ macro_rules! __build_mutation_trait_bounds { additional_bound = [ $({$($bounds)*},)* { - $($table)*: $crate::query_builder::mutations::HandleInsert<$entity_name, $insert, DB, Ctx> + $($table)*: $crate::query_builder::mutations::HandleInsert<$entity_name, $insert, $db, Ctx> }, { - $($table)*: $crate::query_builder::mutations::HandleBatchInsert<$entity_name, $insert, DB, Ctx> + $($table)*: $crate::query_builder::mutations::HandleBatchInsert<$entity_name, $insert, $db, Ctx> }, ], + db = [$db], } }; ( @@ -314,6 +320,7 @@ macro_rules! __build_mutation_trait_bounds { }, original = [ $($orig: tt)* ], additional_bound = [$({$($bounds:tt)*},)*], + db = [$db: ty], ) => { $crate::__build_mutation_trait_bounds! { input = { @@ -322,6 +329,7 @@ macro_rules! __build_mutation_trait_bounds { }, original = [ $($orig)*], additional_bound = [$({$($bounds)*},)*], + db = [$db], } }; ( @@ -335,6 +343,7 @@ macro_rules! __build_mutation_trait_bounds { }, original = [ $($orig: tt)* ], additional_bound = [$({$($bounds:tt)*},)*], + db = [$db: ty], ) => { $crate::__build_mutation_trait_bounds! { input = { @@ -345,9 +354,10 @@ macro_rules! __build_mutation_trait_bounds { additional_bound = [ $({$($bounds)*},)* { - $($table)*: $crate::query_builder::mutations::HandleUpdate<$entity_name, $update, DB, Ctx> + $($table)*: $crate::query_builder::mutations::HandleUpdate<$entity_name, $update, $db, Ctx> }, ], + db = [$db], } }; ( @@ -357,6 +367,7 @@ macro_rules! __build_mutation_trait_bounds { }, original = [ $($orig: tt)*], additional_bound = [$({$($bounds:tt)*},)*], + db = [$db: ty], ) => { $crate::__build_mutation_trait_bounds! { input = { @@ -365,6 +376,7 @@ macro_rules! __build_mutation_trait_bounds { }, original = [$($orig)*], additional_bound = [$({$($bounds)*},)*], + db = [$db], } }; ( @@ -374,6 +386,7 @@ macro_rules! __build_mutation_trait_bounds { }, original = [ $($orig: tt)*], additional_bound = [$({$($bounds:tt)*},)*], + db = [$db:ty], ) => { $crate::__build_mutation_trait_bounds! { input = { @@ -390,6 +403,7 @@ macro_rules! __build_mutation_trait_bounds { }, original = [$($orig)*], additional_bound = [$({$($bounds)*},)*], + db = [$db], } }; ( @@ -399,6 +413,7 @@ macro_rules! __build_mutation_trait_bounds { }, original = [ $($orig: tt)*], additional_bound = [$({$($bounds:tt)*},)*], + db = [$db: ty], ) => { $crate::__build_mutation_trait_bounds! { input = { @@ -406,6 +421,7 @@ macro_rules! __build_mutation_trait_bounds { }, original = [$($orig)*], additional_bound = [$({$($bounds)*},)*], + db = [$db], } }; ( @@ -415,6 +431,7 @@ macro_rules! __build_mutation_trait_bounds { }, original = [ $($orig: tt)*], additional_bound = [$({$($bounds:tt)*},)*], + db = [$db: ty], ) => { $crate::__build_mutation_trait_bounds! { input = { @@ -423,9 +440,10 @@ macro_rules! __build_mutation_trait_bounds { original = [$($orig)*], additional_bound = [$({$($bounds)*},)* { - $($table)*: $crate::query_builder::mutations::HandleDelete<$entity_name, $($delete)*, DB, Ctx> + $($table)*: $crate::query_builder::mutations::HandleDelete<$entity_name, $($delete)*, $db, Ctx> }, ], + db = [$db], } }; ( @@ -441,6 +459,7 @@ macro_rules! __build_mutation_trait_bounds { } ], additional_bound = [$({$($bounds:tt)*},)*], + db = [$db: ty], ) => { $crate::__impl_graphql_obj_for_mutation! { mutation_name = {$($mutation_name)*}, @@ -451,11 +470,122 @@ macro_rules! __build_mutation_trait_bounds { $(lt = $lt,)? body = { $($inner)* - } + }, + db = [$db], } }; } +#[macro_export] +#[doc(hidden)] +#[cfg(feature = "postgres")] +macro_rules! __impl_graphql_obj_for_mutation_and_db { + ( + mutation_name = {$($mutation_name:tt)*}, + structs = [$($entity_name: ident( + $(insert = $insert: ident,)? + $(update = $update: ident,)? + $(delete = $($delete:tt)*)? + ),)*], + $(lt = $lt: tt,)? + body = { + $($inner: tt)* + } + ) => { + $crate::__impl_graphql_obj_for_mutation! { + mutation_name = {$($mutation_name)*}, + structs = [$($entity_name ( + $(insert = $insert,)? + $(update = $update,)? + $(delete = $($delete)*)? + ),)*], + $(lt = $lt,)? + body = { + $($inner)* + }, + db = [$crate::diesel::pg::Pg], + } + } +} + +#[macro_export] +#[doc(hidden)] +#[cfg(feature = "sqlite")] +macro_rules! __impl_graphql_obj_for_mutation_and_db { + ( + mutation_name = {$($mutation_name:tt)*}, + structs = [$($entity_name: ident( + $(insert = $insert: ident,)? + $(update = $update: ident,)? + $(delete = $($delete:tt)*)? + ),)*], + $(lt = $lt: tt,)? + body = { + $($inner: tt)* + } + ) => { + $crate::__impl_graphql_obj_for_mutation! { + mutation_name = {$($mutation_name)*}, + structs = [$($entity_name ( + $(insert = $insert,)? + $(update = $update,)? + $(delete = $($delete)*)? + ),)*], + $(lt = $lt,)? + body = { + $($inner)* + }, + db = [$crate::diesel::sqlite::Sqlite], + } + } +} + +#[macro_export] +#[doc(hidden)] +#[cfg(all(feature = "postgres", feature = "sqlite"))] +macro_rules! __impl_graphql_obj_for_mutation_and_db { + ( + mutation_name = {$($mutation_name:tt)*}, + structs = [$($entity_name: ident( + $(insert = $insert: ident,)? + $(update = $update: ident,)? + $(delete = $($delete:tt)*)? + ),)*], + $(lt = $lt: tt,)? + body = { + $($inner: tt)* + } + ) => { + $crate::__impl_graphql_obj_for_mutation! { + mutation_name = {$($mutation_name)*}, + structs = [$($entity_name ( + $(insert = $insert,)? + $(update = $update,)? + $(delete = $($delete)*)? + ),)*], + $(lt = $lt,)? + body = { + $($inner)* + }, + db = [$crate::diesel::pg::Pg], + } + + $crate::__impl_graphql_obj_for_mutation! { + mutation_name = {$($mutation_name)*}, + structs = [$($entity_name ( + $(insert = $insert,)? + $(update = $update,)? + $(delete = $($delete)*)? + ),)*], + $(lt = $lt,)? + body = { + $($inner)* + }, + db = [$crate::diesel::sqlite::Sqlite], + } + } +} + #[doc(hidden)] #[macro_export] macro_rules! __impl_graphql_obj_for_mutation { @@ -470,6 +600,32 @@ macro_rules! __impl_graphql_obj_for_mutation { body = { $($inner: tt)* } + ) => { + $crate::__impl_graphql_obj_for_mutation_and_db! { + mutation_name = {$($mutation_name)*}, + structs = [$($entity_name ( + $(insert = $insert,)? + $(update = $update,)? + $(delete = $($delete)*)? + ),)*], + $(lt = $lt,)? + body = { + $($inner)* + } + } + }; + ( + mutation_name = {$($mutation_name:tt)*}, + structs = [$($entity_name: ident( + $(insert = $insert: ident,)? + $(update = $update: ident,)? + $(delete = $($delete:tt)*)? + ),)*], + $(lt = $lt: tt,)? + body = { + $($inner: tt)* + }, + db = [$db: ty], ) => { $crate::__build_mutation_trait_bounds! { mutation_name = {$($mutation_name)*}, @@ -479,7 +635,8 @@ macro_rules! __impl_graphql_obj_for_mutation { $(lt = $lt,)? body = { $($inner)* - } + }, + db = [$db], } }; ( @@ -489,45 +646,23 @@ macro_rules! __impl_graphql_obj_for_mutation { $(lt = $lt: tt,)? body = { $($inner: tt)* - } + }, + db = [$db: ty], ) => { $crate::paste::item! { - impl<$($lt,)? Ctx, DB, $([<$entity_name _table>],)* $([<$entity_name _id>],)*> $crate::juniper::GraphQLType<$crate::scalar::WundergraphScalarValue> + impl<$($lt,)? Ctx, $([<$entity_name _table>],)*> $crate::juniper::GraphQLType<$crate::scalar::WundergraphScalarValue> for $($mutation_name)*<$($lt,)? Ctx> - where Ctx: $crate::WundergraphContext, - DB: $crate::diesel::backend::Backend + $crate::query_builder::selection::offset::ApplyOffset + 'static, - DB::QueryBuilder: std::default::Default, - Ctx::Connection: $crate::diesel::Connection, - $($entity_name: $crate::query_builder::selection::LoadingHandler + $crate::diesel::associations::HasTable
]>,)* - $([<$entity_name _table>]: $crate::diesel::Table + 'static + - $crate::diesel::QuerySource + $crate::diesel::Table + $crate::diesel::associations::HasTable
]>,)* - $([<$entity_name _table>]::FromClause: $crate::helper::NamedTable + $crate::diesel::query_builder::QueryFragment,)* - $(<$entity_name as $crate::query_builder::selection::LoadingHandler>::Columns: $crate::query_builder::selection::order::BuildOrder<[<$entity_name _table>], DB>,)* - $(<$entity_name as $crate::query_builder::selection::LoadingHandler>::Columns: $crate::query_builder::selection::select::BuildSelect< - [<$entity_name _table>], - DB, - $crate::query_builder::selection::SqlTypeOfPlaceholder< - <$entity_name as $crate::query_builder::selection::LoadingHandler>::FieldList, - DB, - <$entity_name as $crate::query_builder::selection::LoadingHandler>::PrimaryKeyIndex, - [<$entity_name _table>], - Ctx, - > - >,)* - $(<$entity_name as $crate::query_builder::selection::LoadingHandler>::FieldList: $crate::query_builder::selection::fields::WundergraphFieldList< - DB, - <$entity_name as $crate::query_builder::selection::LoadingHandler>::PrimaryKeyIndex, - [<$entity_name _table>], - Ctx - >,)* - $(<$entity_name as $crate::query_builder::selection::LoadingHandler>::FieldList: - $crate::graphql_type::WundergraphGraphqlHelper<$entity_name, DB, Ctx> + - $crate::query_builder::selection::fields::FieldListExtractor,)* - $(&'static $entity_name: $crate::diesel::Identifiable]>,)* - $([<$entity_name _id>]: std::hash::Hash + std::cmp::Eq + $crate::helper::UnRef<'static>,)* - $([<$entity_name _table>]::PrimaryKey: $crate::helper::PrimaryKeyInputObject< - <[<$entity_name _id>] as $crate::helper::UnRef<'static>>::UnRefed, () - >,)* + where Ctx: $crate::WundergraphContext + 'static, + Ctx::Connection: $crate::diesel::Connection, + $($entity_name: $crate::diesel::associations::HasTable
]>,)* + $($entity_name: $crate::graphql_type::WundergraphGraphqlMapper<$db, Ctx>, )* + $(<$entity_name as $crate::graphql_type::WundergraphGraphqlMapper<$db, Ctx>>::GraphQLType: $crate::juniper::GraphQLType<$crate::scalar::WundergraphScalarValue, TypeInfo = (), Context = ()>,)* + $([<$entity_name _table>]: $crate::diesel::Table + + 'static + + $crate::diesel::QuerySource + + $crate::diesel::associations::HasTable
]>,)* + $([<$entity_name _table>]::FromClause: $crate::helper::NamedTable + $crate::diesel::query_builder::QueryFragment<$db>,)* + $([<$entity_name _table>]::AllColumns: $crate::diesel::SelectableExpression<[<$entity_name _table>]>,)* $($($bounds)*,)* { diff --git a/wundergraph/src/macros/query.rs b/wundergraph/src/macros/query.rs index d9e4b62..4b9d11e 100644 --- a/wundergraph/src/macros/query.rs +++ b/wundergraph/src/macros/query.rs @@ -261,11 +261,12 @@ macro_rules! __impl_graphql_obj_for_query { >>,)* $(&'static $graphql_struct: $crate::diesel::Identifiable]>,)* $([<$graphql_struct _id>]: std::hash::Hash + std::cmp::Eq + $crate::helper::UnRef<'static>,)* + $(<[<$graphql_struct _id>] as $crate::helper::UnRef<'static>>::UnRefed: $crate::query_builder::types::AsInputType,)* $([<$graphql_struct _table>]::PrimaryKey: std::default::Default + $crate::helper::PrimaryKeyInputObject< - <[<$graphql_struct _id>] as $crate::helper::UnRef<'static>>::UnRefed, () + <<[<$graphql_struct _id>] as $crate::helper::UnRef<'static>>::UnRefed as $crate::query_builder::types::AsInputType>::InputType >,)* - $([<$graphql_struct _table>]::PrimaryKey: $crate::diesel::EqAll<<[<$graphql_struct _id>] as $crate::helper::UnRef<'static>>::UnRefed>,)* - $(<[<$graphql_struct _table>]::PrimaryKey as $crate::diesel::EqAll<<[<$graphql_struct _id>] as $crate::helper::UnRef<'static>>::UnRefed>>::Output: $crate::diesel::AppearsOnTable<[<$graphql_struct _table>]> + $crate::diesel::query_builder::QueryFragment + $crate::diesel::expression::NonAggregate,)* + $([<$graphql_struct _table>]::PrimaryKey: $crate::diesel::EqAll<<<[<$graphql_struct _id>] as $crate::helper::UnRef<'static>>::UnRefed as $crate::query_builder::types::AsInputType>::InputType>,)* + $(<[<$graphql_struct _table>]::PrimaryKey as $crate::diesel::EqAll<<<[<$graphql_struct _id>] as $crate::helper::UnRef<'static>>::UnRefed as $crate::query_builder::types::AsInputType>::InputType>>::Output: $crate::diesel::AppearsOnTable<[<$graphql_struct _table>]> + $crate::diesel::query_builder::QueryFragment + $crate::diesel::expression::NonAggregate,)* $(<<$graphql_struct as $crate::query_builder::selection::LoadingHandler>::Filter as $crate::query_builder::selection::filter::BuildFilter>::Ret: $crate::diesel::AppearsOnTable<[<$graphql_struct _table>]>,)* $(<<$graphql_struct as $crate::query_builder::selection::LoadingHandler>::FieldList as $crate::query_builder::selection::fields::FieldListExtractor>::Out: $crate::graphql_type::WundergraphGraphqlHelper<$graphql_struct, DB, Ctx> + diff --git a/wundergraph/src/query_builder/mutations/delete.rs b/wundergraph/src/query_builder/mutations/delete.rs index a9c734b..a16f1f4 100644 --- a/wundergraph/src/query_builder/mutations/delete.rs +++ b/wundergraph/src/query_builder/mutations/delete.rs @@ -31,16 +31,10 @@ pub fn handle_delete( field_name: &'static str, ) -> ExecutionResult where - R: LoadingHandler, + R: HasTable, R::Table: HandleDelete + 'static, DB: Backend + ApplyOffset + 'static, DB::QueryBuilder: Default, - R::Columns: BuildOrder - + BuildSelect< - R::Table, - DB, - SqlTypeOfPlaceholder, - >, ::FromClause: QueryFragment, D: FromInputValue, { diff --git a/wundergraph/src/query_builder/mutations/insert/mod.rs b/wundergraph/src/query_builder/mutations/insert/mod.rs index 22bd47c..f60562a 100644 --- a/wundergraph/src/query_builder/mutations/insert/mod.rs +++ b/wundergraph/src/query_builder/mutations/insert/mod.rs @@ -1,9 +1,6 @@ use crate::query_builder::selection::offset::ApplyOffset; -use crate::query_builder::selection::order::BuildOrder; -use crate::query_builder::selection::select::BuildSelect; -use crate::query_builder::selection::LoadingHandler; -use crate::query_builder::selection::SqlTypeOfPlaceholder; use crate::scalar::WundergraphScalarValue; +use diesel::associations::HasTable; use diesel::backend::Backend; use diesel::query_builder::QueryFragment; use diesel::QuerySource; @@ -23,16 +20,10 @@ pub fn handle_insert( field_name: &'static str, ) -> ExecutionResult where - R: LoadingHandler, + R: HasTable, R::Table: HandleInsert + 'static, DB: Backend + ApplyOffset + 'static, DB::QueryBuilder: Default, - R::Columns: BuildOrder - + BuildSelect< - R::Table, - DB, - SqlTypeOfPlaceholder, - >, ::FromClause: QueryFragment, I: FromInputValue, { @@ -52,16 +43,10 @@ pub fn handle_batch_insert( field_name: &'static str, ) -> ExecutionResult where - R: LoadingHandler, + R: HasTable, R::Table: HandleBatchInsert + 'static, DB: Backend + ApplyOffset + 'static, DB::QueryBuilder: Default, - R::Columns: BuildOrder - + BuildSelect< - R::Table, - DB, - SqlTypeOfPlaceholder, - >, ::FromClause: QueryFragment, I: FromInputValue, { diff --git a/wundergraph/src/query_builder/mutations/update.rs b/wundergraph/src/query_builder/mutations/update.rs index fb116ba..b4dd7c4 100644 --- a/wundergraph/src/query_builder/mutations/update.rs +++ b/wundergraph/src/query_builder/mutations/update.rs @@ -26,16 +26,10 @@ pub fn handle_update( field_name: &'static str, ) -> ExecutionResult where - R: LoadingHandler, + R: HasTable, R::Table: HandleUpdate + 'static, DB: Backend + ApplyOffset + 'static, DB::QueryBuilder: Default, - R::Columns: BuildOrder - + BuildSelect< - R::Table, - DB, - SqlTypeOfPlaceholder, - >, ::FromClause: QueryFragment, U: FromInputValue, { diff --git a/wundergraph/src/query_builder/selection/fields/associations.rs b/wundergraph/src/query_builder/selection/fields/associations.rs index 140ff26..b77903c 100644 --- a/wundergraph/src/query_builder/selection/fields/associations.rs +++ b/wundergraph/src/query_builder/selection/fields/associations.rs @@ -14,20 +14,65 @@ use juniper::{Executor, LookAheadMethods, Selection}; use std::collections::HashMap; use std::hash::Hash; +pub trait AssociationsLookup { + fn new() -> Self; + + fn insert( + &mut self, + key: Option, + len: usize, + values: Vec>, + ); + + fn get( + &self, + key: &Option, + ) -> Option>)>>; +} + +impl AssociationsLookup + for HashMap, Vec<(usize, Vec>)>> +where + K: Eq + Hash, +{ + fn new() -> Self { + HashMap::new() + } + + fn insert( + &mut self, + key: Option, + len: usize, + values: Vec>, + ) { + self.entry(key).or_insert_with(Vec::new).push((len, values)); + } + + fn get( + &self, + key: &Option, + ) -> Option>)>> { + self.get(key).cloned() + } +} + #[doc(hidden)] #[derive(Debug)] -pub struct AssociationsReturn<'a, K: Eq + Hash> { +pub struct AssociationsReturn<'a, K, C> { keys: Vec>, fields: Vec<&'a str>, - values: HashMap, Vec<(usize, Vec>)>>, + values: C, } -impl<'a, K: Eq + Hash> AssociationsReturn<'a, K> { +impl<'a, K, C> AssociationsReturn<'a, K, C> +where + C: AssociationsLookup, +{ fn empty() -> Self { Self { keys: Vec::new(), fields: Vec::new(), - values: HashMap::new(), + values: C::new(), } } @@ -58,7 +103,7 @@ impl<'a, K: Eq + Hash> AssociationsReturn<'a, K> { self.fields.push(alias); for (k, v) in values { - self.values.entry(k).or_insert_with(Vec::new).push((len, v)); + self.values.insert(k, len, v); } Ok(()) } @@ -80,27 +125,23 @@ impl<'a, K: Eq + Hash> AssociationsReturn<'a, K> { .map(|(mut obj, key)| { let values = values.get(&key); if let Some(values) = values { - let mut value_iter = values.iter().peekable(); for (idx, field_name) in fields.iter().enumerate() { - match value_iter.peek() { - Some((field_idx, _)) if idx == *field_idx => { - let value = value_iter - .next() - .expect("It's there because peekable") - .1 - .clone(); - obj.add_field( - field_name.to_owned(), - juniper::Value::List(value), - ); - } - None | Some(_) => { - obj.add_field( - field_name.to_owned(), - juniper::Value::List(Vec::new()), - ); - } - } + let vals = values + .iter() + .filter_map( + |(field_idx, val)| { + if idx == *field_idx { + Some(val) + } else { + None + } + }, + ) + .cloned() + .flatten() + .collect::>(); + + obj.add_field(field_name.to_owned(), juniper::Value::List(vals)); } } else { for f in &fields { @@ -118,9 +159,10 @@ impl<'a, K: Eq + Hash> AssociationsReturn<'a, K> { #[doc(hidden)] pub trait WundergraphResolveAssociations where - K: Eq + Hash, DB: Backend, { + type Container: AssociationsLookup; + fn resolve<'a>( global_args: &'a [juniper::LookAheadArgument], look_ahead: &'a juniper::LookAheadSelection<'a, WundergraphScalarValue>, @@ -128,7 +170,7 @@ where get_name: impl Fn(usize) -> &'static str, get_keys: impl Fn() -> Vec>, executor: &'a Executor<'a, Ctx, WundergraphScalarValue>, - ) -> Result>; + ) -> Result>; } impl WundergraphResolveAssociations for () @@ -136,6 +178,8 @@ where K: Eq + Hash, DB: Backend, { + type Container = HashMap, Vec<(usize, Vec>)>>; + fn resolve<'a>( _global_args: &'a [juniper::LookAheadArgument], _look_ahead: &'a juniper::LookAheadSelection<'a, WundergraphScalarValue>, @@ -143,13 +187,15 @@ where _get_name: impl Fn(usize) -> &'static str, _get_keys: impl Fn() -> Vec>, _executor: &'a Executor<'a, Ctx, WundergraphScalarValue>, - ) -> Result> { + ) -> Result> { Ok(AssociationsReturn::empty()) } } #[doc(hidden)] pub trait WundergraphResolveAssociation { + type Container: AssociationsLookup; + fn resolve( global_args: &[juniper::LookAheadArgument], look_ahead: &juniper::LookAheadSelection<'_, WundergraphScalarValue>, @@ -354,6 +400,8 @@ where ::FromClause: QueryFragment, DB::QueryBuilder: Default, { + type Container = HashMap, Vec<(usize, Vec>)>>; + fn resolve( global_args: &[juniper::LookAheadArgument], look_ahead: &juniper::LookAheadSelection<'_, WundergraphScalarValue>, @@ -372,12 +420,14 @@ macro_rules! wundergraph_impl_resolve_association { } )+) => { $( - impl WundergraphResolveAssociations for ($($T,)*) + impl WundergraphResolveAssociations for ($($T,)*) where Back: Backend, - Key: Eq + Hash, - $($T: WundergraphResolveAssociation,)* + Container: AssociationsLookup, + $($T: WundergraphResolveAssociation,)* { + type Container = Container; + fn resolve<'a>( global_args: &[juniper::LookAheadArgument], look_ahead: &'a juniper::LookAheadSelection<'a, WundergraphScalarValue>, @@ -385,7 +435,7 @@ macro_rules! wundergraph_impl_resolve_association { get_name: impl Fn(usize) -> &'static str, get_keys: impl Fn() -> Vec>, executor: &'a Executor<'a, Ctx, WundergraphScalarValue>, - ) -> Result> + ) -> Result> { let mut ret = AssociationsReturn::empty(); $( diff --git a/wundergraph/src/query_builder/selection/fields/field_list.rs b/wundergraph/src/query_builder/selection/fields/field_list.rs index b8d5590..fb46d5d 100644 --- a/wundergraph/src/query_builder/selection/fields/field_list.rs +++ b/wundergraph/src/query_builder/selection/fields/field_list.rs @@ -4,12 +4,11 @@ use crate::error::Result; use crate::helper::tuple::TupleIndex; use crate::query_builder::selection::query_resolver::WundergraphResolvePlaceHolderList; use crate::query_builder::types::placeholder::PlaceHolderMarker; -use crate::query_builder::types::WundergraphValue; +use crate::query_builder::types::WundergraphSqlValue; use crate::scalar::WundergraphScalarValue; use diesel::backend::Backend; use diesel::{Connection, Queryable}; use juniper::{Executor, Selection}; -use std::hash::Hash; /// A internal trait pub trait WundergraphFieldList { @@ -60,19 +59,19 @@ macro_rules! wundergraph_impl_field_list { impl WundergraphFieldList for ($($T,)*) where Back: Backend, ($($T,)*): FieldListExtractor + NonTableFieldExtractor, - <($($T,)*) as FieldListExtractor>::Out: WundergraphValue, - <<($($T,)*) as FieldListExtractor>::Out as WundergraphValue>::PlaceHolder: TupleIndex + - Queryable<<<($($T,)*) as FieldListExtractor>::Out as WundergraphValue>::SqlType, Back> + 'static, - Vec<<<($($T,)*) as FieldListExtractor>::Out as WundergraphValue>::PlaceHolder>: + <($($T,)*) as FieldListExtractor>::Out: WundergraphSqlValue, + <<($($T,)*) as FieldListExtractor>::Out as WundergraphSqlValue>::PlaceHolder: TupleIndex + + Queryable<<<($($T,)*) as FieldListExtractor>::Out as WundergraphSqlValue>::SqlType, Back> + 'static, + Vec<<<($($T,)*) as FieldListExtractor>::Out as WundergraphSqlValue>::PlaceHolder>: WundergraphResolvePlaceHolderList<<($($T,)*) as FieldListExtractor>::Out, Back, Ctx>, - <<<($($T,)*) as FieldListExtractor>::Out as WundergraphValue>::PlaceHolder as TupleIndex>::Value: PlaceHolderMarker, - <<<<($($T,)*) as FieldListExtractor>::Out as WundergraphValue>::PlaceHolder as TupleIndex>::Value as PlaceHolderMarker>::InnerType: Eq + Hash + Clone, - <($($T,)*) as NonTableFieldExtractor>::Out: WundergraphResolveAssociations<<<<<($($T,)*) as FieldListExtractor>::Out as WundergraphValue>::PlaceHolder as TupleIndex>::Value as PlaceHolderMarker>::InnerType, Table, Back, Ctx>, + <<<($($T,)*) as FieldListExtractor>::Out as WundergraphSqlValue>::PlaceHolder as TupleIndex>::Value: PlaceHolderMarker, + <<<<($($T,)*) as FieldListExtractor>::Out as WundergraphSqlValue>::PlaceHolder as TupleIndex>::Value as PlaceHolderMarker>::InnerType: Clone, + <($($T,)*) as NonTableFieldExtractor>::Out: WundergraphResolveAssociations<<<<<($($T,)*) as FieldListExtractor>::Out as WundergraphSqlValue>::PlaceHolder as TupleIndex>::Value as PlaceHolderMarker>::InnerType, Table, Back, Ctx>, Ctx: WundergraphContext, Ctx::Connection: Connection, { - type PlaceHolder = <<($($T,)*) as FieldListExtractor>::Out as WundergraphValue>::PlaceHolder; - type SqlType = <<($($T,)*) as FieldListExtractor>::Out as WundergraphValue>::SqlType; + type PlaceHolder = <<($($T,)*) as FieldListExtractor>::Out as WundergraphSqlValue>::PlaceHolder; + type SqlType = <<($($T,)*) as FieldListExtractor>::Out as WundergraphSqlValue>::SqlType; const TABLE_FIELD_COUNT: usize = <($($T,)*) as FieldListExtractor>::FIELD_COUNT; const NON_TABLE_FIELD_COUNT: usize = <($($T,)*) as NonTableFieldExtractor>::FIELD_COUNT; diff --git a/wundergraph/src/query_builder/selection/fields/helper.rs b/wundergraph/src/query_builder/selection/fields/helper.rs index a7ea87d..ce926cf 100644 --- a/wundergraph/src/query_builder/selection/fields/helper.rs +++ b/wundergraph/src/query_builder/selection/fields/helper.rs @@ -1,5 +1,7 @@ use crate::helper::tuple::AppendToTuple; -use crate::query_builder::types::{HasMany, WundergraphValue}; +use crate::query_builder::types::{ + AssociatedValue, HasMany, TableField, WundergraphSqlValue, WundergraphValue, +}; /// A helper trait to collect extracted graphql fields which represents a /// database value @@ -91,7 +93,7 @@ impl NonTableFieldExtractor for () { impl TableFieldCollector for () where - T: WundergraphValue, + T: WundergraphSqlValue, { type Out = (T,); @@ -118,7 +120,7 @@ impl TableFieldCollector> for () { impl NonTableFieldCollector for () where - T: WundergraphValue, + T: WundergraphSqlValue, { type Out = (); @@ -198,6 +200,9 @@ macro_rules! wundergraph_impl_field_extractor { }; } +#[allow(missing_debug_implementations)] +pub struct FieldCollectorHelper(std::marker::PhantomData<(T, N, A)>); + macro_rules! wundergraph_impl_field_extractors { ($( $Tuple:tt { @@ -207,8 +212,22 @@ macro_rules! wundergraph_impl_field_extractors { $( wundergraph_impl_field_extractor!($($T,)*); - impl<$($T,)* Next> TableFieldCollector for ($($T,)*) - where Next: WundergraphValue, + impl<$($T,)+ Next> TableFieldCollector for ($($T,)*) + where + Next: WundergraphValue, + FieldCollectorHelper<($($T,)*), Next, Next::ValueType>: TableFieldCollector { + type Out = as TableFieldCollector>::Out; + + const FIELD_COUNT: usize = as TableFieldCollector>::FIELD_COUNT; + + fn map Ret, Ret>(local_index: usize, callback: Func) -> Option + { + as TableFieldCollector>::map(local_index, callback) + } + } + + impl<$($T,)* Next> TableFieldCollector for FieldCollectorHelper<($($T,)*), Next, TableField> + where Next: WundergraphValue, ($($T,)*): FieldListExtractor, <($($T,)*) as FieldListExtractor>::Out: AppendToTuple, { @@ -225,8 +244,10 @@ macro_rules! wundergraph_impl_field_extractors { } } - impl<$($T,)* Next, ForeignKey> TableFieldCollector> for ($($T,)*) - where ($($T,)*): FieldListExtractor, + impl<$($T,)* Next> TableFieldCollector for FieldCollectorHelper<($($T,)*), Next, AssociatedValue> + where + Next: WundergraphValue, + ($($T,)*): FieldListExtractor, { type Out = <($($T,)*) as FieldListExtractor>::Out; @@ -237,8 +258,22 @@ macro_rules! wundergraph_impl_field_extractors { } } - impl<$($T,)* Next> NonTableFieldCollector for ($($T,)*) - where Next: WundergraphValue, + impl<$($T,)+ Next> NonTableFieldCollector for ($($T,)*) + where + Next: WundergraphValue, + FieldCollectorHelper<($($T,)*), Next, Next::ValueType>: NonTableFieldCollector { + type Out = as NonTableFieldCollector>::Out; + + const FIELD_COUNT: usize = as NonTableFieldCollector>::FIELD_COUNT; + + fn map Ret, Ret>(local_index: usize, callback: Func) -> Option + { + as NonTableFieldCollector>::map(local_index, callback) + } + } + + impl<$($T,)* Next> NonTableFieldCollector for FieldCollectorHelper<($($T,)*), Next, TableField> + where Next: WundergraphValue, ($($T,)*): NonTableFieldExtractor, { type Out = <($($T,)*) as NonTableFieldExtractor>::Out; @@ -250,16 +285,18 @@ macro_rules! wundergraph_impl_field_extractors { } } - impl<$($T,)* Next, ForeignKey> NonTableFieldCollector> for ($($T,)*) - where ($($T,)*): NonTableFieldExtractor, - <($($T,)*) as NonTableFieldExtractor>::Out: AppendToTuple>, + impl<$($T,)* Next> NonTableFieldCollector for FieldCollectorHelper<($($T,)*), Next, AssociatedValue> + where + Next: WundergraphValue, + ($($T,)*): NonTableFieldExtractor, + <($($T,)*) as NonTableFieldExtractor>::Out: AppendToTuple, { - type Out = <<($($T,)*) as NonTableFieldExtractor>::Out as AppendToTuple>>::Out; + type Out = <<($($T,)*) as NonTableFieldExtractor>::Out as AppendToTuple>::Out; - const FIELD_COUNT: usize = <<($($T,)*) as NonTableFieldExtractor>::Out as AppendToTuple>>::LENGHT; + const FIELD_COUNT: usize = <<($($T,)*) as NonTableFieldExtractor>::Out as AppendToTuple>::LENGHT; fn map Ret, Ret>(local_index: usize, callback: Func) -> Option { - if local_index == <<($($T,)*) as NonTableFieldExtractor>::Out as AppendToTuple>>::LENGHT - 1 { + if local_index == <<($($T,)*) as NonTableFieldExtractor>::Out as AppendToTuple>::LENGHT - 1 { Some(callback(wundergraph_add_one_to_index!($($idx)*))) } else { <($($T,)*) as NonTableFieldExtractor>::map(local_index, callback) diff --git a/wundergraph/src/query_builder/selection/fields/mod.rs b/wundergraph/src/query_builder/selection/fields/mod.rs index 637d12c..396d00a 100644 --- a/wundergraph/src/query_builder/selection/fields/mod.rs +++ b/wundergraph/src/query_builder/selection/fields/mod.rs @@ -16,4 +16,6 @@ pub use self::field_list::WundergraphFieldList; #[doc(inline)] pub use wundergraph_derive::WundergraphBelongsTo; +pub use self::associations::AssociationsLookup; +pub use self::associations::WundergraphResolveAssociation; pub(crate) use self::associations::WundergraphResolveAssociations; diff --git a/wundergraph/src/query_builder/selection/filter/common_filter/mod.rs b/wundergraph/src/query_builder/selection/filter/common_filter/mod.rs index f5f7005..6ca50ef 100644 --- a/wundergraph/src/query_builder/selection/filter/common_filter/mod.rs +++ b/wundergraph/src/query_builder/selection/filter/common_filter/mod.rs @@ -62,12 +62,13 @@ where impl InnerFilter for FilterOption where - V: GraphQLType + V: GraphQLType + FromInputValue + ToInputValue + FromLookAheadValue + FilterValue + 'static, + V::TypeInfo: Default, Self: Nameable, V::AdditionalFilter: InnerFilter, { diff --git a/wundergraph/src/query_builder/selection/filter/mod.rs b/wundergraph/src/query_builder/selection/filter/mod.rs index 23be68b..1d0378b 100644 --- a/wundergraph/src/query_builder/selection/filter/mod.rs +++ b/wundergraph/src/query_builder/selection/filter/mod.rs @@ -28,7 +28,7 @@ pub(crate) mod filter_value; pub(crate) mod inner_filter; mod not; mod nullable_filter; -mod reference_filter; +pub mod reference_filter; mod string_filter; use self::collector::{AndCollector, FilterCollector, OrCollector}; diff --git a/wundergraph/src/query_builder/selection/mod.rs b/wundergraph/src/query_builder/selection/mod.rs index 94f9cd8..96a37d3 100644 --- a/wundergraph/src/query_builder/selection/mod.rs +++ b/wundergraph/src/query_builder/selection/mod.rs @@ -30,6 +30,7 @@ use crate::helper::{PrimaryKeyArgument, UnRef}; use crate::juniper_ext::FromLookAheadValue; use crate::query_builder::selection::order::BuildOrder; use crate::query_builder::selection::select::BuildSelect; +use crate::query_builder::types::AsInputType; use crate::scalar::WundergraphScalarValue; use diesel::associations::HasTable; use diesel::backend::Backend; @@ -240,10 +241,11 @@ where Ctx: WundergraphContext + QueryModifier, Ctx::Connection: Connection, <&'static Self as Identifiable>::Id: UnRef<'static>, + <<&'static Self as Identifiable>::Id as UnRef<'static>>::UnRefed: AsInputType, ::PrimaryKey: - EqAll<<<&'static Self as Identifiable>::Id as UnRef<'static>>::UnRefed> + Default, + EqAll<<<<&'static Self as Identifiable>::Id as UnRef<'static>>::UnRefed as AsInputType>::InputType> + Default, <::PrimaryKey as EqAll< - <<&'static Self as Identifiable>::Id as UnRef<'static>>::UnRefed, + <<<&'static Self as Identifiable>::Id as UnRef<'static>>::UnRefed as AsInputType>::InputType, >>::Output: AppearsOnTable + NonAggregate + QueryFragment, PrimaryKeyArgument<'static, Self::Table, (), <&'static Self as Identifiable>::Id>: FromLookAheadValue, diff --git a/wundergraph/src/query_builder/selection/order.rs b/wundergraph/src/query_builder/selection/order.rs index c13e66c..f469513 100644 --- a/wundergraph/src/query_builder/selection/order.rs +++ b/wundergraph/src/query_builder/selection/order.rs @@ -8,7 +8,7 @@ use crate::scalar::WundergraphScalarValue; use diesel::backend::Backend; use diesel::expression::NonAggregate; use diesel::query_builder::QueryFragment; -use diesel::{BoxableExpression, Column, ExpressionMethods, QuerySource, SelectableExpression}; +use diesel::{BoxableExpression, QuerySource, SelectableExpression}; use juniper::{ meta, FromInputValue, GraphQLEnum, GraphQLType, LookAheadValue, Registry, ToInputValue, }; @@ -203,42 +203,50 @@ macro_rules! impl_order_traits { impl BuildOrder for ($($T,)+) where Table: ::diesel::Table, DB: Backend, - $($T: Column
+ ExpressionMethods + Copy + Default + - SelectableExpression
+ NonAggregate + QueryFragment + 'static,)+ + $( + $T: //ExpressionMethods + + Copy + + Default + + SelectableExpression
+ + NonAggregate + + QueryFragment + + 'static, + )+ { fn build_order( fields: &[LookAheadValue<'_, WundergraphScalarValue>], - field_name: impl Fn(usize) -> &'static str, + _field_name: impl Fn(usize) -> &'static str, ) -> Result>>> { - let mut ret = Vec::with_capacity(fields.len()); + let ret = Vec::with_capacity(fields.len()); for f in fields { if let LookAheadValue::Object(o) = f { - let column = o.iter().find(|(k, _)| *k == "column") + let _column = o.iter().find(|(k, _)| *k == "column") .and_then(|(_, v)| if let LookAheadValue::Enum(c) = v { Some(c) } else { None }) .ok_or(WundergraphError::CouldNotBuildFilterArgument)?; - let order = o.iter().find(|(k, _)| *k == "direction") + let _order = o.iter().find(|(k, _)| *k == "direction") .and_then(|(_, v)| Order::from_look_ahead(v)) .unwrap_or(Order::Asc); - match *column { - $( - x if x == field_name($idx) => if order == Order::Desc { - ret.push(Box::new($T::default().desc()) - as Box>) - } else { - ret.push(Box::new($T::default().asc()) as Box<_>) - } - )+ - x => { - return Err(WundergraphError::UnknownDatabaseField { - name: x.to_owned() - }); - } - } + todo!() + // match *column { + // $( + // x if x == field_name($idx) => if order == Order::Desc { + // ret.push(Box::new($T::default().desc()) + // as Box>) + // } else { + // ret.push(Box::new($T::default().asc()) as Box<_>) + // } + // )+ + // x => { + // return Err(WundergraphError::UnknownDatabaseField { + // name: x.to_owned() + // }); + // } + // } } else { return Err( WundergraphError::CouldNotBuildFilterArgument diff --git a/wundergraph/src/query_builder/selection/query_resolver.rs b/wundergraph/src/query_builder/selection/query_resolver.rs index fb60397..ca53342 100644 --- a/wundergraph/src/query_builder/selection/query_resolver.rs +++ b/wundergraph/src/query_builder/selection/query_resolver.rs @@ -2,7 +2,7 @@ use crate::error::Result; use crate::query_builder::selection::fields::WundergraphFieldList; use crate::query_builder::types::field_value_resolver::FieldValueResolver; use crate::query_builder::types::placeholder::{PlaceHolder, PlaceHolderMarker}; -use crate::query_builder::types::{ResolveWundergraphFieldValue, WundergraphValue}; +use crate::query_builder::types::{ResolveWundergraphFieldValue, WundergraphSqlValue}; use crate::scalar::WundergraphScalarValue; use diesel::backend::Backend; use juniper::parser::SourcePosition; @@ -52,7 +52,7 @@ macro_rules! wundergraph_value_impl { $( #[allow(clippy::use_self)] impl WundergraphResolvePlaceHolderList<($($ST,)*), Back, Ctx> for Vec<($(PlaceHolder<$T>,)+)> - where $($ST: WundergraphValue> + + where $($ST: WundergraphSqlValue> + ResolveWundergraphFieldValue ,)* $($T: 'static,)* Back: Backend, diff --git a/wundergraph/src/query_builder/selection/select.rs b/wundergraph/src/query_builder/selection/select.rs index 0ee8632..c46cae7 100644 --- a/wundergraph/src/query_builder/selection/select.rs +++ b/wundergraph/src/query_builder/selection/select.rs @@ -4,7 +4,7 @@ use crate::scalar::WundergraphScalarValue; use diesel::backend::Backend; use diesel::expression::NonAggregate; use diesel::query_builder::QueryFragment; -use diesel::{BoxableExpression, Column, Expression, ExpressionMethods, SelectableExpression}; +use diesel::{BoxableExpression, Expression, SelectableExpression}; use juniper::LookAheadMethods; use juniper::LookAheadSelection; @@ -31,9 +31,16 @@ macro_rules! impl_select_builder { Table, DB, ($( as Expression>::SqlType,)+ ), > for ($($T,)+) where Table: ::diesel::Table, - DB: Backend, - $($T: Column
+ Default + ExpressionMethods + - SelectableExpression
+ NonAggregate + QueryFragment + 'static ,)+ + DB: Backend, + DB::QueryBuilder: Default, + $( + $T: Default + + SelectableExpression
+ + NonAggregate + + QueryFragment + + QueryFragment> + + 'static , + )+ $(MaybeNull<$T>: Expression,)+ { fn build_select( @@ -50,15 +57,15 @@ macro_rules! impl_select_builder { >> { Ok(Box::new(( - $( + $({ if select.has_child(get_field_name($idx)) || (is_primary_key_index($idx) && should_select_primary_key) { - MaybeNull::Expr($T::default()) + MaybeNull::<$T>::expr() } else { - MaybeNull::Null - }, - )+ + MaybeNull::<$T>::as_null() + } + },)+ )) as Box<_>) } } diff --git a/wundergraph/src/query_builder/types/as_input_type.rs b/wundergraph/src/query_builder/types/as_input_type.rs new file mode 100644 index 0000000..8959989 --- /dev/null +++ b/wundergraph/src/query_builder/types/as_input_type.rs @@ -0,0 +1,66 @@ +pub trait AsInputType: Sized { + type InputType; +} + +impl AsInputType for i16 { + type InputType = Self; +} + +impl AsInputType for i32 { + type InputType = Self; +} + +impl AsInputType for i64 { + type InputType = Self; +} + +impl AsInputType for f32 { + type InputType = Self; +} + +impl AsInputType for f64 { + type InputType = Self; +} + +impl AsInputType for bool { + type InputType = Self; +} + +impl AsInputType for String { + type InputType = Self; +} + +impl AsInputType for Vec +where + T: AsInputType, + Vec: Into, +{ + type InputType = Vec; +} + +impl AsInputType for Option +where + T: AsInputType, + Option: Into, +{ + type InputType = Option; +} + +macro_rules! impl_tuple_macro_wrapper { + ($( + $Tuple:tt { + $(($idx:tt) -> $T:ident, $ST: ident, $TT: ident,) + + } + )+) => { + $( + impl<$($T,)*> AsInputType for ($($T,)*) + where + $($T: AsInputType,)* + { + type InputType = ($($T::InputType,)*); + } + )* + } +} + +__diesel_for_each_tuple!(impl_tuple_macro_wrapper); diff --git a/wundergraph/src/query_builder/types/field_value_resolver/direct_resolver.rs b/wundergraph/src/query_builder/types/field_value_resolver/direct_resolver.rs index 8b4fe0a..313843e 100644 --- a/wundergraph/src/query_builder/types/field_value_resolver/direct_resolver.rs +++ b/wundergraph/src/query_builder/types/field_value_resolver/direct_resolver.rs @@ -1,7 +1,7 @@ -use super::{FieldValueResolver, ResolveWundergraphFieldValue}; +use super::{DirectResolveable, FieldValueResolver, ResolveWundergraphFieldValue}; use crate::error::Result; use crate::error::WundergraphError; -use crate::query_builder::types::WundergraphValue; +use crate::query_builder::types::WundergraphSqlValue; use crate::scalar::WundergraphScalarValue; use diesel::backend::Backend; use juniper::{Executor, FromContext, GraphQLType, Selection}; @@ -12,7 +12,8 @@ pub struct DirectResolver; impl FieldValueResolver for DirectResolver where DB: Backend, - T: GraphQLType + WundergraphValue, + T: GraphQLType + WundergraphSqlValue + DirectResolveable, + T::TypeInfo: Default, T::PlaceHolder: Into>, >::Context: FromContext, { @@ -29,7 +30,51 @@ where ) -> Result>> { Ok(Some( executor - .resolve_with_ctx(&(), &value.into().expect("Loading should not fail")) + .resolve_with_ctx( + &T::TypeInfo::default(), + &value.into().expect("Loading should not fail"), + ) + .map_err(|inner| WundergraphError::JuniperError { inner })?, + )) + } + + fn finalize( + self, + _global_args: &[juniper::LookAheadArgument], + _look_ahead: &juniper::LookAheadSelection<'_, WundergraphScalarValue>, + _selection: Option<&'_ [Selection<'_, WundergraphScalarValue>]>, + _executor: &Executor<'_, Ctx, WundergraphScalarValue>, + ) -> Result>>> { + Ok(None) + } +} + +impl FieldValueResolver, DB, Ctx> for DirectResolver +where + DB: Backend, + // T: GraphQLType, + Option: GraphQLType + WundergraphSqlValue, + as GraphQLType>::TypeInfo: Default, + as WundergraphSqlValue>::PlaceHolder: Into>>, + as GraphQLType>::Context: FromContext, +{ + fn new(_elements: usize) -> Self { + Self + } + + fn resolve_value( + &mut self, + value: as WundergraphSqlValue>::PlaceHolder, + _look_ahead: &juniper::LookAheadSelection<'_, WundergraphScalarValue>, + _selection: Option<&'_ [Selection<'_, WundergraphScalarValue>]>, + executor: &Executor<'_, Ctx, WundergraphScalarValue>, + ) -> Result>> { + Ok(Some( + executor + .resolve_with_ctx( + & as GraphQLType>::TypeInfo::default(), + &value.into().expect("Loading should not fail"), + ) .map_err(|inner| WundergraphError::JuniperError { inner })?, )) } @@ -48,8 +93,18 @@ where impl ResolveWundergraphFieldValue for T where DB: Backend, - T: GraphQLType + WundergraphValue, + T: GraphQLType + WundergraphSqlValue + DirectResolveable, DirectResolver: FieldValueResolver, { type Resolver = DirectResolver; } + +impl ResolveWundergraphFieldValue for Option +where + T: DirectResolveable, + DB: Backend, + T: GraphQLType + WundergraphSqlValue, + DirectResolver: FieldValueResolver, DB, Ctx>, +{ + type Resolver = DirectResolver; +} diff --git a/wundergraph/src/query_builder/types/field_value_resolver/has_one_resolver.rs b/wundergraph/src/query_builder/types/field_value_resolver/has_one_resolver.rs index d9d19bb..4859130 100644 --- a/wundergraph/src/query_builder/types/field_value_resolver/has_one_resolver.rs +++ b/wundergraph/src/query_builder/types/field_value_resolver/has_one_resolver.rs @@ -5,7 +5,7 @@ use crate::query_builder::selection::fields::WundergraphFieldList; use crate::query_builder::selection::filter::build_filter::BuildFilter; use crate::query_builder::selection::offset::ApplyOffset; use crate::query_builder::selection::{LoadingHandler, SqlTypeOfPlaceholder}; -use crate::query_builder::types::{HasOne, WundergraphValue}; +use crate::query_builder::types::{HasOne, WundergraphSqlValue}; use crate::scalar::WundergraphScalarValue; use diesel::backend::Backend; use diesel::dsl::SqlTypeOf; @@ -40,9 +40,9 @@ where + 'static, Option: Queryable::PrimaryKey>>, DB> + ToSql::PrimaryKey>>, DB>, - HasOne: WundergraphValue, - as WundergraphValue>::PlaceHolder: Into>, - R: WundergraphValue + Clone + Eq + Hash, + HasOne: WundergraphSqlValue, + as WundergraphSqlValue>::PlaceHolder: Into>, + R: WundergraphSqlValue + Clone + Eq + Hash, for<'b> &'b T: Identifiable, T: LoadingHandler, ::FromClause: QueryFragment, @@ -80,7 +80,7 @@ where fn resolve_value( &mut self, - value: as WundergraphValue>::PlaceHolder, + value: as WundergraphSqlValue>::PlaceHolder, _look_ahead: &juniper::LookAheadSelection<'_, WundergraphScalarValue>, _selection: Option<&'_ [Selection<'_, WundergraphScalarValue>]>, _executor: &Executor<'_, Ctx, WundergraphScalarValue>, @@ -109,6 +109,10 @@ where T::get_select(look_ahead)?, )); + #[cfg(feature = "debug")] + { + log::debug!("{:?}", diesel::debug_query(&q)); + } let items = q.load::<( Option, >::PlaceHolder, @@ -142,7 +146,7 @@ where impl FieldValueResolver>, DB, Ctx> for HasOneResolver where DB: Backend, - R: WundergraphValue + Clone + Hash + Eq, + R: WundergraphSqlValue + Clone + Hash + Eq, Self: FieldValueResolver, DB, Ctx>, for<'b> &'b T: Identifiable, R::PlaceHolder: Into>, @@ -156,7 +160,7 @@ where fn resolve_value( &mut self, - value: > as WundergraphValue>::PlaceHolder, + value: > as WundergraphSqlValue>::PlaceHolder, _look_ahead: &juniper::LookAheadSelection<'_, WundergraphScalarValue>, _selection: Option<&'_ [Selection<'_, WundergraphScalarValue>]>, _executor: &Executor<'_, Ctx, WundergraphScalarValue>, @@ -182,14 +186,13 @@ where } } -impl ResolveWundergraphFieldValue for Option> +impl ResolveWundergraphFieldValue for Option> where - HasOneResolver: FieldValueResolver, DB, Ctx> - + FieldValueResolver>, DB, Ctx>, - R: WundergraphValue + Clone + Eq + Hash, - as WundergraphValue>::PlaceHolder: Into>, - HasOne: WundergraphValue, DB: Backend, + R: WundergraphSqlValue + Clone + Eq + Hash, + HasOneResolver: FieldValueResolver>, DB, Ctx>, + Self::PlaceHolder: Into>, + Self: WundergraphSqlValue, { type Resolver = HasOneResolver; } @@ -197,9 +200,9 @@ where impl ResolveWundergraphFieldValue for HasOne where HasOneResolver: FieldValueResolver, DB, Ctx>, - R: WundergraphValue + Clone + Eq + Hash, + R: WundergraphSqlValue + Clone + Eq + Hash, Self::PlaceHolder: Into>, - Self: WundergraphValue, + Self: WundergraphSqlValue, DB: Backend, { type Resolver = HasOneResolver; diff --git a/wundergraph/src/query_builder/types/field_value_resolver/mod.rs b/wundergraph/src/query_builder/types/field_value_resolver/mod.rs index 3e47456..f3e8d12 100644 --- a/wundergraph/src/query_builder/types/field_value_resolver/mod.rs +++ b/wundergraph/src/query_builder/types/field_value_resolver/mod.rs @@ -1,23 +1,25 @@ -use super::WundergraphValue; +use super::WundergraphSqlValue; use crate::error::Result; use crate::scalar::WundergraphScalarValue; use diesel::backend::Backend; use juniper::{Executor, Selection}; mod direct_resolver; -mod has_one_resolver; +pub mod has_one_resolver; /// A internal helper trait indicating how to resolve a given type while query /// execution -pub trait ResolveWundergraphFieldValue: WundergraphValue + Sized { +pub trait ResolveWundergraphFieldValue: WundergraphSqlValue + Sized { /// A type implementing `FieldValueResolver` used to resolve values of /// this type during query execution type Resolver: FieldValueResolver; } +pub trait DirectResolveable {} + pub trait FieldValueResolver where - T: WundergraphValue, + T: WundergraphSqlValue, DB: Backend, { fn new(elements: usize) -> Self; @@ -38,3 +40,12 @@ where executor: &Executor<'_, Ctx, WundergraphScalarValue>, ) -> Result>>>; } + +impl DirectResolveable for i16 {} +impl DirectResolveable for i32 {} +impl DirectResolveable for i64 {} +impl DirectResolveable for bool {} +impl DirectResolveable for String {} +impl DirectResolveable for f32 {} +impl DirectResolveable for f64 {} +impl DirectResolveable for Vec {} diff --git a/wundergraph/src/query_builder/types/has_many.rs b/wundergraph/src/query_builder/types/has_many.rs index 2a19359..675e92f 100644 --- a/wundergraph/src/query_builder/types/has_many.rs +++ b/wundergraph/src/query_builder/types/has_many.rs @@ -1,6 +1,7 @@ use crate::graphql_type::WundergraphGraphqlMapper; +use crate::query_builder::types::wundergraph_value::{AssociatedValue, WundergraphValue}; use crate::scalar::WundergraphScalarValue; -use juniper::{meta, Registry}; +use juniper::{meta, GraphQLType, Registry}; use std::marker::PhantomData; /// Type used to indicate that a given field references multiple other entities @@ -20,4 +21,12 @@ where ) -> meta::Field<'r, WundergraphScalarValue> { T::register_arguments(registry, field) } + + fn type_info() -> >::TypeInfo { + T::type_info() + } +} + +impl WundergraphValue for HasMany { + type ValueType = AssociatedValue; } diff --git a/wundergraph/src/query_builder/types/has_one.rs b/wundergraph/src/query_builder/types/has_one.rs index 0c41cb0..b0521ee 100644 --- a/wundergraph/src/query_builder/types/has_one.rs +++ b/wundergraph/src/query_builder/types/has_one.rs @@ -9,7 +9,7 @@ use diesel::expression::bound::Bound; use diesel::expression::AsExpression; use diesel::Queryable; use juniper::meta::Argument; -use juniper::{FromInputValue, InputValue, LookAheadValue, Registry}; +use juniper::{FromInputValue, GraphQLType, InputValue, LookAheadValue, Registry}; use std::hash::{Hash, Hasher}; /// Type used to indicate that a given field references a single @@ -103,19 +103,6 @@ where } } -// impl ToInputValue for HasOne -// where -// R: ToInputValue, -// T: ToInputValue, -// { -// fn to_input_value(&self) -> InputValue { -// match *self { -// HasOne::Id(ref i) => i.to_input_value(), -// HasOne::Item(ref i) => i.to_input_value(), -// } -// } -// } - impl FromSql for HasOne where DB: Backend, @@ -195,27 +182,33 @@ where T: WundergraphGraphqlMapper, { type GraphQLType = T::GraphQLType; + + fn type_info() -> >::TypeInfo { + T::type_info() + } } -#[allow(clippy::use_self)] -impl WundergraphGraphqlMapper for Option> +impl WundergraphGraphqlMapper for Option where T: WundergraphGraphqlMapper, { type GraphQLType = Option; + + fn type_info() -> >::TypeInfo { + T::type_info() + } } -impl PrimaryKeyInputObject, I> for C +impl PrimaryKeyInputObject> for C where - C: PrimaryKeyInputObject, + C: PrimaryKeyInputObject, R: Eq + Hash, for<'a> &'a T: Identifiable, { fn register<'r>( registry: &mut Registry<'r, WundergraphScalarValue>, - info: &I, ) -> Vec> { - Self::register(registry, info) + Self::register(registry) } fn from_input_value(value: &InputValue) -> Option> { diff --git a/wundergraph/src/query_builder/types/mod.rs b/wundergraph/src/query_builder/types/mod.rs index 8cf7888..9426c50 100644 --- a/wundergraph/src/query_builder/types/mod.rs +++ b/wundergraph/src/query_builder/types/mod.rs @@ -1,14 +1,18 @@ //! This module contains several helper types used constructing the final //! graphql model -pub(crate) mod field_value_resolver; +pub mod as_input_type; +pub mod field_value_resolver; mod has_many; mod has_one; -pub(crate) mod placeholder; +pub mod placeholder; mod wundergraph_value; +pub use self::as_input_type::AsInputType; pub use self::field_value_resolver::ResolveWundergraphFieldValue; pub use self::has_many::HasMany; pub use self::has_one::HasOne; pub use self::placeholder::PlaceHolder; -pub use self::wundergraph_value::WundergraphValue; +pub use self::wundergraph_value::{ + AssociatedValue, TableField, WundergraphSqlValue, WundergraphValue, +}; diff --git a/wundergraph/src/query_builder/types/placeholder.rs b/wundergraph/src/query_builder/types/placeholder.rs index 0ae0dde..69f056d 100644 --- a/wundergraph/src/query_builder/types/placeholder.rs +++ b/wundergraph/src/query_builder/types/placeholder.rs @@ -1,5 +1,6 @@ use diesel::backend::Backend; -use diesel::deserialize::{self, FromSql}; +use diesel::deserialize::{self, FromSql, FromSqlRow, Queryable}; +use diesel::row::Row; use diesel::sql_types::{NotNull, Nullable}; pub trait PlaceHolderMarker { @@ -10,7 +11,7 @@ pub trait PlaceHolderMarker { /// A wrapper type used inside of wundergraph to load values of the type T /// from the database -#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, FromSqlRow, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)] pub struct PlaceHolder(Option); impl PlaceHolderMarker for PlaceHolder { @@ -59,3 +60,29 @@ where } } } + +impl FromSqlRow, DB> for PlaceHolder +where + Option: FromSqlRow, DB>, + DB: Backend, + ST: NotNull, +{ + const FIELDS_NEEDED: usize = as FromSqlRow, DB>>::FIELDS_NEEDED; + + fn build_from_row>(row: &mut R) -> deserialize::Result { + Option::build_from_row(row).map(PlaceHolder) + } +} + +impl Queryable, DB> for PlaceHolder +where + Option: Queryable, DB>, + DB: Backend, + ST: NotNull, +{ + type Row = as Queryable, DB>>::Row; + + fn build(row: Self::Row) -> Self { + PlaceHolder(Option::build(row)) + } +} diff --git a/wundergraph/src/query_builder/types/wundergraph_value.rs b/wundergraph/src/query_builder/types/wundergraph_value.rs index 3d3679c..a817719 100644 --- a/wundergraph/src/query_builder/types/wundergraph_value.rs +++ b/wundergraph/src/query_builder/types/wundergraph_value.rs @@ -147,7 +147,7 @@ pub use wundergraph_derive::WundergraphValue; /// # } /// # fn main() {} /// ``` -pub trait WundergraphValue { +pub trait WundergraphSqlValue { /// A type used to load values of the specified sql type into /// /// For common cases this should be `PlaceHolder` @@ -158,62 +158,79 @@ pub trait WundergraphValue { type SqlType: 'static; } -impl WundergraphValue for i16 { +pub trait WundergraphValue { + type ValueType; +} + +impl WundergraphValue for T +where + T: WundergraphSqlValue, +{ + type ValueType = TableField; +} + +#[derive(Clone, Copy, Debug)] +pub struct TableField; + +#[derive(Clone, Copy, Debug)] +pub struct AssociatedValue; + +impl WundergraphSqlValue for i16 { type PlaceHolder = PlaceHolder; type SqlType = Nullable; } -impl WundergraphValue for i32 { +impl WundergraphSqlValue for i32 { type PlaceHolder = PlaceHolder; type SqlType = Nullable; } -impl WundergraphValue for i64 { +impl WundergraphSqlValue for i64 { type PlaceHolder = PlaceHolder; type SqlType = Nullable; } -impl WundergraphValue for bool { +impl WundergraphSqlValue for bool { type PlaceHolder = PlaceHolder; type SqlType = Nullable; } -impl WundergraphValue for String { +impl WundergraphSqlValue for String { type PlaceHolder = PlaceHolder; type SqlType = Nullable; } -impl WundergraphValue for f32 { +impl WundergraphSqlValue for f32 { type PlaceHolder = PlaceHolder; type SqlType = Nullable; } -impl WundergraphValue for f64 { +impl WundergraphSqlValue for f64 { type PlaceHolder = PlaceHolder; type SqlType = Nullable; } #[cfg(feature = "postgres")] -impl WundergraphValue for Vec +impl WundergraphSqlValue for Vec where - T: WundergraphValue> + 'static, + T: WundergraphSqlValue> + 'static, Inner: diesel::sql_types::NotNull + 'static, { type PlaceHolder = PlaceHolder; type SqlType = Nullable>; } -impl WundergraphValue for Option +impl WundergraphSqlValue for Option where - T: WundergraphValue, + T: WundergraphSqlValue, { type PlaceHolder = T::PlaceHolder; type SqlType = T::SqlType; } -impl WundergraphValue for HasOne +impl WundergraphSqlValue for HasOne where - R: WundergraphValue + Clone + Eq + Hash, + R: WundergraphSqlValue + Clone + Eq + Hash, for<'a> &'a T: Identifiable, { type PlaceHolder = R::PlaceHolder; @@ -227,8 +244,8 @@ macro_rules! wundergraph_value_impl { } )+) => { $( - impl<$($T,)+> WundergraphValue for ($($T,)+) - where $($T: WundergraphValue,)+ + impl<$($T,)+> WundergraphSqlValue for ($($T,)+) + where $($T: WundergraphSqlValue,)+ { type PlaceHolder = ($($T::PlaceHolder,)+); type SqlType = ($($T::SqlType,)+); diff --git a/wundergraph/src/third_party_integrations/chrono.rs b/wundergraph/src/third_party_integrations/chrono.rs index 2b47239..ff8b09d 100644 --- a/wundergraph/src/third_party_integrations/chrono.rs +++ b/wundergraph/src/third_party_integrations/chrono.rs @@ -1,9 +1,12 @@ -use crate::juniper_ext::{FromLookAheadValue, Nameable}; use crate::query_builder::selection::filter::filter_helper::AsColumnFilter; use crate::query_builder::selection::filter::filter_value::FilterValue; use crate::query_builder::selection::filter::FilterOption; -use crate::query_builder::types::{PlaceHolder, WundergraphValue}; +use crate::query_builder::types::{AsInputType, PlaceHolder, WundergraphSqlValue}; use crate::scalar::WundergraphScalarValue; +use crate::{ + juniper_ext::{FromLookAheadValue, Nameable}, + query_builder::types::field_value_resolver::DirectResolveable, +}; use chrono_internal::{DateTime, FixedOffset, NaiveDate, NaiveDateTime, TimeZone, Utc}; use diesel::sql_types::{Date, Nullable, Timestamp}; use juniper::{FromInputValue, LookAheadValue, ToInputValue}; @@ -77,18 +80,18 @@ impl FromLookAheadValue for NaiveDate { } } -impl WundergraphValue for NaiveDateTime { +impl WundergraphSqlValue for NaiveDateTime { type PlaceHolder = PlaceHolder; type SqlType = Nullable; } #[cfg(feature = "postgres")] -impl WundergraphValue for DateTime { +impl WundergraphSqlValue for DateTime { type PlaceHolder = PlaceHolder; type SqlType = Nullable; } -impl WundergraphValue for NaiveDate { +impl WundergraphSqlValue for NaiveDate { type PlaceHolder = PlaceHolder; type SqlType = Nullable; } @@ -125,3 +128,19 @@ impl AsColumnFilter for DateTime { impl AsColumnFilter for NaiveDate { type Filter = FilterOption; } + +impl AsInputType for NaiveDateTime { + type InputType = Self; +} + +impl AsInputType for DateTime { + type InputType = Self; +} + +impl AsInputType for NaiveDate { + type InputType = Self; +} + +impl DirectResolveable for NaiveDateTime {} +impl DirectResolveable for DateTime {} +impl DirectResolveable for NaiveDate {} diff --git a/wundergraph/src/third_party_integrations/uuid.rs b/wundergraph/src/third_party_integrations/uuid.rs index b4c2e09..9b93329 100644 --- a/wundergraph/src/third_party_integrations/uuid.rs +++ b/wundergraph/src/third_party_integrations/uuid.rs @@ -2,7 +2,8 @@ use crate::juniper_ext::{FromLookAheadValue, Nameable}; use crate::query_builder::selection::filter::filter_helper::AsColumnFilter; use crate::query_builder::selection::filter::filter_value::FilterValue; use crate::query_builder::selection::filter::FilterOption; -use crate::query_builder::types::{PlaceHolder, WundergraphValue}; +use crate::query_builder::types::field_value_resolver::DirectResolveable; +use crate::query_builder::types::{AsInputType, PlaceHolder, WundergraphSqlValue}; use crate::scalar::WundergraphScalarValue; use diesel::sql_types::Nullable; use juniper::LookAheadValue; @@ -24,7 +25,7 @@ impl FromLookAheadValue for Uuid { } } -impl WundergraphValue for Uuid { +impl WundergraphSqlValue for Uuid { type PlaceHolder = PlaceHolder; type SqlType = Nullable<::diesel::sql_types::Uuid>; } @@ -37,3 +38,9 @@ impl FilterValue for Uuid { type RawValue = Self; type AdditionalFilter = (); } + +impl AsInputType for Uuid { + type InputType = Self; +} + +impl DirectResolveable for Uuid {} diff --git a/wundergraph_derive/src/wundergraph_entity.rs b/wundergraph_derive/src/wundergraph_entity.rs index 759274a..db2b362 100644 --- a/wundergraph_derive/src/wundergraph_entity.rs +++ b/wundergraph_derive/src/wundergraph_entity.rs @@ -180,6 +180,10 @@ fn derive_loading_handler( ); field.argument(arg) } + + fn type_info() -> () { + () + } } impl #impl_generics LoadingHandler<#backend, __Ctx> for #struct_type #ty_generics diff --git a/wundergraph_derive/src/wundergraph_value.rs b/wundergraph_derive/src/wundergraph_value.rs index b930e0c..0c80274 100644 --- a/wundergraph_derive/src/wundergraph_value.rs +++ b/wundergraph_derive/src/wundergraph_value.rs @@ -12,6 +12,7 @@ pub fn derive(item: &syn::DeriveInput) -> Result { let look_ahead = from_look_ahead(item)?; let wundergraph_value = wundergraph_value(item)?; let as_filter = as_column_filter(item); + let direct_resolvable = direct_resolveable(item); Ok(wrap_in_dummy_mod( "wundergraph_value", @@ -23,8 +24,9 @@ pub fn derive(item: &syn::DeriveInput) -> Result { use wundergraph::juniper::{self, LookAheadValue}; use wundergraph::juniper_ext::{FromLookAheadValue, Nameable}; use wundergraph::scalar::WundergraphScalarValue; - use wundergraph::query_builder::types::{WundergraphValue, PlaceHolder}; + use wundergraph::query_builder::types::{WundergraphSqlValue, PlaceHolder}; use wundergraph::diesel::sql_types::Nullable; + use wundergraph::query_builder::types::field_value_resolver::DirectResolveable; #filter_value @@ -32,10 +34,22 @@ pub fn derive(item: &syn::DeriveInput) -> Result { #look_ahead #wundergraph_value #as_filter + #direct_resolvable }, )) } +fn direct_resolveable(item: &syn::DeriveInput) -> TokenStream { + let item_name = &item.ident; + let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl(); + + quote! { + impl #impl_generics DirectResolveable for #item_name #ty_generics + #where_clause + { } + } +} + fn as_column_filter(item: &syn::DeriveInput) -> TokenStream { let item_name = &item.ident; let (_, ty_generics, where_clause) = item.generics.split_for_impl(); @@ -65,7 +79,7 @@ fn wundergraph_value(item: &syn::DeriveInput) -> Result let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl(); Ok(quote! { - impl #impl_generics WundergraphValue for #item_name #ty_generics + impl #impl_generics WundergraphSqlValue for #item_name #ty_generics #where_clause { type PlaceHolder = PlaceHolder; From 5752de6365219c36adcf662f4c454b54b97442dd Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Fri, 8 Jan 2021 12:12:52 +0100 Subject: [PATCH 2/3] More improvements for multi-key tables --- wundergraph/src/diesel_ext.rs | 258 +++--------------- wundergraph/src/graphql_type.rs | 1 + .../selection/filter/reference_filter.rs | 119 ++++---- .../src/query_builder/selection/order.rs | 91 ++++-- .../src/query_builder/selection/select.rs | 3 +- .../field_value_resolver/has_one_resolver.rs | 5 +- .../src/query_builder/types/has_one.rs | 9 + wundergraph_derive/src/meta.rs | 2 +- wundergraph_derive/src/wundergraph_value.rs | 16 ++ 9 files changed, 205 insertions(+), 299 deletions(-) diff --git a/wundergraph/src/diesel_ext.rs b/wundergraph/src/diesel_ext.rs index ac6fcd6..1aa42d1 100644 --- a/wundergraph/src/diesel_ext.rs +++ b/wundergraph/src/diesel_ext.rs @@ -1,17 +1,11 @@ //! A module containing extension traits for various diesel types -use std::marker::PhantomData; - -use diesel::backend::Backend; use diesel::expression::{AppearsOnTable, Expression, NonAggregate, SelectableExpression}; -use diesel::query_builder::BindCollector; -use diesel::query_builder::QueryBuilder; use diesel::query_builder::{AstPass, QueryFragment}; use diesel::result::QueryResult; -use diesel::sql_types; use diesel::sql_types::IntoNullable; -use diesel::types::HasSqlType; -use diesel::types::TypeMetadata; +use diesel::{backend::Backend, Column}; +use std::marker::PhantomData; /// A helper trait used when boxing filters /// @@ -77,231 +71,59 @@ where impl QueryFragment for MaybeNull where DB: Backend, - T: QueryFragment + QueryFragment> + Default, - DB::QueryBuilder: Default, + T: QueryFragment + Default + Column, { - fn walk_ast(&self, mut pass: AstPass<'_, DB>) -> QueryResult<()> { - let expr = T::default(); + fn walk_ast(&self, mut pass: AstPass) -> QueryResult<()> { if self.as_null { - let mut query_builder = MaybeNullQueryBuilder::new(DB::QueryBuilder::default(), true); - let ast_pass = AstPass::>::to_sql(&mut query_builder); - expr.walk_ast(ast_pass)?; - let identifier_pushed = query_builder.identifier_pushed; - debug_assert!(identifier_pushed % 2 == 0); - - for i in 0..(identifier_pushed / 2) { - if i != 0 { - pass.push_sql(", "); - } - pass.push_sql("NULL"); - } - pass.push_sql(" "); + pass.push_sql("NULL"); } else { - expr.walk_ast(pass)?; + T::default().walk_ast(pass)?; } Ok(()) } } -impl NonAggregate for MaybeNull {} - -impl AppearsOnTable for MaybeNull where Self: Expression {} - -impl SelectableExpression for MaybeNull where Self: Expression {} - -pub(crate) use self::fake_query_builder::FakeBackend; -use self::fake_query_builder::MaybeNullQueryBuilder; - -mod fake_query_builder { - use super::*; - - #[derive(Debug)] - pub struct MaybeNullQueryBuilder { - inner: Q, - generate_nulls: bool, - pub(super) identifier_pushed: usize, - } - - impl MaybeNullQueryBuilder { - pub fn new(inner: Q, generate_nulls: bool) -> Self { - Self { - inner, - generate_nulls, - identifier_pushed: 0, - } - } - } - - impl QueryBuilder> for MaybeNullQueryBuilder - where - Q: QueryBuilder, - DB: Backend, - { - fn push_sql(&mut self, sql: &str) { - self.inner.push_sql(sql) - } - - fn push_identifier(&mut self, identifier: &str) -> QueryResult<()> { - if self.generate_nulls { - self.identifier_pushed += 1; - Ok(()) - } else { - self.inner.push_identifier(identifier) - } - } - - fn push_bind_param(&mut self) { - self.inner.push_bind_param(); - } - - fn finish(self) -> String { - self.inner.finish() - } - } - - #[derive(Debug)] - pub struct FakeBackend(DB); - - impl Backend for FakeBackend - where - DB: Backend, - { - type QueryBuilder = MaybeNullQueryBuilder; - - type BindCollector = FakeBindCollector; - - type RawValue = DB::RawValue; - - type ByteOrder = DB::ByteOrder; - } - - #[derive(Debug)] - pub struct FakeBindCollector(B); - - impl BindCollector> for FakeBindCollector - where - B: BindCollector, - DB: Backend, - { - fn push_bound_value( - &mut self, - _bind: &U, - _metadata_lookup: & as TypeMetadata>::MetadataLookup, - ) -> QueryResult<()> - where - FakeBackend: HasSqlType, - U: diesel::types::ToSql>, - { - unimplemented!() - // self.0.push_bound_value(bind, metadata_lookup) - } - } - - impl TypeMetadata for FakeBackend - where - DB: TypeMetadata, - { - type TypeMetadata = DB::TypeMetadata; - - type MetadataLookup = DB::MetadataLookup; - } - - impl HasSqlType for FakeBackend - where - DB: HasSqlType, - { - fn metadata(lookup: &Self::MetadataLookup) -> Self::TypeMetadata { - DB::metadata(lookup) - } - } - - impl HasSqlType for FakeBackend - where - DB: HasSqlType, - { - fn metadata(lookup: &Self::MetadataLookup) -> Self::TypeMetadata { - DB::metadata(lookup) - } - } - - impl HasSqlType for FakeBackend - where - DB: HasSqlType, - { - fn metadata(lookup: &Self::MetadataLookup) -> Self::TypeMetadata { - DB::metadata(lookup) - } - } - - impl HasSqlType for FakeBackend - where - DB: HasSqlType, - { - fn metadata(lookup: &Self::MetadataLookup) -> Self::TypeMetadata { - DB::metadata(lookup) +impl QueryFragment for MaybeNull> +where + DB: Backend, + (A, B): QueryFragment + Default, +{ + fn walk_ast(&self, mut pass: AstPass) -> QueryResult<()> { + if self.as_null { + pass.push_sql("NULL, NULL"); + } else { + <(A, B) as Default>::default().walk_ast(pass)?; } + Ok(()) } +} - impl HasSqlType for FakeBackend - where - DB: HasSqlType, - { - fn metadata(lookup: &Self::MetadataLookup) -> Self::TypeMetadata { - DB::metadata(lookup) - } - } +#[derive(Default, Debug, Clone, Copy)] +pub struct MultipleColumnHelper(T); - impl HasSqlType for FakeBackend - where - DB: HasSqlType, - { - fn metadata(lookup: &Self::MetadataLookup) -> Self::TypeMetadata { - DB::metadata(lookup) - } +impl Expression for MultipleColumnHelper +where + T: Expression, +{ + type SqlType = T::SqlType; +} +impl NonAggregate for MultipleColumnHelper where T: NonAggregate {} +impl AppearsOnTable for MultipleColumnHelper where T: AppearsOnTable {} +impl SelectableExpression for MultipleColumnHelper where T: SelectableExpression {} +impl QueryFragment for MultipleColumnHelper +where + DB: Backend, + T: QueryFragment, +{ + fn walk_ast(&self, pass: AstPass) -> QueryResult<()> { + self.0.walk_ast(pass) } +} - impl HasSqlType for FakeBackend - where - DB: HasSqlType, - { - fn metadata(lookup: &Self::MetadataLookup) -> Self::TypeMetadata { - DB::metadata(lookup) - } - } - impl HasSqlType for FakeBackend - where - DB: HasSqlType, - { - fn metadata(lookup: &Self::MetadataLookup) -> Self::TypeMetadata { - DB::metadata(lookup) - } - } +impl NonAggregate for MaybeNull {} - impl HasSqlType for FakeBackend - where - DB: HasSqlType, - { - fn metadata(lookup: &Self::MetadataLookup) -> Self::TypeMetadata { - DB::metadata(lookup) - } - } +impl AppearsOnTable for MaybeNull where Self: Expression {} - impl HasSqlType for FakeBackend - where - DB: HasSqlType, - { - fn metadata(lookup: &Self::MetadataLookup) -> Self::TypeMetadata { - DB::metadata(lookup) - } - } +impl SelectableExpression for MaybeNull where Self: Expression {} - impl HasSqlType for FakeBackend - where - DB: HasSqlType, - { - fn metadata(lookup: &Self::MetadataLookup) -> Self::TypeMetadata { - DB::metadata(lookup) - } - } -} diff --git a/wundergraph/src/graphql_type.rs b/wundergraph/src/graphql_type.rs index 0c8cdb0..d7bc9a4 100644 --- a/wundergraph/src/graphql_type.rs +++ b/wundergraph/src/graphql_type.rs @@ -126,6 +126,7 @@ macro_rules! wundergraph_graphql_helper_impl { if let Some(doc) = Loading::TYPE_DESCRIPTION { ty = ty.description(doc); } + meta::MetaType::Object(ty) } } diff --git a/wundergraph/src/query_builder/selection/filter/reference_filter.rs b/wundergraph/src/query_builder/selection/filter/reference_filter.rs index 9602ede..6e57a33 100644 --- a/wundergraph/src/query_builder/selection/filter/reference_filter.rs +++ b/wundergraph/src/query_builder/selection/filter/reference_filter.rs @@ -1,10 +1,10 @@ +#![allow(warnings)] use crate::diesel_ext::BoxableFilter; use crate::juniper_ext::{FromLookAheadValue, NameBuilder, Nameable}; use crate::query_builder::selection::filter::build_filter::BuildFilter; use crate::query_builder::selection::filter::collector::{AndCollector, FilterCollector}; use crate::query_builder::selection::filter::inner_filter::InnerFilter; use crate::scalar::WundergraphScalarValue; -use diesel::associations::HasTable; use diesel::backend::Backend; use diesel::dsl::{EqAny, Filter, NullableSelect, Select, SqlTypeOf}; use diesel::expression::array_comparison::AsInExpression; @@ -13,6 +13,7 @@ use diesel::expression::NonAggregate; use diesel::query_builder::{AsQuery, BoxedSelectStatement, Query, QueryFragment}; use diesel::query_dsl::methods::{BoxedDsl, FilterDsl, SelectDsl, SelectNullableDsl}; use diesel::sql_types::{Bool, SingleValue}; +use diesel::{associations::HasTable, Expression}; use diesel::{AppearsOnTable, Column, ExpressionMethods, NullableExpressionMethods, QueryDsl}; use indexmap::IndexMap; use juniper::meta::{Argument, MetaType}; @@ -41,63 +42,89 @@ where } } -impl BuildFilter for ReferenceFilter +pub trait AsReferenceFilterExpression { + type Expr: BoxableFilter; + type Table: 'static; + + fn as_filter(f: F) -> Self::Expr; +} + +impl AsReferenceFilterExpression for C1 where - C: Column + NonAggregate + QueryFragment + Default + 'static, - Nullable: ExpressionMethods, - C::SqlType: SingleValue, - A: BuildFilter + 'static, - C::Table: 'static, DB: Backend + 'static, - I: BuildFilter + InnerFilter, - C2: Column + NonAggregate + QueryFragment + Default + 'static, + C1: Column + Default + 'static, + C2: Column + Default, + C1::Table: 'static, + C2::Table: 'static, + C1::Table: HasTable
, C2::Table: HasTable
, - ::Query: FilterDsl, - Filter<::Query, I::Ret>: QueryDsl + SelectDsl, - Select::Query, I::Ret>, C2>: QueryDsl + SelectNullableDsl, -NullableSelect::Query, I::Ret>, C2>>: QueryDsl + Query - + BoxedDsl< - 'static, - DB, - Output = BoxedSelectStatement< + Nullable: ExpressionMethods, + as Expression>::SqlType: 'static, + ::Query: FilterDsl, + F: Expression + NonAggregate + QueryFragment, + Filter<::Query, F>: QueryDsl + SelectDsl, + Select::Query, F>, C2>: QueryDsl + SelectNullableDsl, + NullableSelect::Query, F>, C2>>: QueryDsl + Query + + BoxedDsl< 'static, - ::Query, I::Ret>, C2>> as Query>::SqlType, - C2::Table, DB, - >, + Output = BoxedSelectStatement< + 'static, + ::Query, F>, C2>> as Query>::SqlType, + C2::Table, + DB, + >, > + 'static, - BoxedSelectStatement< - 'static, - ::Query, I::Ret>, C2>> as Query>::SqlType, - C2::Table, - DB, - >: AsInExpression>>, - ::Query, I::Ret>, C2>> as Query>::SqlType, - C2::Table, - DB, - > as AsInExpression>>>::InExpression: AppearsOnTable + QueryFragment, - EqAny, BoxedSelectStatement< - 'static, - ::Query, I::Ret>, C2>> as Query>::SqlType, - C2::Table, - DB, - >>: BoxableFilter, - >::Ret: AppearsOnTable + 'static, + EqAny, BoxedSelectStatement< + 'static, + ::Query, F>, C2>> as Query>::SqlType, + C2::Table, + DB, + >>: BoxableFilter, + BoxedSelectStatement< + 'static, + ::Query, F>, C2>> as Query>::SqlType, + C2::Table, + DB, + >: AsInExpression>>, +{ + type Expr = Box>; + + type Table = C1::Table; + + fn as_filter(f: F) -> Self::Expr { + let f = <_ as QueryDsl>::filter(C2::Table::table(), f); + let f = <_ as QueryDsl>::select(f, C2::default()); + let q = <_ as SelectNullableDsl>::nullable(f).into_boxed(); + Box::new(C1::default().nullable().eq_any(q)) as Box<_> + } +} + +impl BuildFilter for ReferenceFilter +where + C: AsReferenceFilterExpression, + C::Expr: BuildFilter + 'static, + >::Ret: AppearsOnTable, + DB: Backend + 'static, + I: BuildFilter + InnerFilter, + A: BuildFilter + 'static, + >::Ret: AppearsOnTable + 'static, { type Ret = Box>; fn into_filter(self) -> Option { let mut and = AndCollector::default(); + dbg!(std::any::type_name::()); + + let inner = self.inner.into_filter(); + + if inner.is_some() { + dbg!("SOME") + } else { + dbg!("None") + }; - let inner = self - .inner - .into_filter() - .map(|f| <_ as QueryDsl>::filter(C2::Table::table(), f)) - .map(|f| <_ as QueryDsl>::select(f, C2::default())) - .map(|f| <_ as SelectNullableDsl>::nullable(f).into_boxed()) - .map(|q| Box::new(C::default().nullable().eq_any(q)) as Box<_>); + let inner = inner.map(|f| C::as_filter(f)); and.append_filter(inner); and.append_filter(self.additional); diff --git a/wundergraph/src/query_builder/selection/order.rs b/wundergraph/src/query_builder/selection/order.rs index f469513..5f1224b 100644 --- a/wundergraph/src/query_builder/selection/order.rs +++ b/wundergraph/src/query_builder/selection/order.rs @@ -1,13 +1,13 @@ use super::offset::ApplyOffset; use super::LoadingHandler; -use crate::error::Result; use crate::error::WundergraphError; use crate::juniper_ext::FromLookAheadValue; use crate::query_builder::selection::fields::FieldListExtractor; use crate::scalar::WundergraphScalarValue; -use diesel::backend::Backend; -use diesel::expression::NonAggregate; +use crate::{diesel_ext::MultipleColumnHelper, error::Result}; use diesel::query_builder::QueryFragment; +use diesel::{backend::Backend, ExpressionMethods}; +use diesel::{expression::NonAggregate, Column}; use diesel::{BoxableExpression, QuerySource, SelectableExpression}; use juniper::{ meta, FromInputValue, GraphQLEnum, GraphQLType, LookAheadValue, Registry, ToInputValue, @@ -192,6 +192,49 @@ pub trait WundergraphGraphqlOrderHelper { F: Fn(usize) -> &'static str; } +pub trait AsOrderExpression { + fn as_order_expression(order: Order) -> Box>; +} + +impl AsOrderExpression for C +where + DB: Backend, + C: ExpressionMethods + + Column + + Default + + QueryFragment + + SelectableExpression + + NonAggregate + + 'static, +{ + fn as_order_expression(order: Order) -> Box> { + if order == Order::Desc { + Box::new(C::default().desc()) as Box> + } else { + Box::new(C::default().asc()) as Box<_> + } + } +} + +impl AsOrderExpression for MultipleColumnHelper<(PK, V)> +where + DB: Backend, + PK: ExpressionMethods + + Default + + QueryFragment + + SelectableExpression + + NonAggregate + + 'static, +{ + fn as_order_expression(order: Order) -> Box> { + if order == Order::Desc { + Box::new(PK::default().desc()) as Box> + } else { + Box::new(PK::default().asc()) as Box<_> + } + } +} + macro_rules! impl_order_traits { ($( $Tuple:tt { @@ -204,49 +247,37 @@ macro_rules! impl_order_traits { where Table: ::diesel::Table, DB: Backend, $( - $T: //ExpressionMethods + - Copy + - Default + - SelectableExpression
+ - NonAggregate + - QueryFragment + - 'static, + $T: AsOrderExpression, )+ { fn build_order( fields: &[LookAheadValue<'_, WundergraphScalarValue>], - _field_name: impl Fn(usize) -> &'static str, + field_name: impl Fn(usize) -> &'static str, ) -> Result>>> { - let ret = Vec::with_capacity(fields.len()); + let mut ret = Vec::with_capacity(fields.len()); for f in fields { if let LookAheadValue::Object(o) = f { - let _column = o.iter().find(|(k, _)| *k == "column") + let column = o.iter().find(|(k, _)| *k == "column") .and_then(|(_, v)| if let LookAheadValue::Enum(c) = v { Some(c) } else { None }) .ok_or(WundergraphError::CouldNotBuildFilterArgument)?; - let _order = o.iter().find(|(k, _)| *k == "direction") + let order = o.iter().find(|(k, _)| *k == "direction") .and_then(|(_, v)| Order::from_look_ahead(v)) .unwrap_or(Order::Asc); - todo!() - // match *column { - // $( - // x if x == field_name($idx) => if order == Order::Desc { - // ret.push(Box::new($T::default().desc()) - // as Box>) - // } else { - // ret.push(Box::new($T::default().asc()) as Box<_>) - // } - // )+ - // x => { - // return Err(WundergraphError::UnknownDatabaseField { - // name: x.to_owned() - // }); - // } - // } + match *column { + $( + x if x == field_name($idx) => ret.push($T::as_order_expression(order)), + )+ + x => { + return Err(WundergraphError::UnknownDatabaseField { + name: x.to_owned() + }); + } + } } else { return Err( WundergraphError::CouldNotBuildFilterArgument diff --git a/wundergraph/src/query_builder/selection/select.rs b/wundergraph/src/query_builder/selection/select.rs index c46cae7..d494150 100644 --- a/wundergraph/src/query_builder/selection/select.rs +++ b/wundergraph/src/query_builder/selection/select.rs @@ -38,10 +38,9 @@ macro_rules! impl_select_builder { SelectableExpression
+ NonAggregate + QueryFragment + - QueryFragment> + 'static , )+ - $(MaybeNull<$T>: Expression,)+ + $(MaybeNull<$T>: Expression + QueryFragment,)+ { fn build_select( select: &LookAheadSelection<'_, WundergraphScalarValue>, diff --git a/wundergraph/src/query_builder/types/field_value_resolver/has_one_resolver.rs b/wundergraph/src/query_builder/types/field_value_resolver/has_one_resolver.rs index 4859130..25825f1 100644 --- a/wundergraph/src/query_builder/types/field_value_resolver/has_one_resolver.rs +++ b/wundergraph/src/query_builder/types/field_value_resolver/has_one_resolver.rs @@ -35,9 +35,10 @@ impl<'a, R, T, DB, Ctx> FieldValueResolver, DB, Ctx> for HasOneReso where DB: Backend + ApplyOffset - + HasSqlType> + + 'static + HasSqlType::PrimaryKey>>> - + 'static, + + HasSqlType>, + // + 'static, Option: Queryable::PrimaryKey>>, DB> + ToSql::PrimaryKey>>, DB>, HasOne: WundergraphSqlValue, diff --git a/wundergraph/src/query_builder/types/has_one.rs b/wundergraph/src/query_builder/types/has_one.rs index b0521ee..3548d32 100644 --- a/wundergraph/src/query_builder/types/has_one.rs +++ b/wundergraph/src/query_builder/types/has_one.rs @@ -116,6 +116,8 @@ where use diesel::serialize::{self, ToSql}; use std::io::Write; +use super::AsInputType; + impl ToSql for HasOne where DB: Backend, @@ -226,3 +228,10 @@ where } } } + +impl AsInputType for HasOne +where + R: AsInputType, +{ + type InputType = ::InputType; +} diff --git a/wundergraph_derive/src/meta.rs b/wundergraph_derive/src/meta.rs index dce45ce..2701739 100644 --- a/wundergraph_derive/src/meta.rs +++ b/wundergraph_derive/src/meta.rs @@ -105,7 +105,7 @@ impl MetaItem { } pub fn ident_value(&self) -> Result { - let maybe_attr = self.nested().ok().and_then(|mut n| n.nth(0)); + let maybe_attr = self.nested().ok().and_then(|mut n| n.next()); let maybe_word = maybe_attr.as_ref().and_then(|m| m.path().ok()); match maybe_word { Some(x) => { diff --git a/wundergraph_derive/src/wundergraph_value.rs b/wundergraph_derive/src/wundergraph_value.rs index 0c80274..89b918a 100644 --- a/wundergraph_derive/src/wundergraph_value.rs +++ b/wundergraph_derive/src/wundergraph_value.rs @@ -13,6 +13,7 @@ pub fn derive(item: &syn::DeriveInput) -> Result { let wundergraph_value = wundergraph_value(item)?; let as_filter = as_column_filter(item); let direct_resolvable = direct_resolveable(item); + let as_input_type = as_input_type(item); Ok(wrap_in_dummy_mod( "wundergraph_value", @@ -27,6 +28,7 @@ pub fn derive(item: &syn::DeriveInput) -> Result { use wundergraph::query_builder::types::{WundergraphSqlValue, PlaceHolder}; use wundergraph::diesel::sql_types::Nullable; use wundergraph::query_builder::types::field_value_resolver::DirectResolveable; + use wundergraph::query_builder::types::AsInputType; #filter_value @@ -35,10 +37,24 @@ pub fn derive(item: &syn::DeriveInput) -> Result { #wundergraph_value #as_filter #direct_resolvable + #as_input_type }, )) } +fn as_input_type(item: &syn::DeriveInput) -> TokenStream { + let item_name = &item.ident; + let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl(); + + quote! { + impl #impl_generics AsInputType for #item_name #ty_generics + #where_clause + { + type InputType = Self; + } + } +} + fn direct_resolveable(item: &syn::DeriveInput) -> TokenStream { let item_name = &item.ident; let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl(); From 82769730c5f0b4f8112aa1dc40b082ac5a3c224f Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Thu, 24 Nov 2022 19:29:01 +0100 Subject: [PATCH 3/3] Remove dbg! calls --- .../src/query_builder/selection/filter/reference_filter.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/wundergraph/src/query_builder/selection/filter/reference_filter.rs b/wundergraph/src/query_builder/selection/filter/reference_filter.rs index 6e57a33..273fa4d 100644 --- a/wundergraph/src/query_builder/selection/filter/reference_filter.rs +++ b/wundergraph/src/query_builder/selection/filter/reference_filter.rs @@ -114,16 +114,9 @@ where fn into_filter(self) -> Option { let mut and = AndCollector::default(); - dbg!(std::any::type_name::()); let inner = self.inner.into_filter(); - if inner.is_some() { - dbg!("SOME") - } else { - dbg!("None") - }; - let inner = inner.map(|f| C::as_filter(f)); and.append_filter(inner); and.append_filter(self.additional);