Skip to content

Commit c577c0f

Browse files
[PyLimit] adding skip and fetch with datafusion expr support (#1674)
1 parent cc2ec5c commit c577c0f

2 files changed

Lines changed: 21 additions & 12 deletions

File tree

crates/core/src/expr/limit.rs

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use pyo3::IntoPyObjectExt;
2222
use pyo3::prelude::*;
2323

2424
use crate::common::df_schema::PyDFSchema;
25+
use crate::expr::PyExpr;
2526
use crate::expr::logical_node::LogicalNode;
2627
use crate::sql::logical::PyLogicalPlan;
2728

@@ -64,19 +65,23 @@ impl Display for PyLimit {
6465

6566
#[pymethods]
6667
impl PyLimit {
67-
// NOTE: Upstream now has expressions for skip and fetch
68-
// TODO: Do we still want to expose these?
69-
// REF: https://github.com/apache/datafusion/pull/12836
70-
71-
// /// Retrieves the skip value for this `Limit`
72-
// fn skip(&self) -> usize {
73-
// self.limit.skip
74-
// }
68+
// Retrieves the skip expression for this `Limit`, if any.
69+
//
70+
// `LIMIT`/`OFFSET` were changed upstream to support arbitrary
71+
// expressions (not just constants), see
72+
// https://github.com/apache/datafusion/pull/13028. Callers that expect
73+
// a simple literal (the common case, e.g. `OFFSET 5`) should evaluate
74+
// the returned `PyExpr` via `Expr.python_value()`.
75+
fn skip(&self) -> PyResult<Option<PyExpr>> {
76+
Ok(self.limit.skip.as_deref().cloned().map(PyExpr::from))
77+
}
7578

76-
// /// Retrieves the fetch value for this `Limit`
77-
// fn fetch(&self) -> Option<usize> {
78-
// self.limit.fetch
79-
// }
79+
// Retrieves the fetch expression for this `Limit`, if any.
80+
//
81+
// See the note on `skip` above regarding expression-based limits.
82+
fn fetch(&self) -> PyResult<Option<PyExpr>> {
83+
Ok(self.limit.fetch.as_deref().cloned().map(PyExpr::from))
84+
}
8085

8186
/// Retrieves the input `LogicalPlan` to this `Limit` node
8287
fn input(&self) -> PyResult<Vec<PyLogicalPlan>> {

python/tests/test_expr.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,13 +115,17 @@ def test_limit(test_ctx):
115115
plan = plan.to_variant()
116116
assert isinstance(plan, Limit)
117117
assert "Skip: None" in str(plan)
118+
assert plan.skip() is None
119+
assert plan.fetch().python_value().as_py() == 10
118120

119121
df = test_ctx.sql("select c1 from test LIMIT 10 OFFSET 5")
120122
plan = df.logical_plan()
121123

122124
plan = plan.to_variant()
123125
assert isinstance(plan, Limit)
124126
assert "Skip: Some(Literal(Int64(5), None))" in str(plan)
127+
assert plan.skip().python_value().as_py() == 5
128+
assert plan.fetch().python_value().as_py() == 10
125129

126130

127131
def test_aggregate_query(test_ctx):

0 commit comments

Comments
 (0)