|
17 | 17 |
|
18 | 18 | //! [`PushDownLimit`] pushes `LIMIT` earlier in the query plan
|
19 | 19 |
|
| 20 | +use std::cmp::min; |
20 | 21 | use std::sync::Arc;
|
21 | 22 |
|
22 | 23 | use crate::optimizer::ApplyOrder;
|
@@ -56,47 +57,12 @@ impl OptimizerRule for PushDownLimit {
|
56 | 57 |
|
57 | 58 | if let LogicalPlan::Limit(child) = &*limit.input {
|
58 | 59 | // Merge the Parent Limit and the Child Limit.
|
59 |
| - |
60 |
| - // Case 0: Parent and Child are disjoint. (child_fetch <= skip) |
61 |
| - // Before merging: |
62 |
| - // |........skip........|---fetch-->| Parent Limit |
63 |
| - // |...child_skip...|---child_fetch-->| Child Limit |
64 |
| - // After merging: |
65 |
| - // |.........(child_skip + skip).........| |
66 |
| - // Before merging: |
67 |
| - // |...skip...|------------fetch------------>| Parent Limit |
68 |
| - // |...child_skip...|-------------child_fetch------------>| Child Limit |
69 |
| - // After merging: |
70 |
| - // |....(child_skip + skip)....|---(child_fetch - skip)-->| |
71 |
| - |
72 |
| - // Case 1: Parent is beyond the range of Child. (skip < child_fetch <= skip + fetch) |
73 |
| - // Before merging: |
74 |
| - // |...skip...|------------fetch------------>| Parent Limit |
75 |
| - // |...child_skip...|-------------child_fetch------------>| Child Limit |
76 |
| - // After merging: |
77 |
| - // |....(child_skip + skip)....|---(child_fetch - skip)-->| |
78 |
| - |
79 |
| - // Case 2: Parent is in the range of Child. (skip + fetch < child_fetch) |
80 |
| - // Before merging: |
81 |
| - // |...skip...|---fetch-->| Parent Limit |
82 |
| - // |...child_skip...|-------------child_fetch------------>| Child Limit |
83 |
| - // After merging: |
84 |
| - // |....(child_skip + skip)....|---fetch-->| |
85 |
| - let parent_skip = limit.skip; |
86 |
| - let new_fetch = match (limit.fetch, child.fetch) { |
87 |
| - (Some(fetch), Some(child_fetch)) => { |
88 |
| - Some(min(fetch, child_fetch.saturating_sub(parent_skip))) |
89 |
| - } |
90 |
| - (Some(fetch), None) => Some(fetch), |
91 |
| - (None, Some(child_fetch)) => { |
92 |
| - Some(child_fetch.saturating_sub(parent_skip)) |
93 |
| - } |
94 |
| - (None, None) => None, |
95 |
| - }; |
| 60 | + let (skip, fetch) = |
| 61 | + combine_limit(limit.skip, limit.fetch, child.skip, child.fetch); |
96 | 62 |
|
97 | 63 | let plan = LogicalPlan::Limit(Limit {
|
98 |
| - skip: child.skip + parent_skip, |
99 |
| - fetch: new_fetch, |
| 64 | + skip, |
| 65 | + fetch, |
100 | 66 | input: Arc::new((*child.input).clone()),
|
101 | 67 | });
|
102 | 68 | return self
|
@@ -217,6 +183,78 @@ impl OptimizerRule for PushDownLimit {
|
217 | 183 | }
|
218 | 184 | }
|
219 | 185 |
|
| 186 | +/// Combines two limits into a single |
| 187 | +/// |
| 188 | +/// Returns the combined limit `(skip, fetch)` |
| 189 | +/// |
| 190 | +/// # Case 0: Parent and Child are disjoint. (`child_fetch <= skip`) |
| 191 | +/// |
| 192 | +/// ```text |
| 193 | +/// Before merging: |
| 194 | +/// |........skip........|---fetch-->| Parent Limit |
| 195 | +/// |...child_skip...|---child_fetch-->| Child Limit |
| 196 | +/// ``` |
| 197 | +/// |
| 198 | +/// After merging: |
| 199 | +/// ```text |
| 200 | +/// |.........(child_skip + skip).........| |
| 201 | +/// ``` |
| 202 | +/// |
| 203 | +/// Before merging: |
| 204 | +/// ```text |
| 205 | +/// |...skip...|------------fetch------------>| Parent Limit |
| 206 | +/// |...child_skip...|-------------child_fetch------------>| Child Limit |
| 207 | +/// ``` |
| 208 | +/// |
| 209 | +/// After merging: |
| 210 | +/// ```text |
| 211 | +/// |....(child_skip + skip)....|---(child_fetch - skip)-->| |
| 212 | +/// ``` |
| 213 | +/// |
| 214 | +/// # Case 1: Parent is beyond the range of Child. (`skip < child_fetch <= skip + fetch`) |
| 215 | +/// |
| 216 | +/// Before merging: |
| 217 | +/// ```text |
| 218 | +/// |...skip...|------------fetch------------>| Parent Limit |
| 219 | +/// |...child_skip...|-------------child_fetch------------>| Child Limit |
| 220 | +/// ``` |
| 221 | +/// |
| 222 | +/// After merging: |
| 223 | +/// ```text |
| 224 | +/// |....(child_skip + skip)....|---(child_fetch - skip)-->| |
| 225 | +/// ``` |
| 226 | +/// |
| 227 | +/// # Case 2: Parent is in the range of Child. (`skip + fetch < child_fetch`) |
| 228 | +/// Before merging: |
| 229 | +/// ```text |
| 230 | +/// |...skip...|---fetch-->| Parent Limit |
| 231 | +/// |...child_skip...|-------------child_fetch------------>| Child Limit |
| 232 | +/// ``` |
| 233 | +/// |
| 234 | +/// After merging: |
| 235 | +/// ```text |
| 236 | +/// |....(child_skip + skip)....|---fetch-->| |
| 237 | +/// ``` |
| 238 | +fn combine_limit( |
| 239 | + parent_skip: usize, |
| 240 | + parent_fetch: Option<usize>, |
| 241 | + child_skip: usize, |
| 242 | + child_fetch: Option<usize>, |
| 243 | +) -> (usize, Option<usize>) { |
| 244 | + let combined_skip = child_skip.saturating_add(parent_skip); |
| 245 | + |
| 246 | + let combined_fetch = match (parent_fetch, child_fetch) { |
| 247 | + (Some(parent_fetch), Some(child_fetch)) => { |
| 248 | + Some(min(parent_fetch, child_fetch.saturating_sub(parent_skip))) |
| 249 | + } |
| 250 | + (Some(parent_fetch), None) => Some(parent_fetch), |
| 251 | + (None, Some(child_fetch)) => Some(child_fetch.saturating_sub(parent_skip)), |
| 252 | + (None, None) => None, |
| 253 | + }; |
| 254 | + |
| 255 | + (combined_skip, combined_fetch) |
| 256 | +} |
| 257 | + |
220 | 258 | fn push_down_join(join: &Join, limit: usize) -> Option<Join> {
|
221 | 259 | use JoinType::*;
|
222 | 260 |
|
|
0 commit comments