github.com/dolthub/go-mysql-server@v0.18.0/sql/rowexec/join_iters.go (about) 1 // Copyright 2020-2021 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package rowexec 16 17 import ( 18 "errors" 19 "fmt" 20 "io" 21 "reflect" 22 23 "go.opentelemetry.io/otel/attribute" 24 "go.opentelemetry.io/otel/trace" 25 26 "github.com/dolthub/go-mysql-server/sql" 27 "github.com/dolthub/go-mysql-server/sql/expression" 28 "github.com/dolthub/go-mysql-server/sql/plan" 29 "github.com/dolthub/go-mysql-server/sql/transform" 30 ) 31 32 func newJoinIter(ctx *sql.Context, b sql.NodeExecBuilder, j *plan.JoinNode, row sql.Row) (sql.RowIter, error) { 33 var leftName, rightName string 34 if leftTable, ok := j.Left().(sql.Nameable); ok { 35 leftName = leftTable.Name() 36 } else { 37 leftName = reflect.TypeOf(j.Left()).String() 38 } 39 40 if rightTable, ok := j.Right().(sql.Nameable); ok { 41 rightName = rightTable.Name() 42 } else { 43 rightName = reflect.TypeOf(j.Right()).String() 44 } 45 46 span, ctx := ctx.Span("plan.joinIter", trace.WithAttributes( 47 attribute.String("left", leftName), 48 attribute.String("right", rightName), 49 )) 50 51 l, err := b.Build(ctx, j.Left(), row) 52 if err != nil { 53 span.End() 54 return nil, err 55 } 56 return sql.NewSpanIter(span, &joinIter{ 57 parentRow: row, 58 primary: l, 59 secondaryProvider: j.Right(), 60 cond: j.Filter, 61 joinType: j.Op, 62 rowSize: len(row) + len(j.Left().Schema()) + len(j.Right().Schema()), 63 scopeLen: j.ScopeLen, 64 b: b, 65 }), nil 66 } 67 68 // joinIter is an iterator that iterates over every row in the primary table and performs an index lookup in 69 // the secondary table for each value 70 type joinIter struct { 71 parentRow sql.Row 72 primary sql.RowIter 73 primaryRow sql.Row 74 secondaryProvider sql.Node 75 secondary sql.RowIter 76 cond sql.Expression 77 joinType plan.JoinType 78 79 foundMatch bool 80 rowSize int 81 scopeLen int 82 b sql.NodeExecBuilder 83 } 84 85 func (i *joinIter) loadPrimary(ctx *sql.Context) error { 86 if i.primaryRow == nil { 87 r, err := i.primary.Next(ctx) 88 if err != nil { 89 return err 90 } 91 92 i.primaryRow = i.parentRow.Append(r) 93 i.foundMatch = false 94 } 95 96 return nil 97 } 98 99 func (i *joinIter) loadSecondary(ctx *sql.Context) (sql.Row, error) { 100 if i.secondary == nil { 101 rowIter, err := i.b.Build(ctx, i.secondaryProvider, i.primaryRow) 102 103 if err != nil { 104 return nil, err 105 } 106 if plan.IsEmptyIter(rowIter) { 107 return nil, plan.ErrEmptyCachedResult 108 } 109 i.secondary = rowIter 110 } 111 112 secondaryRow, err := i.secondary.Next(ctx) 113 if err != nil { 114 if err == io.EOF { 115 err = i.secondary.Close(ctx) 116 i.secondary = nil 117 if err != nil { 118 return nil, err 119 } 120 i.primaryRow = nil 121 return nil, io.EOF 122 } 123 return nil, err 124 } 125 126 return secondaryRow, nil 127 } 128 129 func (i *joinIter) Next(ctx *sql.Context) (sql.Row, error) { 130 for { 131 if err := i.loadPrimary(ctx); err != nil { 132 return nil, err 133 } 134 135 primary := i.primaryRow 136 secondary, err := i.loadSecondary(ctx) 137 if err != nil { 138 if errors.Is(err, io.EOF) { 139 if !i.foundMatch && i.joinType.IsLeftOuter() { 140 i.primaryRow = nil 141 row := i.buildRow(primary, nil) 142 return i.removeParentRow(row), nil 143 } 144 continue 145 } else if errors.Is(err, plan.ErrEmptyCachedResult) { 146 if !i.foundMatch && i.joinType.IsLeftOuter() { 147 i.primaryRow = nil 148 row := i.buildRow(primary, nil) 149 return i.removeParentRow(row), nil 150 } 151 152 return nil, io.EOF 153 } 154 return nil, err 155 } 156 157 row := i.buildRow(primary, secondary) 158 res, err := sql.EvaluateCondition(ctx, i.cond, row) 159 if err != nil { 160 return nil, err 161 } 162 163 if res == nil && i.joinType.IsExcludeNulls() { 164 err = i.secondary.Close(ctx) 165 i.secondary = nil 166 if err != nil { 167 return nil, err 168 } 169 i.primaryRow = nil 170 continue 171 } 172 173 if !sql.IsTrue(res) { 174 continue 175 } 176 177 i.foundMatch = true 178 return i.removeParentRow(row), nil 179 } 180 } 181 182 func (i *joinIter) removeParentRow(r sql.Row) sql.Row { 183 copy(r[i.scopeLen:], r[len(i.parentRow):]) 184 r = r[:len(r)-len(i.parentRow)+i.scopeLen] 185 return r 186 } 187 188 // buildRow builds the result set row using the rows from the primary and secondary tables 189 func (i *joinIter) buildRow(primary, secondary sql.Row) sql.Row { 190 row := make(sql.Row, i.rowSize) 191 192 copy(row, primary) 193 copy(row[len(primary):], secondary) 194 195 return row 196 } 197 198 func (i *joinIter) Close(ctx *sql.Context) (err error) { 199 if i.primary != nil { 200 if err = i.primary.Close(ctx); err != nil { 201 if i.secondary != nil { 202 _ = i.secondary.Close(ctx) 203 } 204 return err 205 } 206 } 207 208 if i.secondary != nil { 209 err = i.secondary.Close(ctx) 210 i.secondary = nil 211 } 212 213 return err 214 } 215 216 func newExistsIter(ctx *sql.Context, b sql.NodeExecBuilder, j *plan.JoinNode, row sql.Row) (sql.RowIter, error) { 217 leftIter, err := b.Build(ctx, j.Left(), row) 218 219 if err != nil { 220 return nil, err 221 } 222 return &existsIter{ 223 parentRow: row, 224 typ: j.Op, 225 primary: leftIter, 226 secondaryProvider: j.Right(), 227 cond: j.Filter, 228 scopeLen: j.ScopeLen, 229 rowSize: len(row) + len(j.Left().Schema()) + len(j.Right().Schema()), 230 nullRej: !(j.Filter != nil && plan.IsNullRejecting(j.Filter)), 231 b: b, 232 }, nil 233 } 234 235 type existsIter struct { 236 typ plan.JoinType 237 primary sql.RowIter 238 secondaryProvider sql.Node 239 cond sql.Expression 240 241 primaryRow sql.Row 242 243 parentRow sql.Row 244 scopeLen int 245 rowSize int 246 nullRej bool 247 rightIterNonEmpty bool 248 b sql.NodeExecBuilder 249 } 250 251 type existsState uint8 252 253 const ( 254 esIncLeft existsState = iota 255 esIncRight 256 esRightIterEOF 257 esCompare 258 esRejectNull 259 esRet 260 ) 261 262 func (i *existsIter) Next(ctx *sql.Context) (sql.Row, error) { 263 var row sql.Row 264 var right sql.Row 265 var left sql.Row 266 var rIter sql.RowIter 267 var err error 268 269 // the common sequence is: LOAD_LEFT -> LOAD_RIGHT -> COMPARE -> RET 270 // notable exceptions are represented as goto jumps: 271 // - non-null rejecting filters jump to COMPARE with a nil right row 272 // when the secondaryProvider is empty 273 // - antiJoin succeeds to RET when LOAD_RIGHT EOF's 274 // - semiJoin fails when LOAD_RIGHT EOF's, falling back to LOAD_LEFT 275 // - antiJoin fails when COMPARE returns true, falling back to LOAD_LEFT 276 nextState := esIncLeft 277 for { 278 switch nextState { 279 case esIncLeft: 280 r, err := i.primary.Next(ctx) 281 if err != nil { 282 return nil, err 283 } 284 left = i.parentRow.Append(r) 285 rIter, err = i.b.Build(ctx, i.secondaryProvider, left) 286 287 if err != nil { 288 return nil, err 289 } 290 if plan.IsEmptyIter(rIter) { 291 if i.nullRej || i.typ.IsAnti() { 292 return nil, io.EOF 293 } 294 nextState = esCompare 295 } else { 296 nextState = esIncRight 297 } 298 case esIncRight: 299 right, err = rIter.Next(ctx) 300 if err != nil { 301 iterErr := rIter.Close(ctx) 302 if iterErr != nil { 303 return nil, fmt.Errorf("%w; error on close: %s", err, iterErr) 304 } 305 if errors.Is(err, io.EOF) { 306 nextState = esRightIterEOF 307 } else { 308 return nil, err 309 } 310 } else { 311 i.rightIterNonEmpty = true 312 nextState = esCompare 313 } 314 case esRightIterEOF: 315 if i.typ.IsSemi() { 316 // reset iter, no match 317 nextState = esIncLeft 318 } else if !i.rightIterNonEmpty && !isTrueLit(i.cond) { 319 // ANTI_JOIN with empty RHS is subject to special cases, 320 // the first two are a valid match. 321 // 1) If we matched no rows but rows were returned 322 // 2) No match and no rows returned, EXISTS check 323 // 3) No match and no rows returned, IN check 324 return nil, io.EOF 325 } else { 326 nextState = esRet 327 } 328 case esCompare: 329 row = i.buildRow(left, right) 330 res, err := sql.EvaluateCondition(ctx, i.cond, row) 331 if err != nil { 332 return nil, err 333 } 334 335 if res == nil && i.typ.IsExcludeNulls() { 336 nextState = esRejectNull 337 continue 338 } 339 340 if !sql.IsTrue(res) { 341 nextState = esIncRight 342 } else { 343 err = rIter.Close(ctx) 344 if err != nil { 345 return nil, err 346 } 347 if i.typ.IsAnti() { 348 // reset iter, found match -> no return row 349 nextState = esIncLeft 350 } else { 351 nextState = esRet 352 } 353 } 354 case esRejectNull: 355 if i.typ.IsAnti() { 356 nextState = esIncLeft 357 } else { 358 nextState = esIncRight 359 } 360 case esRet: 361 return i.removeParentRow(left), nil 362 default: 363 return nil, fmt.Errorf("invalid exists join state") 364 } 365 } 366 } 367 368 func isTrueLit(e sql.Expression) bool { 369 if lit, ok := e.(*expression.Literal); ok { 370 return lit.Value() == true 371 } 372 return false 373 } 374 375 func (i *existsIter) removeParentRow(r sql.Row) sql.Row { 376 copy(r[i.scopeLen:], r[len(i.parentRow):]) 377 r = r[:len(r)-len(i.parentRow)+i.scopeLen] 378 return r 379 } 380 381 // buildRow builds the result set row using the rows from the primary and secondary tables 382 func (i *existsIter) buildRow(primary, secondary sql.Row) sql.Row { 383 row := make(sql.Row, i.rowSize) 384 385 copy(row, primary) 386 copy(row[len(primary):], secondary) 387 388 return row 389 } 390 391 func (i *existsIter) Close(ctx *sql.Context) (err error) { 392 if i.primary != nil { 393 if err = i.primary.Close(ctx); err != nil { 394 return err 395 } 396 } 397 return err 398 } 399 400 func newFullJoinIter(ctx *sql.Context, b sql.NodeExecBuilder, j *plan.JoinNode, row sql.Row) (sql.RowIter, error) { 401 leftIter, err := b.Build(ctx, j.Left(), row) 402 403 if err != nil { 404 return nil, err 405 } 406 return &fullJoinIter{ 407 parentRow: row, 408 l: leftIter, 409 rp: j.Right(), 410 cond: j.Filter, 411 scopeLen: j.ScopeLen, 412 rowSize: len(row) + len(j.Left().Schema()) + len(j.Right().Schema()), 413 seenLeft: make(map[uint64]struct{}), 414 seenRight: make(map[uint64]struct{}), 415 b: b, 416 }, nil 417 } 418 419 // fullJoinIter implements full join as a union of left and right join: 420 // FJ(A,B) => U(LJ(A,B), RJ(A,B)). The current algorithm will have a 421 // runtime and memory complexity O(m+n). 422 type fullJoinIter struct { 423 l sql.RowIter 424 rp sql.Node 425 b sql.NodeExecBuilder 426 r sql.RowIter 427 cond sql.Expression 428 429 parentRow sql.Row 430 leftRow sql.Row 431 scopeLen int 432 rowSize int 433 434 leftDone bool 435 seenLeft map[uint64]struct{} 436 seenRight map[uint64]struct{} 437 } 438 439 func (i *fullJoinIter) Next(ctx *sql.Context) (sql.Row, error) { 440 for { 441 if i.leftDone { 442 break 443 } 444 if i.leftRow == nil { 445 r, err := i.l.Next(ctx) 446 if errors.Is(err, io.EOF) { 447 i.leftDone = true 448 i.l = nil 449 i.r = nil 450 } 451 if err != nil { 452 return nil, err 453 } 454 455 i.leftRow = r 456 } 457 458 if i.r == nil { 459 iter, err := i.b.Build(ctx, i.rp, i.leftRow) 460 if err != nil { 461 return nil, err 462 } 463 i.r = iter 464 } 465 466 rightRow, err := i.r.Next(ctx) 467 if err == io.EOF { 468 key, err := sql.HashOf(i.leftRow) 469 if err != nil { 470 return nil, err 471 } 472 if _, ok := i.seenLeft[key]; !ok { 473 // (left, null) only if we haven't matched left 474 ret := i.buildRow(i.leftRow, nil) 475 i.r = nil 476 i.leftRow = nil 477 return i.removeParentRow(ret), nil 478 } 479 i.r = nil 480 i.leftRow = nil 481 } 482 483 row := i.buildRow(i.leftRow, rightRow) 484 matches, err := sql.EvaluateCondition(ctx, i.cond, row) 485 if err != nil { 486 return nil, err 487 } 488 if !sql.IsTrue(matches) { 489 continue 490 } 491 rkey, err := sql.HashOf(rightRow) 492 if err != nil { 493 return nil, err 494 } 495 i.seenRight[rkey] = struct{}{} 496 lKey, err := sql.HashOf(i.leftRow) 497 if err != nil { 498 return nil, err 499 } 500 i.seenLeft[lKey] = struct{}{} 501 return i.removeParentRow(row), nil 502 } 503 504 for { 505 if i.r == nil { 506 iter, err := i.b.Build(ctx, i.rp, i.leftRow) 507 if err != nil { 508 return nil, err 509 } 510 511 i.r = iter 512 } 513 514 rightRow, err := i.r.Next(ctx) 515 if errors.Is(err, io.EOF) { 516 err := i.r.Close(ctx) 517 if err != nil { 518 return nil, err 519 } 520 return nil, io.EOF 521 } 522 523 key, err := sql.HashOf(rightRow) 524 if err != nil { 525 return nil, err 526 } 527 if _, ok := i.seenRight[key]; ok { 528 continue 529 } 530 // (null, right) only if we haven't matched right 531 ret := i.buildRow(nil, rightRow) 532 return i.removeParentRow(ret), nil 533 } 534 } 535 536 func (i *fullJoinIter) removeParentRow(r sql.Row) sql.Row { 537 copy(r[i.scopeLen:], r[len(i.parentRow):]) 538 r = r[:len(r)-len(i.parentRow)+i.scopeLen] 539 return r 540 } 541 542 // buildRow builds the result set row using the rows from the primary and secondary tables 543 func (i *fullJoinIter) buildRow(primary, secondary sql.Row) sql.Row { 544 row := make(sql.Row, i.rowSize) 545 546 copy(row, primary) 547 copy(row[len(primary):], secondary) 548 549 return row 550 } 551 552 func (i *fullJoinIter) Close(ctx *sql.Context) (err error) { 553 if i.l != nil { 554 err = i.l.Close(ctx) 555 } 556 557 if i.r != nil { 558 if err == nil { 559 err = i.r.Close(ctx) 560 } else { 561 i.r.Close(ctx) 562 } 563 } 564 565 return err 566 } 567 568 func newCrossJoinIter(ctx *sql.Context, b sql.NodeExecBuilder, j *plan.JoinNode, row sql.Row) (sql.RowIter, error) { 569 var left, right string 570 if leftTable, ok := j.Left().(sql.Nameable); ok { 571 left = leftTable.Name() 572 } else { 573 left = reflect.TypeOf(j.Left()).String() 574 } 575 576 if rightTable, ok := j.Right().(sql.Nameable); ok { 577 right = rightTable.Name() 578 } else { 579 right = reflect.TypeOf(j.Right()).String() 580 } 581 582 span, ctx := ctx.Span("plan.CrossJoin", trace.WithAttributes( 583 attribute.String("left", left), 584 attribute.String("right", right), 585 )) 586 587 l, err := b.Build(ctx, j.Left(), row) 588 if err != nil { 589 span.End() 590 return nil, err 591 } 592 593 return sql.NewSpanIter(span, &crossJoinIterator{ 594 b: b, 595 parentRow: row, 596 l: l, 597 rp: j.Right(), 598 rowSize: len(row) + len(j.Left().Schema()) + len(j.Right().Schema()), 599 scopeLen: j.ScopeLen, 600 }), nil 601 } 602 603 type crossJoinIterator struct { 604 l sql.RowIter 605 r sql.RowIter 606 rp sql.Node 607 b sql.NodeExecBuilder 608 609 parentRow sql.Row 610 611 rowSize int 612 scopeLen int 613 614 leftRow sql.Row 615 } 616 617 func (i *crossJoinIterator) Next(ctx *sql.Context) (sql.Row, error) { 618 for { 619 if i.leftRow == nil { 620 r, err := i.l.Next(ctx) 621 if err != nil { 622 return nil, err 623 } 624 625 i.leftRow = i.parentRow.Append(r) 626 } 627 628 if i.r == nil { 629 iter, err := i.b.Build(ctx, i.rp, i.leftRow) 630 if err != nil { 631 return nil, err 632 } 633 634 i.r = iter 635 } 636 637 rightRow, err := i.r.Next(ctx) 638 if err == io.EOF { 639 i.r = nil 640 i.leftRow = nil 641 continue 642 } 643 644 if err != nil { 645 return nil, err 646 } 647 648 var row sql.Row 649 row = append(row, i.leftRow...) 650 row = append(row, rightRow...) 651 652 return i.removeParentRow(row), nil 653 } 654 } 655 656 func (i *crossJoinIterator) removeParentRow(r sql.Row) sql.Row { 657 copy(r[i.scopeLen:], r[len(i.parentRow):]) 658 r = r[:len(r)-len(i.parentRow)+i.scopeLen] 659 return r 660 } 661 662 func (i *crossJoinIterator) Close(ctx *sql.Context) (err error) { 663 if i.l != nil { 664 err = i.l.Close(ctx) 665 } 666 667 if i.r != nil { 668 if err == nil { 669 err = i.r.Close(ctx) 670 } else { 671 i.r.Close(ctx) 672 } 673 } 674 675 return err 676 } 677 678 // lateralJoinIter is an iterator that performs a lateral join. 679 // A LateralJoin is a join where the right side is a subquery that can reference the left side, like through a filter. 680 // MySQL Docs: https://dev.mysql.com/doc/refman/8.0/en/lateral-derived-tables.html 681 // Example: 682 // select * from t; 683 // +---+ 684 // | i | 685 // +---+ 686 // | 1 | 687 // | 2 | 688 // | 3 | 689 // +---+ 690 // select * from t1; 691 // +---+ 692 // | i | 693 // +---+ 694 // | 1 | 695 // | 4 | 696 // | 5 | 697 // +---+ 698 // select * from t, lateral (select * from t1 where t.i = t1.j) tt; 699 // +---+---+ 700 // | i | j | 701 // +---+---+ 702 // | 1 | 1 | 703 // +---+---+ 704 // cond is passed to the filter iter to be evaluated. 705 type lateralJoinIterator struct { 706 pRow sql.Row 707 lRow sql.Row 708 rRow sql.Row 709 lIter sql.RowIter 710 rIter sql.RowIter 711 rNode sql.Node 712 cond sql.Expression 713 jType plan.JoinType 714 715 rowSize int 716 scopeLen int 717 718 foundMatch bool 719 720 b sql.NodeExecBuilder 721 } 722 723 func newLateralJoinIter(ctx *sql.Context, b sql.NodeExecBuilder, j *plan.JoinNode, row sql.Row) (sql.RowIter, error) { 724 var left, right string 725 if leftTable, ok := j.Left().(sql.Nameable); ok { 726 left = leftTable.Name() 727 } else { 728 left = reflect.TypeOf(j.Left()).String() 729 } 730 if rightTable, ok := j.Right().(sql.Nameable); ok { 731 right = rightTable.Name() 732 } else { 733 right = reflect.TypeOf(j.Right()).String() 734 } 735 736 span, ctx := ctx.Span("plan.LateralJoin", trace.WithAttributes( 737 attribute.String("left", left), 738 attribute.String("right", right), 739 )) 740 741 l, err := b.Build(ctx, j.Left(), row) 742 if err != nil { 743 span.End() 744 return nil, err 745 } 746 747 return sql.NewSpanIter(span, &lateralJoinIterator{ 748 pRow: row, 749 lIter: l, 750 rNode: j.Right(), 751 cond: j.Filter, 752 jType: j.Op, 753 rowSize: len(row) + len(j.Left().Schema()) + len(j.Right().Schema()), 754 scopeLen: j.ScopeLen, 755 b: b, 756 }), nil 757 } 758 759 func (i *lateralJoinIterator) loadLeft(ctx *sql.Context) error { 760 if i.lRow == nil { 761 lRow, err := i.lIter.Next(ctx) 762 if err != nil { 763 return err 764 } 765 i.lRow = lRow 766 i.foundMatch = false 767 } 768 return nil 769 } 770 771 func (i *lateralJoinIterator) buildRight(ctx *sql.Context) error { 772 if i.rIter == nil { 773 prepended, _, err := transform.Node(i.rNode, plan.PrependRowInPlan(i.lRow, true)) 774 if err != nil { 775 return err 776 } 777 iter, err := i.b.Build(ctx, prepended, i.lRow) 778 if err != nil { 779 return err 780 } 781 i.rIter = iter 782 } 783 return nil 784 } 785 786 func (i *lateralJoinIterator) loadRight(ctx *sql.Context) error { 787 if i.rRow == nil { 788 rRow, err := i.rIter.Next(ctx) 789 if err != nil { 790 return err 791 } 792 i.rRow = rRow[len(i.lRow):] 793 } 794 return nil 795 } 796 797 func (i *lateralJoinIterator) buildRow(lRow, rRow sql.Row) sql.Row { 798 row := make(sql.Row, i.rowSize) 799 copy(row, lRow) 800 copy(row[len(lRow):], rRow) 801 return row 802 } 803 804 func (i *lateralJoinIterator) removeParentRow(r sql.Row) sql.Row { 805 copy(r[i.scopeLen:], r[len(i.pRow):]) 806 r = r[:len(r)-len(i.pRow)+i.scopeLen] 807 return r 808 } 809 810 func (i *lateralJoinIterator) reset(ctx *sql.Context) (err error) { 811 if i.rIter != nil { 812 err = i.rIter.Close(ctx) 813 i.rIter = nil 814 } 815 i.lRow = nil 816 i.rRow = nil 817 return 818 } 819 820 func (i *lateralJoinIterator) Next(ctx *sql.Context) (sql.Row, error) { 821 for { 822 if err := i.loadLeft(ctx); err != nil { 823 return nil, err 824 } 825 if err := i.buildRight(ctx); err != nil { 826 return nil, err 827 } 828 if err := i.loadRight(ctx); err != nil { 829 if errors.Is(err, io.EOF) { 830 if !i.foundMatch && i.jType == plan.JoinTypeLateralLeft { 831 res := i.buildRow(i.lRow, nil) 832 if rerr := i.reset(ctx); rerr != nil { 833 return nil, rerr 834 } 835 return i.removeParentRow(res), nil 836 } 837 if rerr := i.reset(ctx); rerr != nil { 838 return nil, rerr 839 } 840 continue 841 } 842 return nil, err 843 } 844 845 row := i.buildRow(i.lRow, i.rRow) 846 i.rRow = nil 847 if i.cond != nil { 848 if res, err := sql.EvaluateCondition(ctx, i.cond, row); err != nil { 849 return nil, err 850 } else if !sql.IsTrue(res) { 851 continue 852 } 853 } 854 855 i.foundMatch = true 856 return i.removeParentRow(row), nil 857 } 858 } 859 860 func (i *lateralJoinIterator) Close(ctx *sql.Context) error { 861 var lerr, rerr error 862 if i.lIter != nil { 863 lerr = i.lIter.Close(ctx) 864 } 865 if i.rIter != nil { 866 rerr = i.rIter.Close(ctx) 867 } 868 if lerr != nil { 869 return lerr 870 } 871 if rerr != nil { 872 return rerr 873 } 874 return nil 875 }