@@ -25,6 +25,7 @@ import (
2525 "github.com/dolthub/go-mysql-server/sql/hash"
2626 "github.com/dolthub/go-mysql-server/sql/iters"
2727 "github.com/dolthub/go-mysql-server/sql/plan"
28+ "github.com/dolthub/go-mysql-server/sql/transform"
2829 "github.com/dolthub/go-mysql-server/sql/types"
2930)
3031
@@ -126,16 +127,29 @@ func (i *offsetIter) Close(ctx *sql.Context) error {
126127var _ sql.RowIter = & iters.JsonTableRowIter {}
127128
128129type ProjectIter struct {
129- projs []sql.Expression
130- canDefer bool
131- childIter sql.RowIter
130+ projs []sql.Expression
131+ canDefer bool
132+ hasNestedIters bool
133+ nestedState * nestedIterState
134+ childIter sql.RowIter
135+ }
136+
137+ type nestedIterState struct {
138+ projections []sql.Expression
139+ sourceRow sql.Row
140+ iterEvaluators []* RowIterEvaluator
132141}
133142
134143func (i * ProjectIter ) Next (ctx * sql.Context ) (sql.Row , error ) {
144+ if i .hasNestedIters {
145+ return i .ProjectRowWithNestedIters (ctx )
146+ }
147+
135148 childRow , err := i .childIter .Next (ctx )
136149 if err != nil {
137150 return nil , err
138151 }
152+
139153 return ProjectRow (ctx , i .projs , childRow )
140154}
141155
@@ -155,6 +169,136 @@ func (i *ProjectIter) GetChildIter() sql.RowIter {
155169 return i .childIter
156170}
157171
172+ // ProjectRowWithNestedIters evaluates a set of projections, allowing for nested iterators in the expressions.
173+ func (i * ProjectIter ) ProjectRowWithNestedIters (
174+ ctx * sql.Context ,
175+ ) (sql.Row , error ) {
176+
177+ // For the set of iterators, we return one row each element in the longest of the iterators provided.
178+ // Other iterator values will be NULL after they are depleted. All non-iterator fields for the row are returned
179+ // identically for each row in the result set.
180+ if i .nestedState != nil {
181+ row , err := ProjectRow (ctx , i .nestedState .projections , i .nestedState .sourceRow )
182+ if err != nil {
183+ return nil , err
184+ }
185+
186+ nestedIterationFinished := true
187+ for _ , evaluator := range i .nestedState .iterEvaluators {
188+ if ! evaluator .finished && evaluator .iter != nil {
189+ nestedIterationFinished = false
190+ break
191+ }
192+ }
193+
194+ if nestedIterationFinished {
195+ i .nestedState = nil
196+ return i .ProjectRowWithNestedIters (ctx )
197+ }
198+
199+ return row , nil
200+ }
201+
202+ row , err := i .childIter .Next (ctx )
203+ if err != nil {
204+ return nil , err
205+ }
206+
207+ i .nestedState = & nestedIterState {
208+ sourceRow : row ,
209+ }
210+
211+ // We need a new set of projections, with any iterator-returning expressions replaced by new expressions that will
212+ // return the result of the iteration on each call to Eval. We also need to keep a list of all such iterators, so
213+ // that we can tell when they have all finished their iterations.
214+ var rowIterEvaluators []* RowIterEvaluator
215+ newProjs := make ([]sql.Expression , len (i .projs ))
216+ for i , proj := range i .projs {
217+ p , _ , err := transform .Expr (proj , func (e sql.Expression ) (sql.Expression , transform.TreeIdentity , error ) {
218+ if rie , ok := e .(sql.RowIterExpression ); ok && rie .ReturnsRowIter () {
219+ ri , err := rie .EvalRowIter (ctx , row )
220+ if err != nil {
221+ return nil , false , err
222+ }
223+
224+ evaluator := & RowIterEvaluator {
225+ iter : ri ,
226+ typ : rie .Type (),
227+ }
228+ rowIterEvaluators = append (rowIterEvaluators , evaluator )
229+ return evaluator , transform .NewTree , nil
230+ }
231+
232+ return e , transform .SameTree , nil
233+ })
234+
235+ if err != nil {
236+ return nil , err
237+ }
238+
239+ newProjs [i ] = p
240+ }
241+
242+ i .nestedState .projections = newProjs
243+ i .nestedState .iterEvaluators = rowIterEvaluators
244+
245+ return i .ProjectRowWithNestedIters (ctx )
246+ }
247+
248+ // RowIterEvaluator is an expression that returns the next value from a sql.RowIter each time Eval is called.
249+ type RowIterEvaluator struct {
250+ iter sql.RowIter
251+ typ sql.Type
252+ finished bool
253+ }
254+
255+ var _ sql.Expression = (* RowIterEvaluator )(nil )
256+
257+ func (r RowIterEvaluator ) Resolved () bool {
258+ return true
259+ }
260+
261+ func (r RowIterEvaluator ) String () string {
262+ return "RowIterEvaluator"
263+ }
264+
265+ func (r RowIterEvaluator ) Type () sql.Type {
266+ return r .typ
267+ }
268+
269+ func (r RowIterEvaluator ) IsNullable () bool {
270+ return true
271+ }
272+
273+ func (r * RowIterEvaluator ) Eval (ctx * sql.Context , row sql.Row ) (interface {}, error ) {
274+ if r .finished || r .iter == nil {
275+ return nil , nil
276+ }
277+
278+ nextRow , err := r .iter .Next (ctx )
279+ if err != nil {
280+ if errors .Is (err , io .EOF ) {
281+ r .finished = true
282+ return nil , nil
283+ }
284+ return nil , err
285+ }
286+
287+ // All of the set-returning functions return a single value per column
288+ return nextRow [0 ], nil
289+ }
290+
291+ func (r RowIterEvaluator ) Children () []sql.Expression {
292+ return nil
293+ }
294+
295+ func (r RowIterEvaluator ) WithChildren (children ... sql.Expression ) (sql.Expression , error ) {
296+ if len (children ) != 0 {
297+ return nil , sql .ErrInvalidChildrenNumber .New (r , len (children ), 0 )
298+ }
299+ return & r , nil
300+ }
301+
158302// ProjectRow evaluates a set of projections.
159303func ProjectRow (
160304 ctx * sql.Context ,
0 commit comments