5
5
"errors"
6
6
"fmt"
7
7
8
+ "github.com/apache/arrow-go/v18/arrow"
9
+
8
10
"github.com/grafana/loki/v3/pkg/engine/planner/physical"
9
11
)
10
12
@@ -68,7 +70,7 @@ func (c *Context) executeSortMerge(_ context.Context, _ *physical.SortMerge, inp
68
70
return errorPipeline (errNotImplemented )
69
71
}
70
72
71
- func (c * Context ) executeLimit (_ context.Context , _ * physical.Limit , inputs []Pipeline ) Pipeline {
73
+ func (c * Context ) executeLimit (_ context.Context , limit * physical.Limit , inputs []Pipeline ) Pipeline {
72
74
if len (inputs ) == 0 {
73
75
return emptyPipeline ()
74
76
}
@@ -77,7 +79,51 @@ func (c *Context) executeLimit(_ context.Context, _ *physical.Limit, inputs []Pi
77
79
return errorPipeline (fmt .Errorf ("limit expects exactly one input, got %d" , len (inputs )))
78
80
}
79
81
80
- return errorPipeline (errNotImplemented )
82
+ // We gradually reduce offsetRemaining and limitRemaining as we process more records, as the
83
+ // offsetRemaining and limitRemaining may cross record boundaries.
84
+ var (
85
+ offsetRemaining = int64 (limit .Skip )
86
+ limitRemaining = int64 (limit .Fetch )
87
+ )
88
+
89
+ return newGenericPipeline (Local , func (inputs []Pipeline ) state {
90
+ var length int64
91
+ var start , end int64
92
+ var batch arrow.Record
93
+
94
+ // We skip yielding zero-length batches while offsetRemainig > 0
95
+ for length == 0 {
96
+ // Stop once we reached the limit
97
+ if limitRemaining <= 0 {
98
+ return Exhausted
99
+ }
100
+
101
+ // Pull the next item from downstream
102
+ input := inputs [0 ]
103
+ err := input .Read ()
104
+ if err != nil {
105
+ return newState (input .Value ())
106
+ }
107
+ batch , _ = input .Value ()
108
+
109
+ // We want to slice batch so it only contains the rows we're looking for
110
+ // accounting for both the limit and offset.
111
+ // We constrain the start and end to be within the bounds of the record.
112
+ start = min (offsetRemaining , batch .NumRows ())
113
+ end = min (start + limitRemaining , batch .NumRows ())
114
+ length = end - start
115
+
116
+ offsetRemaining -= start
117
+ limitRemaining -= length
118
+ }
119
+
120
+ if length <= 0 && offsetRemaining <= 0 {
121
+ return Exhausted
122
+ }
123
+
124
+ rec := batch .NewSlice (start , end )
125
+ return successState (rec )
126
+ }, inputs ... )
81
127
}
82
128
83
129
func (c * Context ) executeFilter (_ context.Context , _ * physical.Filter , inputs []Pipeline ) Pipeline {
0 commit comments