#
# 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
}

[Relational]
define InnerJoin {
    Left RelExpr
    Right RelExpr
}

[PushLimitIntoIndexJoin, Explore]
(Limit
    (IndexJoin
      (Scan $scanDef:*)
      $indexJoinDef:*
    )
    (Const $limit:* & (IsPositiveInt $limit))
    $ordering:* & (CanLimitConstrainedScan $scanDef $ordering)
)
=>
(IndexJoin
  (Scan (LimitScanDef $scanDef $limit $ordering))
  $indexJoinDef
)

[TestRootAccess, Explore]
(InnerJoin (InnerJoin) & (ShouldReorderJoins (Root)))
=>
(ReorderJoins (Root))
----
----
// 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/opt/props/physical"
	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
)

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

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

func (_e *explorer) exploreLimit(
	_rootState *exploreState,
	_root *memo.LimitExpr,
	_rootOrd int,
	_required *physical.Required,
) (_fullyExplored bool) {
	opt.MaybeInjectOptimizerTestingPanic(_e.ctx, _e.evalCtx)
	_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.IsPositiveInt(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
}

func (_e *explorer) exploreInnerJoin(
	_rootState *exploreState,
	_root *memo.InnerJoinExpr,
	_rootOrd int,
	_required *physical.Required,
) (_fullyExplored bool) {
	opt.MaybeInjectOptimizerTestingPanic(_e.ctx, _e.evalCtx)
	_fullyExplored = true

	// [TestRootAccess]
	{
		_partlyExplored := _rootOrd < _rootState.start
		_state := _e.lookupExploreState(_root.Left)
		if !_state.fullyExplored {
			_fullyExplored = false
		}
		var _member memo.RelExpr
		for _ord := 0; _ord < _state.end; _ord++ {
			if _member == nil {
				_member = _root.Left.FirstExpr()
			} else {
				_member = _member.NextExpr()
			}
			if !_partlyExplored || _ord >= _state.start {
				_innerJoin, _ := _member.(*memo.InnerJoinExpr)
				if _innerJoin != nil {
					if _e.funcs.ShouldReorderJoins(_root) {
						if _e.o.matchedRule == nil || _e.o.matchedRule(opt.TestRootAccess) {
							var _last memo.RelExpr
							if _e.o.appliedRule != nil {
								_last = memo.LastGroupMember(_root)
							}
							_e.funcs.ReorderJoins(_root, _required, _root)
							if _e.o.appliedRule != nil {
								_e.o.appliedRule(opt.TestRootAccess, _root, _last.NextExpr())
							}
						}
					}
				}
			}
		}
	}

	return _fullyExplored
}
----
----

#
# Use strictly-typed variable aliases when a variable is bound to a single type
# of expression.
#
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
}

[MatchInputScan, Explore]
(Limit
    $scan:(Scan $scanPrivate:*)
    $limit:*
    $ordering:* &
        (BoolFuncOnScan $scan)
)
=>
(Limit (GenerateFuncOnScan $scan) $limit $ordering)
----
----
// 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/opt/props/physical"
	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
)

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

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

func (_e *explorer) exploreLimit(
	_rootState *exploreState,
	_root *memo.LimitExpr,
	_rootOrd int,
	_required *physical.Required,
) (_fullyExplored bool) {
	opt.MaybeInjectOptimizerTestingPanic(_e.ctx, _e.evalCtx)
	_fullyExplored = true

	// [MatchInputScan]
	{
		_partlyExplored := _rootOrd < _rootState.start
		scan := _root.Input
		_state := _e.lookupExploreState(scan)
		if !_state.fullyExplored {
			_fullyExplored = false
		}
		var _member memo.RelExpr
		for _ord := 0; _ord < _state.end; _ord++ {
			if _member == nil {
				_member = scan.FirstExpr()
			} else {
				_member = _member.NextExpr()
			}
			scan = _member
			if !_partlyExplored || _ord >= _state.start {
				_scan, _ := _member.(*memo.ScanExpr)
				if _scan != nil {
					scanPrivate := &_scan.ScanPrivate
					limit := _root.Limit
					ordering := _root.Ordering
					if _e.funcs.BoolFuncOnScan(_scan) {
						if _e.o.matchedRule == nil || _e.o.matchedRule(opt.MatchInputScan) {
							_expr := &memo.LimitExpr{
								Input:    _e.funcs.GenerateFuncOnScan(_scan),
								Limit:    limit,
								Ordering: ordering,
							}
							_interned := _e.mem.AddLimitToGroup(_expr, _root)
							if _e.o.appliedRule != nil {
								if _interned != _expr {
									_e.o.appliedRule(opt.MatchInputScan, _root, nil)
								} else {
									_e.o.appliedRule(opt.MatchInputScan, _root, _interned)
								}
							}
						}
					}
				}
			}
		}
	}

	return _fullyExplored
}
----
----

#
# Do not use strictly-typed variable aliases when a variable is bound to a
# not (^) optgen expression.
#
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
}

