diff --git a/query-engine/connectors/sql-query-connector/src/database/operations/coerce.rs b/query-engine/connectors/sql-query-connector/src/database/operations/coerce.rs index b25c1fee4e16..0ad14a8098c1 100644 --- a/query-engine/connectors/sql-query-connector/src/database/operations/coerce.rs +++ b/query-engine/connectors/sql-query-connector/src/database/operations/coerce.rs @@ -75,6 +75,48 @@ fn coerce_json_relation_to_pv(value: serde_json::Value, rs: &RelationSelection) false => Either::Right(iter), }; + let iter = if rs.args.requires_inmemory_distinct_with_joins() { + Either::Left(iter.unique_by(|maybe_value| { + // Mapping errors to a unit type here does not discard the error information + // from the final iterator because the result that we return from this closure + // is only a key for comparing the elements, we do not map the elements + // themselves here. We can't use the original errors in keys because `SqlError` + // is not Eq + Hash. The consequence is that only the first error will be kept, + // but the same thing will happen when collecting the iterator to + // `Result>` anyway. This also means we have to way to introduce a new + // error and return it out of `unique_by`, so we have to panic if an element is + // not an object, but this is fine because we know we already mapped the + // elements using `coerce_json_relation_to_pv` above, so they must be objects. + // We also panic if we can't find the distinct field in the result set, which + // is less desirable but also exactly what the in-memory record processor for + // the old query strategy does. + maybe_value.as_ref().map_err(|_| ()).map(|value| { + let object = value + .clone() + .into_object() + .expect("Expected coerced_json_relation_to_pv to return list of objects"); + rs.args + .distinct + .as_ref() + .map(|distinct| { + distinct + .scalars() + .map(|sf| { + object + .iter() + // TODO: Use name instead of db_name after https://github.com/prisma/prisma-engines/pull/4732 is merged + .find_map(|(key, value)| (key == sf.db_name()).then_some(value)) + .expect("Distinct field must be present in the result") + }) + .collect::>() + }) + .unwrap_or_default(); + }) + })) + } else { + Either::Right(iter) + }; + Ok(PrismaValue::List(iter.collect::>>()?)) } serde_json::Value::Object(obj) => { diff --git a/query-engine/connectors/sql-query-connector/src/database/operations/read.rs b/query-engine/connectors/sql-query-connector/src/database/operations/read.rs index 24b576c936c2..54f52ef56330 100644 --- a/query-engine/connectors/sql-query-connector/src/database/operations/read.rs +++ b/query-engine/connectors/sql-query-connector/src/database/operations/read.rs @@ -9,6 +9,7 @@ use crate::{ use connector_interface::*; use futures::stream::{FuturesUnordered, StreamExt}; +use itertools::Itertools; use quaint::ast::*; use query_structure::*; @@ -173,6 +174,20 @@ pub(crate) async fn get_many_records_joins( records.reverse(); } + // Apply in-memory distinct if it wasn't applied on the database level. + if query_arguments.requires_inmemory_distinct_with_joins() { + let Some(distinct) = query_arguments.distinct.as_ref() else { + return Ok(records); + }; + + records.records = records + .records + .into_iter() + // TODO: we will need a different method once https://github.com/prisma/prisma-engines/pull/4732 lands. + .unique_by(|record| record.extract_selection_result(&records.field_names, distinct).unwrap()) + .collect(); + } + Ok(records) }