Skip to content

Commit

Permalink
fix one2m & m2m ordering (using limit hack)
Browse files Browse the repository at this point in the history
  • Loading branch information
Weakky committed Jan 18, 2024
1 parent 8bf285c commit b93c7f1
Show file tree
Hide file tree
Showing 7 changed files with 23 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,7 @@ const CAPABILITIES: ConnectorCapabilities = enumflags2::make_bitflags!(Connector
RowIn |
DeleteReturning |
SupportsFiltersOnRelationsWithoutJoins |
LateralJoin |
LateralJoinM2MOrdering
LateralJoin
});

const SCALAR_TYPE_DEFAULTS: &[(ScalarType, CockroachType)] = &[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,7 @@ const CAPABILITIES: ConnectorCapabilities = enumflags2::make_bitflags!(Connector
DistinctOn |
DeleteReturning |
SupportsFiltersOnRelationsWithoutJoins |
LateralJoin |
LateralJoinM2MOrdering
LateralJoin
});

pub struct PostgresDatamodelConnector;
Expand Down
1 change: 0 additions & 1 deletion psl/psl-core/src/datamodel_connector/capabilities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@ capabilities!(
DeleteReturning, // Connector supports deleting records and returning them in one operation.
SupportsFiltersOnRelationsWithoutJoins, // Connector supports rendering filters on relation fields without joins.
LateralJoin, // Connector supports lateral joins to resolve relations.
LateralJoinM2MOrdering // Connector supports ordering M2M relations with lateral joins.
);

/// Contains all capabilities that the connector is able to serve.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use psl::datamodel_connector::Flavour;
use std::borrow::Cow;
use tracing::Span;

Expand Down Expand Up @@ -133,6 +134,12 @@ impl SelectBuilder {

let inner = inner.with_columns(inner_selection.into()).comment("inner select");

let take = match is_mysql(rs) {
// On MySQL, using LIMIT makes the ordering of the JSON_AGG working. Beware, this is undocumented behavior.
true => rs.args.take_abs().or(Some(i64::MAX)),
false => rs.args.take_abs(),
};

let middle = Select::from_table(Table::from(inner).alias(inner_alias.to_table_string()))
// SELECT <inner_alias>.<JSON_ADD_IDENT>
.column(Column::from((inner_alias.to_table_string(), JSON_AGG_IDENT)))
Expand All @@ -141,7 +148,7 @@ impl SelectBuilder {
// WHERE ...
.with_filters(rs.args.filter.clone(), Some(inner_alias), ctx)
// LIMIT $1 OFFSET $2
.with_pagination(rs.args.take_abs(), rs.args.skip)
.with_pagination(take, rs.args.skip)
.comment("middle select");

// SELECT COALESCE(JSON_AGG(<inner_alias>), '[]') AS <inner_alias> FROM ( <middle> ) as <inner_alias_2>
Expand Down Expand Up @@ -181,14 +188,19 @@ impl SelectBuilder {
.lateral();

let child_table = rf.as_table(ctx).alias(m2m_table_alias.to_table_string());
let take = match is_mysql(rs) {
// On MySQL, using LIMIT makes the ordering of the JSON_AGG working. Beware, this is undocumented behavior.
true => rs.args.take_abs().or(Some(i64::MAX)),
false => rs.args.take_abs(),
};

let inner = Select::from_table(child_table)
.value(Column::from((m2m_join_alias.to_table_string(), JSON_AGG_IDENT)))
.left_join(m2m_join_data) // join m2m table
.and_where(join_conditions) // adds join condition to the child table
.with_ordering(&rs.args, Some(m2m_join_alias.to_table_string()), ctx) // adds ordering stmts
.with_filters(rs.args.filter.clone(), Some(m2m_join_alias), ctx) // adds query filters // TODO: avoid clone filter
.with_pagination(rs.args.take_abs(), rs.args.skip)
.with_pagination(take, rs.args.skip)
.comment("inner"); // adds pagination

let outer = Select::from_table(Table::from(inner).alias(outer_alias.to_table_string()))
Expand Down Expand Up @@ -415,3 +427,9 @@ fn json_agg() -> Function<'static> {
fn empty_json_array() -> serde_json::Value {
serde_json::Value::Array(Vec::new())
}

// TODO: Hack to get around the fact that we don't have a way to know if we're on mysql or not
// TODO: Remove this once we have a proper way to know the connector type
fn is_mysql(rs: &RelationSelection) -> bool {
rs.args.model().dm.schema.connector.flavour() == Flavour::Mysql
}
1 change: 0 additions & 1 deletion query-engine/core/src/query_graph_builder/read/many.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ fn find_many_with_options(
args.cursor.as_ref(),
args.distinct.as_ref(),
&nested,
&selected_fields,
&aggregation_selections,
query_schema,
);
Expand Down
1 change: 0 additions & 1 deletion query-engine/core/src/query_graph_builder/read/one.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ fn find_unique_with_options(
None,
None,
&nested,
&selected_fields,
&aggregation_selections,
query_schema,
);
Expand Down
11 changes: 1 addition & 10 deletions query-engine/core/src/query_graph_builder/read/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,18 +247,9 @@ pub(crate) fn get_relation_load_strategy(
cursor: Option<&SelectionResult>,
distinct: Option<&FieldSelection>,
nested_queries: &[ReadQuery],
field_selection: &FieldSelection,
aggregation_selections: &[RelAggregationSelection],
query_schema: &QuerySchema,
) -> RelationLoadStrategy {
// MySQL does not support M2M ordering with lateral joins.
// We fallback to the query-based strategy when a m2m relation with ordering is present.
let has_ordered_m2m = field_selection
.relations()
.any(|rf| rf.field.relation().is_many_to_many() && !rf.args.order_by.is_empty());
let can_order_m2m = query_schema.has_capability(ConnectorCapability::LateralJoinM2MOrdering);
let supports_m2m_ordering = !has_ordered_m2m || can_order_m2m;

if query_schema.has_feature(PreviewFeature::RelationJoins)
&& query_schema.has_capability(ConnectorCapability::LateralJoin)
&& cursor.is_none()
Expand All @@ -268,7 +259,7 @@ pub(crate) fn get_relation_load_strategy(
ReadQuery::RelatedRecordsQuery(q) => q.has_cursor() || q.has_distinct() || q.has_aggregation_selections(),
_ => false,
})
&& supports_m2m_ordering
// && supports_m2m_ordering
&& requested_strategy != Some(RelationLoadStrategy::Query)
{
RelationLoadStrategy::Join
Expand Down

0 comments on commit b93c7f1

Please sign in to comment.