[MatchInputNonScan, Explore]
(Limit
    $input:^(Scan)
    $limit:*
    $ordering:* &
        (BoolFuncOnNonScan $input)
)
=>
(Limit (GenerateFuncOnNonScan $input) $limit $ordering)
----
----
// 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/opt/props/physical"
	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
)

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

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

func (_e *explorer) exploreLimit(
	_rootState *exploreState,
	_root *memo.LimitExpr,
	_rootOrd int,
	_required *physical.Required,
) (_fullyExplored bool) {
	opt.MaybeInjectOptimizerTestingPanic(_e.ctx, _e.evalCtx)
	_fullyExplored = true

	// [MatchInputNonScan]
	{
		_partlyExplored := _rootOrd < _rootState.start
		input := _root.Input
		_state := _e.lookupExploreState(input)
		if !_state.fullyExplored {
			_fullyExplored = false
		}
		var _member memo.RelExpr
		for _ord := 0; _ord < _state.end; _ord++ {
			if _member == nil {
				_member = input.FirstExpr()
			} else {
				_member = _member.NextExpr()
			}
			input = _member
			if !_partlyExplored || _ord >= _state.start {
				_scan, _ := _member.(*memo.ScanExpr)
				if _scan == nil {
					limit := _root.Limit
					ordering := _root.Ordering
					if _e.funcs.BoolFuncOnNonScan(input) {
						if _e.o.matchedRule == nil || _e.o.matchedRule(opt.MatchInputNonScan) {
							_expr := &memo.LimitExpr{
								Input:    _e.funcs.GenerateFuncOnNonScan(input),
								Limit:    limit,
								Ordering: ordering,
							}
							_interned := _e.mem.AddLimitToGroup(_expr, _root)
							if _e.o.appliedRule != nil {
								if _interned != _expr {
									_e.o.appliedRule(opt.MatchInputNonScan, _root, nil)
								} else {
									_e.o.appliedRule(opt.MatchInputNonScan, _root, _interned)
								}
							}
						}
					}
				}
			}
		}
	}

	return _fullyExplored
}
----
----

#
# Generate code for let expressions.
#
optgen explorer test.opt
[Relational]
define Union {
    Left  RelExpr
    Right RelExpr
}

[Let, Explore]
(Union
    $left:*
    $right:* &
      (Let ($a $b $ok):(Split $left $right) $ok)
)
=>
(Union $a $b)

[LetNestedInFunction, Explore]
(Union
    $left:*
    $right:* &
      (IsOk (New (Let ($a $b):(Split $left $right) $a))) &
      (IsOk $b)
)
=>
(Union $a $b)

[LetNestedInFunctionWithBinding, Explore]
(Union
    $left:*
    $right:* &
      (IsOK $newA:(New (Let ($a $b):(Split $left $right) $a))) &
      (IsOk $b)
)
=>
(Union $newA $b)

[LetInNestedMatch, Explore]
(Union
    $left:(Union
        $innerLeft:*
        $innerRight:* &
            (Let ($a $b $ok):(Split $innerLeft $innerRight) $ok)
    )
    $right:* &
        (IsValid $a $b)
)
=>
(Union (Union $a $b) $right)

[LetNestedInFunctionAndLet, Explore]
(Union
    $left:*
    $right:* &
        (Let ($a $b):
            (Split (Let ($newLeft):(New $left) $newLeft))
            $a
        ) &
        (IsOk $b)
)
=>
(Union $a $b)

[LetInReplace, Explore]
(Union $left:* $right:*)
=>
(Union
    (Let ($a $b):(Split $left $right) $a)
    $b
)
----
----
// 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/opt/props/physical"
	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
)

