Repository: Brendonovich/prisma-client-rust (language: rust)

Issue:
Mixed Batch Container Types
From #236:
> I've been thinking about how batching multiple types of query containers at the same time could work, since Prisma Client JS allows you to batch whatever you want. In your example, something like _batch((tag::Create, Vec<tag::Upsert>)) would do the trick, but at the moment you can only use either a tuple or a Vec as a batch container.
> Allowing this would require some extra work on my part, since I'd have to keep track of how many queries were provided to each tuple and Vec, receive the resulting data as one big Vec, and then partition the results back out to tuples and Vec of the appropriate size.


Discussion / hints:

Fix patch:
diff --git a/cli/src/generator/client.rs b/cli/src/generator/client.rs
index bb94387b..8bdf5d06 100644
--- a/cli/src/generator/client.rs
+++ b/cli/src/generator/client.rs
@@ -127,7 +127,7 @@ pub fn generate(args: &GenerateArgs) -> TokenStream {
                 )
             }
 
-            pub async fn _batch<'a, T: #pcr::BatchContainer<'a, Marker>, Marker>(&self, queries: T) -> #pcr::Result<<T as #pcr::BatchContainer<'a, Marker>>::ReturnType> {
+            pub async fn _batch<'a, T: #pcr::BatchContainer<'a, Marker>, Marker>(&'a self, queries: T) -> #pcr::Result<<T as #pcr::BatchContainer<'a, Marker>>::ReturnType> {
                 #pcr::batch(queries, &self.0).await
             }
 
diff --git a/src/queries/batch.rs b/src/queries/batch.rs
index 60a2d974..dc28cd10 100644
--- a/src/queries/batch.rs
+++ b/src/queries/batch.rs
@@ -1,50 +1,165 @@
 use std::collections::VecDeque;
 
 use query_core::Operation;
-use serde::{
-    de::{DeserializeOwned, IntoDeserializer},
-    Deserialize,
-};
 
 use crate::{PrismaClientInternals, Query};
 
+pub enum BatchItemDataMeta {
+    Query,
+    Vec(usize, Box<Self>),
+    Tuple(Vec<Self>),
+}
+
+pub enum BatchItemData {
+    Query(Operation),
+    Vec(Vec<Self>),
+    Tuple(Vec<Self>),
+}
+
+impl BatchItemData {
+    fn meta(&self) -> BatchItemDataMeta {
+        match self {
+            Self::Query(_) => BatchItemDataMeta::Query,
+            Self::Vec(v) => BatchItemDataMeta::Vec(v.len(), Box::new(v[0].meta())),
+            Self::Tuple(v) => BatchItemDataMeta::Tuple(v.iter().map(BatchItemData::meta).collect()),
+        }
+    }
+
+    fn operations(self, v: &mut Vec<Operation>) {
+        match self {
+            Self::Query(op) => v.push(op),
+            Self::Vec(items) => items.into_iter().for_each(|i| i.operations(v)),
+            Self::Tuple(items) => items.into_iter().for_each(|i| i.operations(v)),
+        }
+    }
+}
+
+pub enum BatchDataMeta {
+    Iterator(usize, BatchItemDataMeta),
+    Tuple(Vec<BatchItemDataMeta>),
+}
+
+pub enum BatchData {
+    Iterator(Vec<BatchItemData>),
+    Tuple(Vec<BatchItemData>),
+}
+
+impl BatchData {
+    fn meta(&self) -> BatchDataMeta {
+        match self {
+            Self::Iterator(v) => BatchDataMeta::Iterator(v.len(), v[0].meta()),
+            Self::Tuple(v) => BatchDataMeta::Tuple(v.iter().map(BatchItemData::meta).collect()),
+        }
+    }
+
+    fn operations(self) -> Vec<Operation> {
+        let items = match self {
+            Self::Tuple(items) => items,
+            Self::Iterator(items) => items,
+        };
+
+        let mut ops = vec![];
+
+        items.into_iter().for_each(|i| i.operations(&mut ops));
+
+        ops
+    }
+}
+
 pub async fn batch<'a, T: BatchContainer<'a, Marker>, Marker>(
     container: T,
-    client: &PrismaClientInternals,
-) -> super::Result<<T as BatchContainer<'a, Marker>>::ReturnType> {
-    let data = client
+    client: &'a PrismaClientInternals,
+) -> super::Result<<T as BatchContainer<Marker>>::ReturnType> {
+    let data = container.data();
+    let meta = data.meta();
+
+    let operations = data.operations();
+
+    let values = client
         .engine
-        .execute_all(container.graphql())
+        .execute_all(operations)
         .await?
         .into_iter()
-        .map(|result| Ok(T::RawType::deserialize(result?.into_deserializer())?))
         .collect::<super::Result<VecDeque<_>>>()?;
 
-    Ok(T::convert(data))
+    Ok(T::resolve(meta, values))
+}
+
+pub trait BatchItem<'a> {
+    type ReturnValue;
+
+    fn data(self) -> BatchItemData;
+
+    fn resolve(
+        meta: &BatchItemDataMeta,
+        values: &mut VecDeque<serde_value::Value>,
+    ) -> Self::ReturnValue;
+}
+
+impl<'a, Q: Query<'a>> BatchItem<'a> for Q {
+    type ReturnValue = Q::ReturnValue;
+
+    fn data(self) -> BatchItemData {
+        BatchItemData::Query(self.graphql().0)
+    }
+
+    fn resolve(
+        _: &BatchItemDataMeta,
+        values: &mut VecDeque<serde_value::Value>,
+    ) -> Self::ReturnValue {
+        Q::convert(
+            values
+                .pop_front()
+                .unwrap()
+                .deserialize_into::<Q::RawType>()
+                .unwrap(),
+        )
+    }
+}
+
+impl<'a, I: BatchItem<'a>> BatchItem<'a> for Vec<I> {
+    type ReturnValue = Vec<I::ReturnValue>;
+
+    fn data(self) -> BatchItemData {
+        BatchItemData::Vec(self.into_iter().map(BatchItem::data).collect())
+    }
+
+    fn resolve(
+        meta: &BatchItemDataMeta,
+        values: &mut VecDeque<serde_value::Value>,
+    ) -> Self::ReturnValue {
+        match meta {
+            BatchItemDataMeta::Vec(count, meta) => (0..*count)
+                .map(|_| I::resolve(meta.as_ref(), values))
+                .collect(),
+            _ => unreachable!(),
+        }
+    }
 }
 
 /// A container that can hold queries to batch into a transaction
 pub trait BatchContainer<'a, Marker> {
