Skip to content

Commit f9b0a2d

Browse files
committed
Fix stack overflow calculating projected orderings
1 parent d4bc1c1 commit f9b0a2d

File tree

1 file changed

+73
-16
lines changed

1 file changed

+73
-16
lines changed

datafusion/physical-expr/src/equivalence/properties.rs

Lines changed: 73 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1295,21 +1295,32 @@ fn construct_prefix_orderings(
12951295
relevant_sort_expr: &PhysicalSortExpr,
12961296
dependency_map: &DependencyMap,
12971297
) -> Vec<LexOrdering> {
1298+
let mut processed_dependencies = IndexSet::new();
12981299
dependency_map[relevant_sort_expr]
12991300
.dependencies
13001301
.iter()
1301-
.flat_map(|dep| construct_orderings(dep, dependency_map))
1302+
.flat_map(|dep| {
1303+
construct_orderings(dep, &mut processed_dependencies, dependency_map)
1304+
})
13021305
.collect()
13031306
}
13041307

1305-
/// Given a set of relevant dependencies (`relevant_deps`) and a map of dependencies
1306-
/// (`dependency_map`), this function generates all possible prefix orderings
1307-
/// based on the given dependencies.
1308+
/// Generates all possible orderings where dependencies are satisfied for the
1309+
/// current projection expression.
1310+
///
1311+
/// # Examaple
1312+
/// If `dependences` is `a + b ASC` and the dependency map holds dependencies
1313+
/// * `a ASC` --> `[c ASC]`
1314+
/// * `b ASC` --> `[d DESC]`,
1315+
///
1316+
/// This function generates these two sort orders
1317+
/// * `[c ASC, d DESC, a + b ASC]`
1318+
/// * `[d DESC, c ASC, a + b ASC]`
13081319
///
13091320
/// # Parameters
13101321
///
1311-
/// * `dependencies` - A reference to the dependencies.
1312-
/// * `dependency_map` - A reference to the map of dependencies for expressions.
1322+
/// * `dependencies` - Set of relevant expressions.
1323+
/// * `dependency_map` - Map of dependencies for expressions that may appear in `dependencies`
13131324
///
13141325
/// # Returns
13151326
///
@@ -1335,11 +1346,6 @@ fn generate_dependency_orderings(
13351346
return vec![vec![]];
13361347
}
13371348

1338-
// Generate all possible orderings where dependencies are satisfied for the
1339-
// current projection expression. For example, if expression is `a + b ASC`,
1340-
// and the dependency for `a ASC` is `[c ASC]`, the dependency for `b ASC`
1341-
// is `[d DESC]`, then we generate `[c ASC, d DESC, a + b ASC]` and
1342-
// `[d DESC, c ASC, a + b ASC]`.
13431349
relevant_prefixes
13441350
.into_iter()
13451351
.multi_cartesian_product()
@@ -1421,7 +1427,7 @@ struct DependencyNode {
14211427
}
14221428

14231429
impl DependencyNode {
1424-
// Insert dependency to the state (if exists).
1430+
/// Insert dependency to the state (if exists).
14251431
fn insert_dependency(&mut self, dependency: Option<&PhysicalSortExpr>) {
14261432
if let Some(dep) = dependency {
14271433
self.dependencies.insert(dep.clone());
@@ -1453,10 +1459,16 @@ type Dependencies = IndexSet<PhysicalSortExpr>;
14531459
///
14541460
/// A vector of lexicographical orderings (`Vec<LexOrdering>`) based on the given
14551461
/// sort expression and its dependencies.
1456-
fn construct_orderings(
1457-
referred_sort_expr: &PhysicalSortExpr,
1458-
dependency_map: &DependencyMap,
1462+
fn construct_orderings<'a>(
1463+
referred_sort_expr: &'a PhysicalSortExpr,
1464+
processed_dependencies: &mut IndexSet<&'a PhysicalSortExpr>,
1465+
dependency_map: &'a DependencyMap,
14591466
) -> Vec<LexOrdering> {
1467+
// if we have already processed this dependency don't do it again
1468+
if !processed_dependencies.insert(referred_sort_expr) {
1469+
return vec![];
1470+
}
1471+
14601472
// We are sure that `referred_sort_expr` is inside `dependency_map`.
14611473
let node = &dependency_map[referred_sort_expr];
14621474
// Since we work on intermediate nodes, we are sure `val.target_sort_expr`
@@ -1468,7 +1480,8 @@ fn construct_orderings(
14681480
node.dependencies
14691481
.iter()
14701482
.flat_map(|dep| {
1471-
let mut orderings = construct_orderings(dep, dependency_map);
1483+
let mut orderings =
1484+
construct_orderings(dep, processed_dependencies, dependency_map);
14721485
for ordering in orderings.iter_mut() {
14731486
ordering.push(target_sort_expr.clone())
14741487
}
@@ -1763,6 +1776,50 @@ mod tests {
17631776
Ok(())
17641777
}
17651778

1779+
#[test]
1780+
fn project_equivalence_properties_test_multi() -> Result<()> {
1781+
// test multiple input orderings with equivalence properties
1782+
let input_schema = Arc::new(Schema::new(vec![
1783+
Field::new("a", DataType::Int64, true),
1784+
Field::new("b", DataType::Int64, true),
1785+
Field::new("c", DataType::Int64, true),
1786+
Field::new("d", DataType::Int64, true),
1787+
]));
1788+
1789+
let mut input_properties = EquivalenceProperties::new(Arc::clone(&input_schema));
1790+
// add equivalent ordering [a, b, c, d]
1791+
input_properties.add_new_ordering(vec![
1792+
parse_sort_expr("a", &input_schema),
1793+
parse_sort_expr("b", &input_schema),
1794+
parse_sort_expr("c", &input_schema),
1795+
parse_sort_expr("d", &input_schema),
1796+
]);
1797+
1798+
// add equivalent ordering [a, c, b, d]
1799+
input_properties.add_new_ordering(vec![
1800+
parse_sort_expr("a", &input_schema),
1801+
parse_sort_expr("c", &input_schema),
1802+
parse_sort_expr("b", &input_schema), // NB b and c are swapped
1803+
parse_sort_expr("d", &input_schema),
1804+
]);
1805+
1806+
// simply project all the columns in order
1807+
let proj_exprs = vec![
1808+
(col("a", &input_schema)?, "a".to_string()),
1809+
(col("b", &input_schema)?, "b".to_string()),
1810+
(col("c", &input_schema)?, "c".to_string()),
1811+
(col("d", &input_schema)?, "d".to_string()),
1812+
];
1813+
let projection_mapping = ProjectionMapping::try_new(&proj_exprs, &input_schema)?;
1814+
let out_properties = input_properties.project(&projection_mapping, input_schema);
1815+
1816+
assert_eq!(
1817+
out_properties.to_string(),
1818+
"order: [[a@0 ASC,b@1 ASC,c@2 ASC,d@3 ASC]]"
1819+
);
1820+
Ok(())
1821+
}
1822+
17661823
#[test]
17671824
fn test_join_equivalence_properties() -> Result<()> {
17681825
let schema = create_test_schema()?;

0 commit comments

Comments
 (0)