func (_e *explorer) exploreGroupMember(
	state *exploreState,
	member memo.RelExpr,
	ordinal int,
	required *physical.Required,
) (_fullyExplored bool) {
	switch t := member.(type) {
	case *memo.UnionExpr:
		return _e.exploreUnion(state, t, ordinal, required)
	}

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

func (_e *explorer) exploreUnion(
	_rootState *exploreState,
	_root *memo.UnionExpr,
	_rootOrd int,
	_required *physical.Required,
) (_fullyExplored bool) {
	opt.MaybeInjectOptimizerTestingPanic(_e.ctx, _e.evalCtx)
	_fullyExplored = true

	// [Let]
	{
		if _rootOrd >= _rootState.start {
			left := _root.Left
			right := _root.Right
			a, b, ok := _e.funcs.Split(left, right)
			if ok {
				if _e.o.matchedRule == nil || _e.o.matchedRule(opt.Let) {
					_expr := &memo.UnionExpr{
						Left:  a,
						Right: b,
					}
					_interned := _e.mem.AddUnionToGroup(_expr, _root)
					if _e.o.appliedRule != nil {
						if _interned != _expr {
							_e.o.appliedRule(opt.Let, _root, nil)
						} else {
							_e.o.appliedRule(opt.Let, _root, _interned)
						}
					}
				}
			}
		}
	}

	// [LetNestedInFunction]
	{
		if _rootOrd >= _rootState.start {
			left := _root.Left
			right := _root.Right
			a, b := _e.funcs.Split(left, right)
			if _e.funcs.IsOk(_e.funcs.New(a)) {
				if _e.funcs.IsOk(b) {
					if _e.o.matchedRule == nil || _e.o.matchedRule(opt.LetNestedInFunction) {
						_expr := &memo.UnionExpr{
							Left:  a,
							Right: b,
						}
						_interned := _e.mem.AddUnionToGroup(_expr, _root)
						if _e.o.appliedRule != nil {
							if _interned != _expr {
								_e.o.appliedRule(opt.LetNestedInFunction, _root, nil)
							} else {
								_e.o.appliedRule(opt.LetNestedInFunction, _root, _interned)
							}
						}
					}
				}
			}
		}
	}

	// [LetNestedInFunctionWithBinding]
	{
		if _rootOrd >= _rootState.start {
			left := _root.Left
			right := _root.Right
			a, b := _e.funcs.Split(left, right)
			newA := _e.funcs.New(a)
			if _e.funcs.IsOK(newA) {
				if _e.funcs.IsOk(b) {
					if _e.o.matchedRule == nil || _e.o.matchedRule(opt.LetNestedInFunctionWithBinding) {
						_expr := &memo.UnionExpr{
							Left:  newA,
							Right: b,
						}
						_interned := _e.mem.AddUnionToGroup(_expr, _root)
						if _e.o.appliedRule != nil {
							if _interned != _expr {
								_e.o.appliedRule(opt.LetNestedInFunctionWithBinding, _root, nil)
							} else {
								_e.o.appliedRule(opt.LetNestedInFunctionWithBinding, _root, _interned)
							}
						}
					}
				}
			}
		}
	}

	// [LetInNestedMatch]
	{
		_partlyExplored := _rootOrd < _rootState.start
		left := _root.Left
		_state := _e.lookupExploreState(left)
		if !_state.fullyExplored {
			_fullyExplored = false
		}
		var _member memo.RelExpr
		for _ord := 0; _ord < _state.end; _ord++ {
			if _member == nil {
				_member = left.FirstExpr()
			} else {
				_member = _member.NextExpr()
			}
			left = _member
			if !_partlyExplored || _ord >= _state.start {
				_union, _ := _member.(*memo.UnionExpr)
				if _union != nil {
					innerLeft := _union.Left
					innerRight := _union.Right
					a, b, ok := _e.funcs.Split(innerLeft, innerRight)
					if ok {
						right := _root.Right
						if _e.funcs.IsValid(a, b) {
							if _e.o.matchedRule == nil || _e.o.matchedRule(opt.LetInNestedMatch) {
								_expr := &memo.UnionExpr{
									Left: _e.f.ConstructUnion(
										a,
										b,
									),
									Right: right,
								}
								_interned := _e.mem.AddUnionToGroup(_expr, _root)
								if _e.o.appliedRule != nil {
									if _interned != _expr {
										_e.o.appliedRule(opt.LetInNestedMatch, _root, nil)
									} else {
										_e.o.appliedRule(opt.LetInNestedMatch, _root, _interned)
									}
								}
							}
						}
					}
				}
			}
		}
	}

	// [LetNestedInFunctionAndLet]
	{
		if _rootOrd >= _rootState.start {
			left := _root.Left
			right := _root.Right
			newLeft := _e.funcs.New(left)
			a, b := _e.funcs.Split(newLeft)
			if a {
				if _e.funcs.IsOk(b) {
					if _e.o.matchedRule == nil || _e.o.matchedRule(opt.LetNestedInFunctionAndLet) {
						_expr := &memo.UnionExpr{
							Left:  a,
							Right: b,
						}
						_interned := _e.mem.AddUnionToGroup(_expr, _root)
						if _e.o.appliedRule != nil {
							if _interned != _expr {
								_e.o.appliedRule(opt.LetNestedInFunctionAndLet, _root, nil)
							} else {
								_e.o.appliedRule(opt.LetNestedInFunctionAndLet, _root, _interned)
							}
						}
					}
				}
			}
		}
	}

	// [LetInReplace]
	{
		if _rootOrd >= _rootState.start {
			left := _root.Left
			right := _root.Right
			if _e.o.matchedRule == nil || _e.o.matchedRule(opt.LetInReplace) {
				a, b := _e.funcs.Split(left, right)
				_expr := &memo.UnionExpr{
					Left:  a,
					Right: b,
				}
				_interned := _e.mem.AddUnionToGroup(_expr, _root)
				if _e.o.appliedRule != nil {
					if _interned != _expr {
						_e.o.appliedRule(opt.LetInReplace, _root, nil)
					} else {
						_e.o.appliedRule(opt.LetInReplace, _root, _interned)
					}
				}
			}
		}
	}

	return _fullyExplored
}
----
----