-    type RawType: DeserializeOwned;
     type ReturnType;
 
-    fn graphql(self) -> Vec<Operation>;
-    fn convert(raw: VecDeque<Self::RawType>) -> Self::ReturnType;
+    fn data(self) -> BatchData;
+
+    fn resolve(meta: BatchDataMeta, values: VecDeque<serde_value::Value>) -> Self::ReturnType;
 }
 
-impl<'a, T: Query<'a>, I: IntoIterator<Item = T>> BatchContainer<'a, ()> for I {
-    type RawType = T::RawType;
-    type ReturnType = Vec<T::ReturnType>;
+impl<'a, T: BatchItem<'a>, I: IntoIterator<Item = T>> BatchContainer<'a, ()> for I {
+    type ReturnType = Vec<T::ReturnValue>;
 
-    fn graphql(self) -> Vec<Operation> {
-        self.into_iter()
-            .map(Query::graphql)
-            .map(|(o, _)| o)
-            .collect()
+    fn data(self) -> BatchData {
+        BatchData::Iterator(self.into_iter().map(BatchItem::data).collect())
     }
 
-    fn convert(raw: VecDeque<Self::RawType>) -> Self::ReturnType {
-        raw.into_iter().map(T::convert).collect()
+    fn resolve(meta: BatchDataMeta, mut values: VecDeque<serde_value::Value>) -> Self::ReturnType {
+        match meta {
+            BatchDataMeta::Iterator(count, meta) => {
+                (0..count).map(|_| T::resolve(&meta, &mut values)).collect()
+            }
+            _ => unreachable!(),
+        }
     }
 }
 
@@ -53,28 +168,58 @@ pub enum TupleMarker {}
 macro_rules! impl_tuple {
     ($($generic:ident),+) => {
         #[allow(warnings)]
-        impl<'a, $($generic: Query<'a>),+> BatchContainer<'a, TupleMarker> for ($($generic),+) {
-            type RawType = serde_json::Value;
-            type ReturnType = ($($generic::ReturnType),+);
+        impl<'a, $($generic: BatchItem<'a>),+> BatchContainer<'a, TupleMarker> for ($($generic),+) {
+            type ReturnType = ($($generic::ReturnValue),+);
 
-            fn graphql(self) -> Vec<$crate::query_core::Operation> {
+            fn data(self) -> BatchData {
                 let ($($generic),+) = self;
 
-                vec![$($generic.graphql().0),+]
+                BatchData::Tuple(
+                    vec![$(BatchItem::data($generic)),+]
+                )
             }
 
-            fn convert(mut raw: VecDeque<Self::RawType>) -> Self::ReturnType {
-                ($($generic::convert(raw
-                    .pop_front()
-                    .map(|v| serde_json::from_value(v).unwrap())
-                    .unwrap()
-                )),+)
+            fn resolve(meta: BatchDataMeta, mut values: VecDeque<serde_value::Value>) -> Self::ReturnType {
+                match meta {
+                    BatchDataMeta::Tuple(metas) => {
+                        let mut metas_iter = metas.iter();
+
+                        (($(<$generic as BatchItem>::resolve(metas_iter.next().unwrap(), &mut values)),+))
+                    },
+                    _ => unreachable!()
+                }
+            }
+        }
+
+        #[allow(warnings)]
+        impl<'a, $($generic: BatchItem<'a>),+> BatchItem<'a> for ($($generic),+) {
+            type ReturnValue = ($($generic::ReturnValue),+);
+
+            fn data(self) -> BatchItemData {
+                let ($($generic),+) = self;
+
+                BatchItemData::Tuple(
+                    vec![$(BatchItem::data($generic)),+]
+                )
+            }
+
+            fn resolve(
+                meta: &BatchItemDataMeta,
+                values: &mut VecDeque<serde_value::Value>,
+            ) -> Self::ReturnValue {
+                match meta {
+                    BatchItemDataMeta::Tuple(meta) => {
+                        let mut meta = meta.iter();
+
+                        ($($generic::resolve(meta.next().unwrap(), values)),+)
+                    },
+                    _ => unreachable!(),
+                }
             }
         }
     }
 }
 
