Skip to content

Commit e6f5cb6

Browse files
buraksennalamb
andauthored
[minor] make recursive package dependency optional (#13778)
* make recursive optional * add to default for common package * cargo update * added to readme * make test conditional * reviews * cargo update --------- Co-authored-by: Andrew Lamb <[email protected]>
1 parent a267784 commit e6f5cb6

File tree

17 files changed

+54
-50
lines changed

17 files changed

+54
-50
lines changed

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@ Default features:
112112
- `parquet`: support for reading the [Apache Parquet] format
113113
- `regex_expressions`: regular expression functions, such as `regexp_match`
114114
- `unicode_expressions`: Include unicode aware functions such as `character_length`
115-
- `unparser` : enables support to reverse LogicalPlans back into SQL
115+
- `unparser`: enables support to reverse LogicalPlans back into SQL
116+
- `recursive-protection`: uses [recursive](https://docs.rs/recursive/latest/recursive/) for stack overflow protection.
116117

117118
Optional features:
118119

datafusion-cli/Cargo.lock

-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

datafusion/common/Cargo.toml

+3-1
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,12 @@ name = "datafusion_common"
3636
path = "src/lib.rs"
3737

3838
[features]
39+
default = ["recursive-protection"]
3940
avro = ["apache-avro"]
4041
backtrace = []
4142
pyarrow = ["pyo3", "arrow/pyarrow", "parquet"]
4243
force_hash_collisions = []
44+
recursive-protection = ["dep:recursive"]
4345

4446
[dependencies]
4547
ahash = { workspace = true }
@@ -62,7 +64,7 @@ object_store = { workspace = true, optional = true }
6264
parquet = { workspace = true, optional = true, default-features = true }
6365
paste = "1.0.15"
6466
pyo3 = { version = "0.22.0", optional = true }
65-
recursive = { workspace = true }
67+
recursive = { workspace = true, optional = true }
6668
sqlparser = { workspace = true }
6769
tokio = { workspace = true }
6870

datafusion/common/src/tree_node.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
//! [`TreeNode`] for visiting and rewriting expression and plan trees
1919
2020
use crate::Result;
21-
use recursive::recursive;
2221
use std::collections::HashMap;
2322
use std::hash::Hash;
2423
use std::sync::Arc;
@@ -125,7 +124,7 @@ pub trait TreeNode: Sized {
125124
/// TreeNodeVisitor::f_up(ChildNode2)
126125
/// TreeNodeVisitor::f_up(ParentNode)
127126
/// ```
128-
#[recursive]
127+
#[cfg_attr(feature = "recursive-protection", recursive::recursive)]
129128
fn visit<'n, V: TreeNodeVisitor<'n, Node = Self>>(
130129
&'n self,
131130
visitor: &mut V,
@@ -175,7 +174,7 @@ pub trait TreeNode: Sized {
175174
/// TreeNodeRewriter::f_up(ChildNode2)
176175
/// TreeNodeRewriter::f_up(ParentNode)
177176
/// ```
178-
#[recursive]
177+
#[cfg_attr(feature = "recursive-protection", recursive::recursive)]
179178
fn rewrite<R: TreeNodeRewriter<Node = Self>>(
180179
self,
181180
rewriter: &mut R,
@@ -198,7 +197,7 @@ pub trait TreeNode: Sized {
198197
&'n self,
199198
mut f: F,
200199
) -> Result<TreeNodeRecursion> {
201-
#[recursive]
200+
#[cfg_attr(feature = "recursive-protection", recursive::recursive)]
202201
fn apply_impl<'n, N: TreeNode, F: FnMut(&'n N) -> Result<TreeNodeRecursion>>(
203202
node: &'n N,
204203
f: &mut F,
@@ -233,7 +232,7 @@ pub trait TreeNode: Sized {
233232
self,
234233
mut f: F,
235234
) -> Result<Transformed<Self>> {
236-
#[recursive]
235+
#[cfg_attr(feature = "recursive-protection", recursive::recursive)]
237236
fn transform_down_impl<N: TreeNode, F: FnMut(N) -> Result<Transformed<N>>>(
238237
node: N,
239238
f: &mut F,
@@ -257,7 +256,7 @@ pub trait TreeNode: Sized {
257256
self,
258257
mut f: F,
259258
) -> Result<Transformed<Self>> {
260-
#[recursive]
259+
#[cfg_attr(feature = "recursive-protection", recursive::recursive)]
261260
fn transform_up_impl<N: TreeNode, F: FnMut(N) -> Result<Transformed<N>>>(
262261
node: N,
263262
f: &mut F,
@@ -372,7 +371,7 @@ pub trait TreeNode: Sized {
372371
mut f_down: FD,
373372
mut f_up: FU,
374373
) -> Result<Transformed<Self>> {
375-
#[recursive]
374+
#[cfg_attr(feature = "recursive-protection", recursive::recursive)]
376375
fn transform_down_up_impl<
377376
N: TreeNode,
378377
FD: FnMut(N) -> Result<Transformed<N>>,
@@ -2350,6 +2349,7 @@ pub(crate) mod tests {
23502349
Ok(())
23512350
}
23522351

2352+
#[cfg(feature = "recursive-protection")]
23532353
#[test]
23542354
fn test_large_tree() {
23552355
let mut item = TestTreeNode::new_leaf("initial".to_string());

datafusion/expr/Cargo.toml

+3-1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ name = "datafusion_expr"
3636
path = "src/lib.rs"
3737

3838
[features]
39+
default = ["recursive-protection"]
40+
recursive-protection = ["dep:recursive"]
3941

4042
[dependencies]
4143
arrow = { workspace = true }
@@ -48,7 +50,7 @@ datafusion-functions-window-common = { workspace = true }
4850
datafusion-physical-expr-common = { workspace = true }
4951
indexmap = { workspace = true }
5052
paste = "^1.0"
51-
recursive = { workspace = true }
53+
recursive = { workspace = true, optional = true }
5254
serde_json = { workspace = true }
5355
sqlparser = { workspace = true }
5456

datafusion/expr/src/expr_schema.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ use datafusion_common::{
3232
TableReference,
3333
};
3434
use datafusion_functions_window_common::field::WindowUDFFieldArgs;
35-
use recursive::recursive;
3635
use std::collections::HashMap;
3736
use std::sync::Arc;
3837

@@ -100,7 +99,7 @@ impl ExprSchemable for Expr {
10099
/// expression refers to a column that does not exist in the
101100
/// schema, or when the expression is incorrectly typed
102101
/// (e.g. `[utf8] + [bool]`).
103-
#[recursive]
102+
#[cfg_attr(feature = "recursive-protection", recursive::recursive)]
104103
fn get_type(&self, schema: &dyn ExprSchema) -> Result<DataType> {
105104
match self {
106105
Expr::Alias(Alias { expr, name, .. }) => match &**expr {

datafusion/expr/src/logical_plan/tree_node.rs

+6-7
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ use crate::{
4545
UserDefinedLogicalNode, Values, Window,
4646
};
4747
use datafusion_common::tree_node::TreeNodeRefContainer;
48-
use recursive::recursive;
4948

5049
use crate::expr::{Exists, InSubquery};
5150
use datafusion_common::tree_node::{
@@ -669,7 +668,7 @@ impl LogicalPlan {
669668

670669
/// Visits a plan similarly to [`Self::visit`], including subqueries that
671670
/// may appear in expressions such as `IN (SELECT ...)`.
672-
#[recursive]
671+
#[cfg_attr(feature = "recursive-protection", recursive::recursive)]
673672
pub fn visit_with_subqueries<V: for<'n> TreeNodeVisitor<'n, Node = Self>>(
674673
&self,
675674
visitor: &mut V,
@@ -688,7 +687,7 @@ impl LogicalPlan {
688687
/// Similarly to [`Self::rewrite`], rewrites this node and its inputs using `f`,
689688
/// including subqueries that may appear in expressions such as `IN (SELECT
690689
/// ...)`.
691-
#[recursive]
690+
#[cfg_attr(feature = "recursive-protection", recursive::recursive)]
692691
pub fn rewrite_with_subqueries<R: TreeNodeRewriter<Node = Self>>(
693692
self,
694693
rewriter: &mut R,
@@ -707,7 +706,7 @@ impl LogicalPlan {
707706
&self,
708707
mut f: F,
709708
) -> Result<TreeNodeRecursion> {
710-
#[recursive]
709+
#[cfg_attr(feature = "recursive-protection", recursive::recursive)]
711710
fn apply_with_subqueries_impl<
712711
F: FnMut(&LogicalPlan) -> Result<TreeNodeRecursion>,
713712
>(
@@ -742,7 +741,7 @@ impl LogicalPlan {
742741
self,
743742
mut f: F,
744743
) -> Result<Transformed<Self>> {
745-
#[recursive]
744+
#[cfg_attr(feature = "recursive-protection", recursive::recursive)]
746745
fn transform_down_with_subqueries_impl<
747746
F: FnMut(LogicalPlan) -> Result<Transformed<LogicalPlan>>,
748747
>(
@@ -767,7 +766,7 @@ impl LogicalPlan {
767766
self,
768767
mut f: F,
769768
) -> Result<Transformed<Self>> {
770-
#[recursive]
769+
#[cfg_attr(feature = "recursive-protection", recursive::recursive)]
771770
fn transform_up_with_subqueries_impl<
772771
F: FnMut(LogicalPlan) -> Result<Transformed<LogicalPlan>>,
773772
>(
@@ -795,7 +794,7 @@ impl LogicalPlan {
795794
mut f_down: FD,
796795
mut f_up: FU,
797796
) -> Result<Transformed<Self>> {
798-
#[recursive]
797+
#[cfg_attr(feature = "recursive-protection", recursive::recursive)]
799798
fn transform_down_up_with_subqueries_impl<
800799
FD: FnMut(LogicalPlan) -> Result<Transformed<LogicalPlan>>,
801800
FU: FnMut(LogicalPlan) -> Result<Transformed<LogicalPlan>>,

datafusion/optimizer/Cargo.toml

+5-1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ workspace = true
3535
name = "datafusion_optimizer"
3636
path = "src/lib.rs"
3737

38+
[features]
39+
default = ["recursive-protection"]
40+
recursive-protection = ["dep:recursive"]
41+
3842
[dependencies]
3943
arrow = { workspace = true }
4044
chrono = { workspace = true }
@@ -44,7 +48,7 @@ datafusion-physical-expr = { workspace = true }
4448
indexmap = { workspace = true }
4549
itertools = { workspace = true }
4650
log = { workspace = true }
47-
recursive = { workspace = true }
51+
recursive = { workspace = true, optional = true }
4852
regex = { workspace = true }
4953
regex-syntax = "0.8.0"
5054

datafusion/optimizer/src/analyzer/subquery.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818
use crate::analyzer::check_plan;
1919
use crate::utils::collect_subquery_cols;
20-
use recursive::recursive;
2120

2221
use datafusion_common::tree_node::{TreeNode, TreeNodeRecursion};
2322
use datafusion_common::{plan_err, Result};
@@ -79,7 +78,7 @@ pub fn check_subquery_expr(
7978
match outer_plan {
8079
LogicalPlan::Projection(_)
8180
| LogicalPlan::Filter(_) => Ok(()),
82-
LogicalPlan::Aggregate(Aggregate {group_expr, aggr_expr,..}) => {
81+
LogicalPlan::Aggregate(Aggregate { group_expr, aggr_expr, .. }) => {
8382
if group_expr.contains(expr) && !aggr_expr.contains(expr) {
8483
// TODO revisit this validation logic
8584
plan_err!(
@@ -88,7 +87,7 @@ pub fn check_subquery_expr(
8887
} else {
8988
Ok(())
9089
}
91-
},
90+
}
9291
_ => plan_err!(
9392
"Correlated scalar subquery can only be used in Projection, Filter, Aggregate plan nodes"
9493
)
@@ -129,7 +128,7 @@ fn check_correlations_in_subquery(inner_plan: &LogicalPlan) -> Result<()> {
129128
}
130129

131130
// Recursively check the unsupported outer references in the sub query plan.
132-
#[recursive]
131+
#[cfg_attr(feature = "recursive-protection", recursive::recursive)]
133132
fn check_inner_plan(inner_plan: &LogicalPlan, can_contain_outer_ref: bool) -> Result<()> {
134133
if !can_contain_outer_ref && inner_plan.contains_outer_reference() {
135134
return plan_err!("Accessing outer reference columns is not allowed in the plan");

datafusion/optimizer/src/common_subexpr_eliminate.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ use std::fmt::Debug;
2222
use std::sync::Arc;
2323

2424
use crate::{OptimizerConfig, OptimizerRule};
25-
use recursive::recursive;
2625

2726
use crate::optimizer::ApplyOrder;
2827
use crate::utils::NamePreserver;
@@ -532,7 +531,7 @@ impl OptimizerRule for CommonSubexprEliminate {
532531
None
533532
}
534533

535-
#[recursive]
534+
#[cfg_attr(feature = "recursive-protection", recursive::recursive)]
536535
fn rewrite(
537536
&self,
538537
plan: LogicalPlan,
@@ -952,7 +951,7 @@ mod test {
952951
)?
953952
.build()?;
954953

955-
let expected ="Aggregate: groupBy=[[]], aggr=[[avg(__common_expr_1) AS col1, my_agg(__common_expr_1) AS col2]]\
954+
let expected = "Aggregate: groupBy=[[]], aggr=[[avg(__common_expr_1) AS col1, my_agg(__common_expr_1) AS col2]]\
956955
\n Projection: UInt32(1) + test.a AS __common_expr_1, test.a, test.b, test.c\
957956
\n TableScan: test";
958957

datafusion/optimizer/src/eliminate_cross_join.rs

+10-11
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818
//! [`EliminateCrossJoin`] converts `CROSS JOIN` to `INNER JOIN` if join predicates are available.
1919
use crate::{OptimizerConfig, OptimizerRule};
20-
use recursive::recursive;
2120
use std::sync::Arc;
2221

2322
use crate::join_key_set::JoinKeySet;
@@ -80,7 +79,7 @@ impl OptimizerRule for EliminateCrossJoin {
8079
true
8180
}
8281

83-
#[recursive]
82+
#[cfg_attr(feature = "recursive-protection", recursive::recursive)]
8483
fn rewrite(
8584
&self,
8685
plan: LogicalPlan,
@@ -651,7 +650,7 @@ mod tests {
651650
" Inner Join: t1.a = t2.a [a:UInt32, b:UInt32, c:UInt32, a:UInt32, b:UInt32, c:UInt32, a:UInt32, b:UInt32, c:UInt32]",
652651
" Inner Join: t1.a = t3.a [a:UInt32, b:UInt32, c:UInt32, a:UInt32, b:UInt32, c:UInt32]",
653652
" TableScan: t1 [a:UInt32, b:UInt32, c:UInt32]",
654-
" TableScan: t3 [a:UInt32, b:UInt32, c:UInt32]",
653+
" TableScan: t3 [a:UInt32, b:UInt32, c:UInt32]",
655654
" TableScan: t2 [a:UInt32, b:UInt32, c:UInt32]"
656655
];
657656

@@ -1237,10 +1236,10 @@ mod tests {
12371236
.build()?;
12381237

12391238
let expected = vec![
1240-
"Filter: t1.a + UInt32(100) = t2.a * UInt32(2) OR t2.b = t1.a [a:UInt32, b:UInt32, c:UInt32, a:UInt32, b:UInt32, c:UInt32]",
1241-
" Cross Join: [a:UInt32, b:UInt32, c:UInt32, a:UInt32, b:UInt32, c:UInt32]",
1242-
" TableScan: t1 [a:UInt32, b:UInt32, c:UInt32]",
1243-
" TableScan: t2 [a:UInt32, b:UInt32, c:UInt32]",
1239+
"Filter: t1.a + UInt32(100) = t2.a * UInt32(2) OR t2.b = t1.a [a:UInt32, b:UInt32, c:UInt32, a:UInt32, b:UInt32, c:UInt32]",
1240+
" Cross Join: [a:UInt32, b:UInt32, c:UInt32, a:UInt32, b:UInt32, c:UInt32]",
1241+
" TableScan: t1 [a:UInt32, b:UInt32, c:UInt32]",
1242+
" TableScan: t2 [a:UInt32, b:UInt32, c:UInt32]",
12441243
];
12451244

12461245
assert_optimized_plan_eq(plan, expected);
@@ -1293,10 +1292,10 @@ mod tests {
12931292
.build()?;
12941293

12951294
let expected = vec![
1296-
"Filter: t2.c < UInt32(15) OR t2.c = UInt32(688) [a:UInt32, b:UInt32, c:UInt32, a:UInt32, b:UInt32, c:UInt32]",
1297-
" Inner Join: t1.a + UInt32(100) = t2.a * UInt32(2) [a:UInt32, b:UInt32, c:UInt32, a:UInt32, b:UInt32, c:UInt32]",
1298-
" TableScan: t1 [a:UInt32, b:UInt32, c:UInt32]",
1299-
" TableScan: t2 [a:UInt32, b:UInt32, c:UInt32]",
1295+
"Filter: t2.c < UInt32(15) OR t2.c = UInt32(688) [a:UInt32, b:UInt32, c:UInt32, a:UInt32, b:UInt32, c:UInt32]",
1296+
" Inner Join: t1.a + UInt32(100) = t2.a * UInt32(2) [a:UInt32, b:UInt32, c:UInt32, a:UInt32, b:UInt32, c:UInt32]",
1297+
" TableScan: t1 [a:UInt32, b:UInt32, c:UInt32]",
1298+
" TableScan: t2 [a:UInt32, b:UInt32, c:UInt32]",
13001299
];
13011300

13021301
assert_optimized_plan_eq(plan, expected);

datafusion/optimizer/src/optimize_projections/mod.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ mod required_indices;
2121

2222
use crate::optimizer::ApplyOrder;
2323
use crate::{OptimizerConfig, OptimizerRule};
24-
use recursive::recursive;
2524
use std::collections::HashSet;
2625
use std::sync::Arc;
2726

@@ -110,7 +109,7 @@ impl OptimizerRule for OptimizeProjections {
110109
/// columns.
111110
/// - `Ok(None)`: Signal that the given logical plan did not require any change.
112111
/// - `Err(error)`: An error occurred during the optimization process.
113-
#[recursive]
112+
#[cfg_attr(feature = "recursive-protection", recursive::recursive)]
114113
fn optimize_projections(
115114
plan: LogicalPlan,
116115
config: &dyn OptimizerConfig,

datafusion/physical-optimizer/Cargo.toml

+5-1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ rust-version = { workspace = true }
3131
[lints]
3232
workspace = true
3333

34+
[features]
35+
default = ["recursive-protection"]
36+
recursive-protection = ["dep:recursive"]
37+
3438
[dependencies]
3539
arrow = { workspace = true }
3640
datafusion-common = { workspace = true, default-features = true }
@@ -40,7 +44,7 @@ datafusion-physical-expr = { workspace = true }
4044
datafusion-physical-plan = { workspace = true }
4145
itertools = { workspace = true }
4246
log = { workspace = true }
43-
recursive = { workspace = true }
47+
recursive = { workspace = true, optional = true }
4448

4549
[dev-dependencies]
4650
datafusion-expr = { workspace = true }

datafusion/physical-optimizer/src/aggregate_statistics.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ use datafusion_physical_plan::placeholder_row::PlaceholderRowExec;
2525
use datafusion_physical_plan::projection::ProjectionExec;
2626
use datafusion_physical_plan::udaf::{AggregateFunctionExpr, StatisticsArgs};
2727
use datafusion_physical_plan::{expressions, ExecutionPlan};
28-
use recursive::recursive;
2928
use std::sync::Arc;
3029

3130
use crate::PhysicalOptimizerRule;
@@ -42,7 +41,7 @@ impl AggregateStatistics {
4241
}
4342

4443
impl PhysicalOptimizerRule for AggregateStatistics {
45-
#[recursive]
44+
#[cfg_attr(feature = "recursive-protection", recursive::recursive)]
4645
fn optimize(
4746
&self,
4847
plan: Arc<dyn ExecutionPlan>,

0 commit comments

Comments
 (0)