#
# Generate code for interesting rule.
#
optgen explorer test.opt
[Relational]
define Scan {
    _ ScanPrivate
}

[Private]
define ScanPrivate {
	Table      TableID
	Index      int
	Cols       ColSet
	Constraint Constraint
	HardLimit  ScanLimit
	Flags      ScanFlags
}

[Relational]
define Limit {
    Input RelExpr
    Limit ScalarExpr

    Ordering OrderingChoice
}

[Relational]
define IndexJoin {
    Input RelExpr

    _ IndexJoinPrivate
}

[Private]
define IndexJoinPrivate {
	Table TableID
	Cols  ColSet
}

[Scalar, ConstValue]
define Const {
    Value Datum
}

[PushLimitIntoIndexJoin, Explore]
(Limit
    (IndexJoin
      (Scan $scanDef:*)
      $indexJoinDef:*
    )
    (Const $limit:* & (IsPositiveLimit $limit))
    $ordering:* & (CanLimitConstrainedScan $scanDef $ordering)
)
=>
(IndexJoin
  (Scan (LimitScanDef $scanDef $limit $ordering))
  $indexJoinDef
)
----
----
// Code generated by optgen; [omitted]

package xform

import (
	"github.com/cockroachdb/cockroach/pkg/sql/opt"
	"github.com/cockroachdb/cockroach/pkg/sql/opt/memo"
	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
)

func (_e *explorer) exploreGroupMember(
	state *exploreState,
	member memo.RelExpr,
	ordinal int,
) (_fullyExplored bool) {
	switch t := member.(type) {
	case *memo.LimitExpr:
		return _e.exploreLimit(state, t, ordinal)
	}

	// No rules for other operator types.
	return true
}

func (_e *explorer) exploreLimit(
	_rootState *exploreState,
	_root *memo.LimitExpr,
	_rootOrd int,
) (_fullyExplored bool) {
	_fullyExplored = true

	// [PushLimitIntoIndexJoin]
	{
		_partlyExplored := _rootOrd < _rootState.start
		_state := _e.lookupExploreState(_root.Input)
		if !_state.fullyExplored {
			_fullyExplored = false
		}
		var _member memo.RelExpr
		for _ord := 0; _ord < _state.end; _ord++ {
			if _member == nil {
				_member = _root.Input.FirstExpr()
			} else {
				_member = _member.NextExpr()
			}
			_partlyExplored := _partlyExplored && _ord < _state.start
			_indexJoin, _ := _member.(*memo.IndexJoinExpr)
			if _indexJoin != nil {
				_state := _e.lookupExploreState(_indexJoin.Input)
				if !_state.fullyExplored {
					_fullyExplored = false
				}
				var _member memo.RelExpr
				for _ord := 0; _ord < _state.end; _ord++ {
					if _member == nil {
						_member = _indexJoin.Input.FirstExpr()
					} else {
						_member = _member.NextExpr()
					}
					if !_partlyExplored || _ord >= _state.start {
						_scan, _ := _member.(*memo.ScanExpr)
						if _scan != nil {
							scanDef := &_scan.ScanPrivate
							indexJoinDef := &_indexJoin.IndexJoinPrivate
							_const, _ := _root.Limit.(*memo.ConstExpr)
							if _const != nil {
								limit := _const.Value
								if _e.funcs.IsPositiveLimit(limit) {
									ordering := _root.Ordering
									if _e.funcs.CanLimitConstrainedScan(scanDef, ordering) {
										if _e.o.matchedRule == nil || _e.o.matchedRule(opt.PushLimitIntoIndexJoin) {
											_expr := &memo.IndexJoinExpr{
												Input: _e.f.ConstructScan(
													_e.funcs.LimitScanDef(scanDef, limit, ordering),
												),
												IndexJoinPrivate: *indexJoinDef,
											}
											_interned := _e.mem.AddIndexJoinToGroup(_expr, _root)
											if _e.o.appliedRule != nil {
												if _interned != _expr {
													_e.o.appliedRule(opt.PushLimitIntoIndexJoin, _root, nil)
												} else {
													_e.o.appliedRule(opt.PushLimitIntoIndexJoin, _root, _interned)
												}
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}
	}

	return _fullyExplored
}
----
----