-impl_tuple!(T1);
 impl_tuple!(T1, T2);
 impl_tuple!(T1, T2, T3);
 impl_tuple!(T1, T2, T3, T4);
@@ -91,3 +236,24 @@ impl_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14);
 impl_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15);
 impl_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16);
 impl_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17);
+
+/// TODO: remove this in 0.7.0
+#[allow(warnings)]
+impl<'a, Q: Query<'a>> BatchContainer<'a, TupleMarker> for Q {
+    type ReturnType = Q::ReturnValue;
+
+    fn data(self) -> BatchData {
+        BatchData::Tuple(vec![BatchItem::data(self)])
+    }
+
+    fn resolve(meta: BatchDataMeta, mut values: VecDeque<serde_value::Value>) -> Self::ReturnType {
+        match meta {
+            BatchDataMeta::Tuple(metas) => {
+                let mut metas_iter = metas.iter();
+
+                <Q as BatchItem>::resolve(metas_iter.next().unwrap(), &mut values)
+            }
+            _ => unreachable!(),
+        }
+    }
+}
diff --git a/src/queries/count.rs b/src/queries/count.rs
index facbf498..735d6912 100644
--- a/src/queries/count.rs
+++ b/src/queries/count.rs
@@ -65,7 +65,7 @@ pub struct CountResult {
 
 impl<'a, Actions: ModelActions> Query<'a> for Count<'a, Actions> {
     type RawType = CountAggregateResult;
-    type ReturnType = i64;
+    type ReturnValue = i64;
 
     fn graphql(self) -> (Operation, &'a PrismaClientInternals) {
         (
@@ -129,7 +129,7 @@ impl<'a, Actions: ModelActions> Query<'a> for Count<'a, Actions> {
         )
     }
 
-    fn convert(raw: Self::RawType) -> Self::ReturnType {
+    fn convert(raw: Self::RawType) -> Self::ReturnValue {
         raw._count._all
     }
 }
diff --git a/src/queries/create.rs b/src/queries/create.rs
index 39422a64..9b57bc4a 100644
--- a/src/queries/create.rs
+++ b/src/queries/create.rs
@@ -70,7 +70,7 @@ impl<'a, Actions: ModelActions> Create<'a, Actions> {
 
 impl<'a, Actions: ModelActions> Query<'a> for Create<'a, Actions> {
     type RawType = Actions::Data;
-    type ReturnType = Self::RawType;
+    type ReturnValue = Self::RawType;
 
     fn graphql(self) -> (Operation, &'a PrismaClientInternals) {
         let mut scalar_selections = Actions::scalar_selections();
@@ -83,7 +83,7 @@ impl<'a, Actions: ModelActions> Query<'a> for Create<'a, Actions> {
         )
     }
 
-    fn convert(raw: Self::RawType) -> Self::ReturnType {
+    fn convert(raw: Self::RawType) -> Self::ReturnValue {
         raw
     }
 }
diff --git a/src/queries/create_many.rs b/src/queries/create_many.rs
index 10783a16..1bd06c3f 100644
--- a/src/queries/create_many.rs
+++ b/src/queries/create_many.rs
@@ -65,7 +65,7 @@ impl<'a, Actions: ModelActions> CreateMany<'a, Actions> {
 
 impl<'a, Actions: ModelActions> Query<'a> for CreateMany<'a, Actions> {
     type RawType = BatchResult;
-    type ReturnType = i64;
+    type ReturnValue = i64;
 
     fn graphql(self) -> (Operation, &'a PrismaClientInternals) {
         (
@@ -78,7 +78,7 @@ impl<'a, Actions: ModelActions> Query<'a> for CreateMany<'a, Actions> {
         )
     }
 
-    fn convert(raw: Self::RawType) -> Self::ReturnType {
+    fn convert(raw: Self::RawType) -> Self::ReturnValue {
         raw.count
     }
 }
diff --git a/src/queries/delete.rs b/src/queries/delete.rs
index 6bb3301b..f7295026 100644
--- a/src/queries/delete.rs
+++ b/src/queries/delete.rs
@@ -70,7 +70,7 @@ impl<'a, Actions: ModelActions> Delete<'a, Actions> {
 
 impl<'a, Actions: ModelActions> Query<'a> for Delete<'a, Actions> {
     type RawType = Actions::Data;
-    type ReturnType = Actions::Data;
+    type ReturnValue = Actions::Data;
 
     fn graphql(self) -> (Operation, &'a PrismaClientInternals) {
         let mut scalar_selections = Actions::scalar_selections();
@@ -83,7 +83,7 @@ impl<'a, Actions: ModelActions> Query<'a> for Delete<'a, Actions> {
         )
     }
 
-    fn convert(raw: Self::RawType) -> Self::ReturnType {
+    fn convert(raw: Self::RawType) -> Self::ReturnValue {
         raw
     }
 }
diff --git a/src/queries/delete_many.rs b/src/queries/delete_many.rs
index 4a45939b..ab7cf9ec 100644
--- a/src/queries/delete_many.rs
+++ b/src/queries/delete_many.rs
@@ -30,7 +30,7 @@ impl<'a, Actions: ModelActions> DeleteMany<'a, Actions> {
 
 impl<'a, Actions: ModelActions> Query<'a> for DeleteMany<'a, Actions> {
     type RawType = BatchResult;
-    type ReturnType = i64;
+    type ReturnValue = i64;
 
     fn graphql(self) -> (Operation, &'a PrismaClientInternals) {
         (
@@ -56,7 +56,7 @@ impl<'a, Actions: ModelActions> Query<'a> for DeleteMany<'a, Actions> {
         )
     }
 
-    fn convert(raw: Self::RawType) -> Self::ReturnType {
+    fn convert(raw: Self::RawType) -> Self::ReturnValue {
         Self::convert(raw)
     }
 }
diff --git a/src/queries/execute_raw.rs b/src/queries/execute_raw.rs
index baa210e9..cf3bff6e 100644
--- a/src/queries/execute_raw.rs
+++ b/src/queries/execute_raw.rs
@@ -28,7 +28,7 @@ impl<'a> ExecuteRaw<'a> {
 
 impl<'a> Query<'a> for ExecuteRaw<'a> {
     type RawType = i64;
-    type ReturnType = Self::RawType;
+    type ReturnValue = Self::RawType;
 
     fn graphql(self) -> (Operation, &'a PrismaClientInternals) {
         (
@@ -48,7 +48,7 @@ impl<'a> Query<'a> for ExecuteRaw<'a> {
         )
     }
 
-    fn convert(raw: Self::RawType) -> Self::ReturnType {
+    fn convert(raw: Self::RawType) -> Self::ReturnValue {
         raw
     }
 }
diff --git a/src/queries/find_first.rs b/src/queries/find_first.rs
index b45b775f..63ef28c2 100644
--- a/src/queries/find_first.rs
+++ b/src/queries/find_first.rs
@@ -157,7 +157,7 @@ impl<'a, Actions: ModelActions> FindFirst<'a, Actions> {
 
 impl<'a, Actions: ModelActions> Query<'a> for FindFirst<'a, Actions> {
     type RawType = Option<Actions::Data>;
-    type ReturnType = Self::RawType;
+    type ReturnValue = Self::RawType;
 
     fn graphql(self) -> (Operation, &'a PrismaClientInternals) {
         let mut scalar_selections = Actions::scalar_selections();
@@ -177,7 +177,7 @@ impl<'a, Actions: ModelActions> Query<'a> for FindFirst<'a, Actions> {
         )
     }
 
-    fn convert(raw: Self::RawType) -> Self::ReturnType {
+    fn convert(raw: Self::RawType) -> Self::ReturnValue {
         raw
     }
 }
diff --git a/src/queries/find_many.rs b/src/queries/find_many.rs
index 345f2879..b3af7584 100644
--- a/src/queries/find_many.rs
+++ b/src/queries/find_many.rs
@@ -157,7 +157,7 @@ impl<'a, Actions: ModelActions> FindMany<'a, Actions> {
 
 impl<'a, Actions: ModelActions> Query<'a> for FindMany<'a, Actions> {
     type RawType = Vec<Actions::Data>;
-    type ReturnType = Self::RawType;
+    type ReturnValue = Self::RawType;
 
     fn graphql(self) -> (Operation, &'a PrismaClientInternals) {
         let mut scalar_selections = Actions::scalar_selections();
@@ -177,7 +177,7 @@ impl<'a, Actions: ModelActions> Query<'a> for FindMany<'a, Actions> {
         )
     }
 
-    fn convert(raw: Self::RawType) -> Self::ReturnType {
+    fn convert(raw: Self::RawType) -> Self::ReturnValue {
         raw
     }
 }
diff --git a/src/queries/find_unique.rs b/src/queries/find_unique.rs
index 97930103..5eea2192 100644
--- a/src/queries/find_unique.rs
+++ b/src/queries/find_unique.rs
@@ -73,7 +73,7 @@ impl<'a, Actions: ModelActions> FindUnique<'a, Actions> {
 
 impl<'a, Actions: ModelActions> Query<'a> for FindUnique<'a, Actions> {
     type RawType = Option<Actions::Data>;
-    type ReturnType = Self::RawType;
+    type ReturnValue = Self::RawType;
 
     fn graphql(self) -> (Operation, &'a PrismaClientInternals) {
         let mut scalar_selections = Actions::scalar_selections();
@@ -86,7 +86,7 @@ impl<'a, Actions: ModelActions> Query<'a> for FindUnique<'a, Actions> {
         )
     }
 
-    fn convert(raw: Self::RawType) -> Self::ReturnType {
+    fn convert(raw: Self::RawType) -> Self::ReturnValue {
         raw
     }
 }
diff --git a/src/queries/include.rs b/src/queries/include.rs
index 4ef5fc32..c2476b3c 100644
--- a/src/queries/include.rs
+++ b/src/queries/include.rs
@@ -35,13 +35,13 @@ impl<'a, Data: query::Data> Include<'a, Data> {
 
 impl<'a, Data: query::Data> Query<'a> for Include<'a, Data> {
     type RawType = Data;
-    type ReturnType = Self::RawType;
+    type ReturnValue = Self::RawType;
 
     fn graphql(self) -> (Operation, &'a PrismaClientInternals) {
         (self.operation, self.client)
     }
 
-    fn convert(raw: Self::RawType) -> Self::ReturnType {
+    fn convert(raw: Self::RawType) -> Self::ReturnValue {
         raw
     }
 }
diff --git a/src/queries/mod.rs b/src/queries/mod.rs
index 12658d3b..eac84922 100644
--- a/src/queries/mod.rs
+++ b/src/queries/mod.rs
@@ -100,7 +100,7 @@ impl Into<(String, prisma_models::PrismaValue)> for SerializedWhereInput {
 
 pub(crate) async fn exec<'a, Q: Query<'a>>(
     query: Q,
-) -> error::Result<<Q as Query<'a>>::ReturnType> {
+) -> error::Result<<Q as Query<'a>>::ReturnValue> {
     let (op, client) = query.graphql();
     let res = client.execute(op).await;
 
diff --git a/src/queries/query.rs b/src/queries/query.rs
index cc720c62..cc985c9d 100644
--- a/src/queries/query.rs
+++ b/src/queries/query.rs
@@ -6,13 +6,13 @@ use crate::{PrismaClientInternals, WhereInput};
 
 pub trait Query<'a> {
     type RawType: DeserializeOwned;
-    type ReturnType: 'static;
+    type ReturnValue: 'static;
 
     fn graphql(self) -> (Operation, &'a PrismaClientInternals);
 
     /// Function for converting between raw database data and the type expected by the user.
     /// Necessary for things like raw queries
-    fn convert(raw: Self::RawType) -> Self::ReturnType;
+    fn convert(raw: Self::RawType) -> Self::ReturnValue;
 }
 
 pub trait ModelActions {
diff --git a/src/queries/query_raw.rs b/src/queries/query_raw.rs
index f8b1d239..0d5a4bfe 100644
--- a/src/queries/query_raw.rs
+++ b/src/queries/query_raw.rs
@@ -65,7 +65,7 @@ where
     Data: DeserializeOwned + 'static,
 {
     type RawType = RawOperationData;
-    type ReturnType = Vec<Data>;
+    type ReturnValue = Vec<Data>;
 
     fn graphql(self) -> (Operation, &'a PrismaClientInternals) {
         (
@@ -85,7 +85,7 @@ where
         )
     }
 
-    fn convert(raw: Self::RawType) -> Self::ReturnType {
+    fn convert(raw: Self::RawType) -> Self::ReturnValue {
         Self::convert(raw).unwrap()
     }
 }
diff --git a/src/queries/select.rs b/src/queries/select.rs
index f523a01e..0fca8a15 100644
--- a/src/queries/select.rs
+++ b/src/queries/select.rs
@@ -35,13 +35,13 @@ impl<'a, Data: DeserializeOwned> Select<'a, Data> {
 
 impl<'a, Data: DeserializeOwned + 'static> Query<'a> for Select<'a, Data> {
     type RawType = Data;
-    type ReturnType = Self::RawType;
+    type ReturnValue = Self::RawType;
 
     fn graphql(self) -> (Operation, &'a PrismaClientInternals) {
         (self.operation, self.client)
     }
 
-    fn convert(raw: Self::RawType) -> Self::ReturnType {
+    fn convert(raw: Self::RawType) -> Self::ReturnValue {
         raw
     }
 }
diff --git a/src/queries/update.rs b/src/queries/update.rs
index b79c4f94..df6dcda2 100644
--- a/src/queries/update.rs
+++ b/src/queries/update.rs
@@ -92,7 +92,7 @@ impl<'a, Actions: ModelActions> Update<'a, Actions> {
 
 impl<'a, Actions: ModelActions> Query<'a> for Update<'a, Actions> {
     type RawType = Actions::Data;
-    type ReturnType = Self::RawType;
+    type ReturnValue = Self::RawType;
 
     fn graphql(self) -> (Operation, &'a PrismaClientInternals) {
         let mut scalar_selections = Actions::scalar_selections();
@@ -109,7 +109,7 @@ impl<'a, Actions: ModelActions> Query<'a> for Update<'a, Actions> {
         )
     }
 
-    fn convert(raw: Self::RawType) -> Self::ReturnType {
+    fn convert(raw: Self::RawType) -> Self::ReturnValue {
         raw
     }
 }
diff --git a/src/queries/update_many.rs b/src/queries/update_many.rs
index e8e1d095..4ce7ce1f 100644
--- a/src/queries/update_many.rs
+++ b/src/queries/update_many.rs
@@ -32,7 +32,7 @@ impl<'a, Actions: ModelActions> UpdateMany<'a, Actions> {
 
 impl<'a, Actions: ModelActions> Query<'a> for UpdateMany<'a, Actions> {
     type RawType = BatchResult;
-    type ReturnType = i64;
+    type ReturnValue = i64;
 
     fn graphql(self) -> (Operation, &'a PrismaClientInternals) {
         (
@@ -67,7 +67,7 @@ impl<'a, Actions: ModelActions> Query<'a> for UpdateMany<'a, Actions> {
         )
     }
 
-    fn convert(raw: Self::RawType) -> Self::ReturnType {
+    fn convert(raw: Self::RawType) -> Self::ReturnValue {
         raw.count
     }
 }
diff --git a/src/queries/upsert.rs b/src/queries/upsert.rs
index a6327dbe..c7fd33a6 100644
--- a/src/queries/upsert.rs
+++ b/src/queries/upsert.rs
@@ -97,7 +97,7 @@ impl<'a, Actions: ModelActions> Upsert<'a, Actions> {
 
 impl<'a, Actions: ModelActions> Query<'a> for Upsert<'a, Actions> {
     type RawType = Actions::Data;
-    type ReturnType = Self::RawType;
+    type ReturnValue = Self::RawType;
 
     fn graphql(self) -> (Operation, &'a PrismaClientInternals) {
         let mut scalar_selections = Actions::scalar_selections();
@@ -115,7 +115,7 @@ impl<'a, Actions: ModelActions> Query<'a> for Upsert<'a, Actions> {
         )
     }
 
-    fn convert(raw: Self::RawType) -> Self::ReturnType {
+    fn convert(raw: Self::RawType) -> Self::ReturnValue {
         raw
     }
 }


Test patch:
diff --git a/integration-tests/tests/batch.rs b/integration-tests/tests/batch.rs
index 8417d8aa..047daa0f 100644
--- a/integration-tests/tests/batch.rs
+++ b/integration-tests/tests/batch.rs
@@ -38,6 +38,53 @@ async fn vec() -> TestResult {
     cleanup(client).await
 }
 
+#[tokio::test]
+async fn tuple_in_vec() -> TestResult {
+    let client = client().await;
+
+    let users = client
+        ._batch(vec![
+            (
+                client.user().create("Brendan".to_string(), vec![]),
+                client.user().create("Brendan".to_string(), vec![]),
+            ),
+            (
+                client.user().create("Oscar".to_string(), vec![]),
+                client.user().create("Oscar".to_string(), vec![]),
+            ),
+        ])
+        .await?;
+
+    assert_eq!(users.len(), 2);
+    assert_eq!(&users[0].1.name, "Brendan");
+    assert_eq!(&users[1].1.name, "Oscar");
+
+    cleanup(client).await
+}
+
+#[tokio::test]
+async fn vec_in_tuple() -> TestResult {
+    let client = client().await;
+
+    let (brendan, oscar) = client
+        ._batch((
+            vec![
+                client.user().create("Brendan".to_string(), vec![]),
+                client.user().create("Brendan".to_string(), vec![]),
+            ],
+            vec![
+                client.user().create("Oscar".to_string(), vec![]),
+                client.user().create("Oscar".to_string(), vec![]),
+            ],
+        ))
+        .await?;
+
+    assert_eq!(&brendan[1].name, "Brendan");
+    assert_eq!(&oscar[1].name, "Oscar");
+
+    cleanup(client).await
+}
+
 #[tokio::test]
 async fn error() -> TestResult {
     let client = client().await;