#
# Generate construct methods for factory that have GroupID, ListID, and
# PrivateID args.
#
optgen factory test.opt
# Not is a negate operator.
define Not {
    Input Expr
}

define FuncCall {
    Name Expr
    Args ExprList
    Def  FuncOpDef
}

# This shouldn't be added to the factory.
[Enforcer]
define Sort {
    Input Expr
}
----
----
// Code generated by optgen; [omitted]

package norm

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

// InternFuncOpDef adds the given value to the memo and returns an ID that
// can be used for later lookup. If the same value was added previously,
// this method is a no-op and returns the ID of the previous value.
func (_f *Factory) InternFuncOpDef(val *memo.FuncOpDef) memo.PrivateID {
	return _f.mem.InternFuncOpDef(val)
}

// ConstructNot constructs an expression for the Not operator.
// Not is a negate operator.
func (_f *Factory) ConstructNot(
	input memo.GroupID,
) memo.GroupID {
	_notExpr := memo.MakeNotExpr(input)
	_group := _f.mem.GroupByFingerprint(_notExpr.Fingerprint())
	if _group != 0 {
		return _group
	}

	return _f.onConstruct(memo.Expr(_notExpr))
}

// ConstructFuncCall constructs an expression for the FuncCall operator.
func (_f *Factory) ConstructFuncCall(
	name memo.GroupID,
	args memo.ListID,
	def memo.PrivateID,
) memo.GroupID {
	_funcCallExpr := memo.MakeFuncCallExpr(name, args, def)
	_group := _f.mem.GroupByFingerprint(_funcCallExpr.Fingerprint())
	if _group != 0 {
		return _group
	}

	return _f.onConstruct(memo.Expr(_funcCallExpr))
}

func (f *Factory) assignPlaceholders(group memo.GroupID) (memo.GroupID, error) {
	if !f.mem.GroupProperties(group).HasPlaceholder() {
		return group, nil
	}
	expr := f.mem.NormExpr(group)
	switch expr.Operator() {
	case opt.NotOp:
		notExpr := expr.AsNot()
		input, err := f.assignPlaceholders(notExpr.Input())
		if err != nil {
			return 0, err
		}
		return f.ConstructNot(input), nil
	case opt.FuncCallOp:
		funcCallExpr := expr.AsFuncCall()
		lb := MakeListBuilder(&f.funcs)
		name, err := f.assignPlaceholders(funcCallExpr.Name())
		if err != nil {
			return 0, err
		}
		for _, item := range f.mem.LookupList(funcCallExpr.Args()) {
			newItem, err := f.assignPlaceholders(item)
			if err != nil {
				return 0, err
			}
			lb.AddItem(newItem)
		}
		args := lb.BuildList()
		def := funcCallExpr.Def()
		return f.ConstructFuncCall(name, args, def), nil
	case opt.PlaceholderOp:
		value := expr.AsPlaceholder().Value()
		placeholder := f.mem.LookupPrivate(value).(*tree.Placeholder)
		d, err := placeholder.Eval(f.evalCtx)
		if err != nil {
			return 0, err
		}
		return f.ConstructConstVal(d), nil
	}
	panic("unhandled operator")
}

type dynConstructFunc func(f *Factory, operands memo.DynamicOperands) memo.GroupID

var dynConstructLookup [opt.NumOperators]dynConstructFunc

func init() {
	// UnknownOp
	dynConstructLookup[opt.UnknownOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		panic("op type not initialized")
	}

	// NotOp
	dynConstructLookup[opt.NotOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		return f.ConstructNot(memo.GroupID(operands[0]))
	}

	// FuncCallOp
	dynConstructLookup[opt.FuncCallOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		return f.ConstructFuncCall(memo.GroupID(operands[0]), operands[1].ListID(), memo.PrivateID(operands[2]))
	}

}

func (f *Factory) DynamicConstruct(op opt.Operator, operands memo.DynamicOperands) memo.GroupID {
	return dynConstructLookup[op](f, operands)
}
----
----

#
# Generate static matching and replacement code.
#
optgen factory test.opt
define InnerJoin {
    Left  Expr
    Right Expr
}

[CommuteJoin, Normalize]
(InnerJoin $r:* $s:*) => (InnerJoin $s $r)
----
----
// Code generated by optgen; [omitted]

package norm

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

// ConstructInnerJoin constructs an expression for the InnerJoin operator.
func (_f *Factory) ConstructInnerJoin(
	left memo.GroupID,
	right memo.GroupID,
) memo.GroupID {
	_innerJoinExpr := memo.MakeInnerJoinExpr(left, right)
	_group := _f.mem.GroupByFingerprint(_innerJoinExpr.Fingerprint())
	if _group != 0 {
		return _group
	}

	// [CommuteJoin]
	{
		r := left
		s := right
		if _f.matchedRule == nil || _f.matchedRule(opt.CommuteJoin) {
			_group = _f.ConstructInnerJoin(
				s,
				r,
			)
			_f.mem.AddAltFingerprint(_innerJoinExpr.Fingerprint(), _group)
			if _f.appliedRule != nil {
				_f.appliedRule(opt.CommuteJoin, _group, 0, 0)
			}
			return _group
		}
	}

	return _f.onConstruct(memo.Expr(_innerJoinExpr))
}

func (f *Factory) assignPlaceholders(group memo.GroupID) (memo.GroupID, error) {
	if !f.mem.GroupProperties(group).HasPlaceholder() {
		return group, nil
	}
	expr := f.mem.NormExpr(group)
	switch expr.Operator() {
	case opt.InnerJoinOp:
		innerJoinExpr := expr.AsInnerJoin()
		left, err := f.assignPlaceholders(innerJoinExpr.Left())
		if err != nil {
			return 0, err
		}
		right, err := f.assignPlaceholders(innerJoinExpr.Right())
		if err != nil {
			return 0, err
		}
		return f.ConstructInnerJoin(left, right), nil
	case opt.PlaceholderOp:
		value := expr.AsPlaceholder().Value()
		placeholder := f.mem.LookupPrivate(value).(*tree.Placeholder)
		d, err := placeholder.Eval(f.evalCtx)
		if err != nil {
			return 0, err
		}
		return f.ConstructConstVal(d), nil
	}
	panic("unhandled operator")
}

type dynConstructFunc func(f *Factory, operands memo.DynamicOperands) memo.GroupID

var dynConstructLookup [opt.NumOperators]dynConstructFunc

func init() {
	// UnknownOp
	dynConstructLookup[opt.UnknownOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		panic("op type not initialized")
	}

	// InnerJoinOp
	dynConstructLookup[opt.InnerJoinOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		return f.ConstructInnerJoin(memo.GroupID(operands[0]), memo.GroupID(operands[1]))
	}

}

func (f *Factory) DynamicConstruct(op opt.Operator, operands memo.DynamicOperands) memo.GroupID {
	return dynConstructLookup[op](f, operands)
}
----
----

#
# Generate multiple match names, not expr, custom function, literal names in
# match and replace patterns, and use OpName builtin to construct replacement
# expression with name known at compile-time.
#
optgen factory test.opt
define Eq {
    Left  Expr
    Right Expr
}

define Lt {
    Left  Expr
    Right Expr
}

define Plus {
    Left  Expr
    Right Expr
}

define Minus {
    Left  Expr
    Right Expr
}

define Const {
    Value Datum
}

[NormalizeVarPlus, Normalize]
(Eq | Lt
    (Plus
        $leftLeft:^(Const)
        $leftRight:(Const)
    )
    $right:(Const) & ^(IsInvalidBinary Minus $right $leftRight)
) =>
((OpName)
    $leftLeft
    (ConstructBinary Minus $right $leftRight)
)
----
----
// Code generated by optgen; [omitted]

package norm

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

// InternDatum adds the given value to the memo and returns an ID that
// can be used for later lookup. If the same value was added previously,
// this method is a no-op and returns the ID of the previous value.
func (_f *Factory) InternDatum(val tree.Datum) memo.PrivateID {
	return _f.mem.InternDatum(val)
}

// ConstructEq constructs an expression for the Eq operator.
func (_f *Factory) ConstructEq(
	left memo.GroupID,
	right memo.GroupID,
) memo.GroupID {
	_eqExpr := memo.MakeEqExpr(left, right)
	_group := _f.mem.GroupByFingerprint(_eqExpr.Fingerprint())
	if _group != 0 {
		return _group
	}

	// [NormalizeVarPlus]
	{
		_plusExpr := _f.mem.NormExpr(left).AsPlus()
		if _plusExpr != nil {
			leftLeft := _plusExpr.Left()
			_constExpr := _f.mem.NormExpr(_plusExpr.Left()).AsConst()
			if _constExpr == nil {
				leftRight := _plusExpr.Right()
				_constExpr2 := _f.mem.NormExpr(_plusExpr.Right()).AsConst()
				if _constExpr2 != nil {
					_constExpr3 := _f.mem.NormExpr(right).AsConst()
					if _constExpr3 != nil {
						if !_f.funcs.IsInvalidBinary(opt.MinusOp, right, leftRight) {
							if _f.matchedRule == nil || _f.matchedRule(opt.NormalizeVarPlus) {
								_group = _f.ConstructEq(
									leftLeft,
									_f.funcs.ConstructBinary(opt.MinusOp, right, leftRight),
								)
								_f.mem.AddAltFingerprint(_eqExpr.Fingerprint(), _group)
								if _f.appliedRule != nil {
									_f.appliedRule(opt.NormalizeVarPlus, _group, 0, 0)
								}
								return _group
							}
						}
					}
				}
			}
		}
	}

	return _f.onConstruct(memo.Expr(_eqExpr))
}

// ConstructLt constructs an expression for the Lt operator.
func (_f *Factory) ConstructLt(
	left memo.GroupID,
	right memo.GroupID,
) memo.GroupID {
	_ltExpr := memo.MakeLtExpr(left, right)
	_group := _f.mem.GroupByFingerprint(_ltExpr.Fingerprint())
	if _group != 0 {
		return _group
	}

	// [NormalizeVarPlus]
	{
		_plusExpr := _f.mem.NormExpr(left).AsPlus()
		if _plusExpr != nil {
			leftLeft := _plusExpr.Left()
			_constExpr := _f.mem.NormExpr(_plusExpr.Left()).AsConst()
			if _constExpr == nil {
				leftRight := _plusExpr.Right()
				_constExpr2 := _f.mem.NormExpr(_plusExpr.Right()).AsConst()
				if _constExpr2 != nil {
					_constExpr3 := _f.mem.NormExpr(right).AsConst()
					if _constExpr3 != nil {
						if !_f.funcs.IsInvalidBinary(opt.MinusOp, right, leftRight) {
							if _f.matchedRule == nil || _f.matchedRule(opt.NormalizeVarPlus) {
								_group = _f.ConstructLt(
									leftLeft,
									_f.funcs.ConstructBinary(opt.MinusOp, right, leftRight),
								)
								_f.mem.AddAltFingerprint(_ltExpr.Fingerprint(), _group)
								if _f.appliedRule != nil {
									_f.appliedRule(opt.NormalizeVarPlus, _group, 0, 0)
								}
								return _group
							}
						}
					}
				}
			}
		}
	}

	return _f.onConstruct(memo.Expr(_ltExpr))
}

// ConstructPlus constructs an expression for the Plus operator.
func (_f *Factory) ConstructPlus(
	left memo.GroupID,
	right memo.GroupID,
) memo.GroupID {
	_plusExpr := memo.MakePlusExpr(left, right)
	_group := _f.mem.GroupByFingerprint(_plusExpr.Fingerprint())
	if _group != 0 {
		return _group
	}

	return _f.onConstruct(memo.Expr(_plusExpr))
}

// ConstructMinus constructs an expression for the Minus operator.
func (_f *Factory) ConstructMinus(
	left memo.GroupID,
	right memo.GroupID,
) memo.GroupID {
	_minusExpr := memo.MakeMinusExpr(left, right)
	_group := _f.mem.GroupByFingerprint(_minusExpr.Fingerprint())
	if _group != 0 {
		return _group
	}

	return _f.onConstruct(memo.Expr(_minusExpr))
}

// ConstructConst constructs an expression for the Const operator.
func (_f *Factory) ConstructConst(
	value memo.PrivateID,
) memo.GroupID {
	_constExpr := memo.MakeConstExpr(value)
	_group := _f.mem.GroupByFingerprint(_constExpr.Fingerprint())
	if _group != 0 {
		return _group
	}

	return _f.onConstruct(memo.Expr(_constExpr))
}

func (f *Factory) assignPlaceholders(group memo.GroupID) (memo.GroupID, error) {
	if !f.mem.GroupProperties(group).HasPlaceholder() {
		return group, nil
	}
	expr := f.mem.NormExpr(group)
	switch expr.Operator() {
	case opt.EqOp:
		eqExpr := expr.AsEq()
		left, err := f.assignPlaceholders(eqExpr.Left())
		if err != nil {
			return 0, err
		}
		right, err := f.assignPlaceholders(eqExpr.Right())
		if err != nil {
			return 0, err
		}
		return f.ConstructEq(left, right), nil
	case opt.LtOp:
		ltExpr := expr.AsLt()
		left, err := f.assignPlaceholders(ltExpr.Left())
		if err != nil {
			return 0, err
		}
		right, err := f.assignPlaceholders(ltExpr.Right())
		if err != nil {
			return 0, err
		}
		return f.ConstructLt(left, right), nil
	case opt.PlusOp:
		plusExpr := expr.AsPlus()
		left, err := f.assignPlaceholders(plusExpr.Left())
		if err != nil {
			return 0, err
		}
		right, err := f.assignPlaceholders(plusExpr.Right())
		if err != nil {
			return 0, err
		}
		return f.ConstructPlus(left, right), nil
	case opt.MinusOp:
		minusExpr := expr.AsMinus()
		left, err := f.assignPlaceholders(minusExpr.Left())
		if err != nil {
			return 0, err
		}
		right, err := f.assignPlaceholders(minusExpr.Right())
		if err != nil {
			return 0, err
		}
		return f.ConstructMinus(left, right), nil
	case opt.PlaceholderOp:
		value := expr.AsPlaceholder().Value()
		placeholder := f.mem.LookupPrivate(value).(*tree.Placeholder)
		d, err := placeholder.Eval(f.evalCtx)
		if err != nil {
			return 0, err
		}
		return f.ConstructConstVal(d), nil
	}
	panic("unhandled operator")
}

type dynConstructFunc func(f *Factory, operands memo.DynamicOperands) memo.GroupID

var dynConstructLookup [opt.NumOperators]dynConstructFunc

func init() {
	// UnknownOp
	dynConstructLookup[opt.UnknownOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		panic("op type not initialized")
	}

	// EqOp
	dynConstructLookup[opt.EqOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		return f.ConstructEq(memo.GroupID(operands[0]), memo.GroupID(operands[1]))
	}

	// LtOp
	dynConstructLookup[opt.LtOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		return f.ConstructLt(memo.GroupID(operands[0]), memo.GroupID(operands[1]))
	}

	// PlusOp
	dynConstructLookup[opt.PlusOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		return f.ConstructPlus(memo.GroupID(operands[0]), memo.GroupID(operands[1]))
	}

	// MinusOp
	dynConstructLookup[opt.MinusOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		return f.ConstructMinus(memo.GroupID(operands[0]), memo.GroupID(operands[1]))
	}

	// ConstOp
	dynConstructLookup[opt.ConstOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		return f.ConstructConst(memo.PrivateID(operands[0]))
	}

}

func (f *Factory) DynamicConstruct(op opt.Operator, operands memo.DynamicOperands) memo.GroupID {
	return dynConstructLookup[op](f, operands)
}
----
----

#
# Generate not match of op with no args, string match, list match, string
# construction, custom replace function.
#
optgen factory test.opt
define Func {
    Name Expr
    Args ExprList
    Def  FuncOpDef
}

define Variable {
    Col ColumnID
}

[Concat, Normalize]
(Func "concat" $args:[ ... $item:^(Variable) & (IsEmpty $item) ... ])
=>
(Func "concat" (RemoveListItem $args $item))
----
----
// Code generated by optgen; [omitted]

package norm

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

// InternFuncOpDef adds the given value to the memo and returns an ID that
// can be used for later lookup. If the same value was added previously,
// this method is a no-op and returns the ID of the previous value.
func (_f *Factory) InternFuncOpDef(val *memo.FuncOpDef) memo.PrivateID {
	return _f.mem.InternFuncOpDef(val)
}

// InternColumnID adds the given value to the memo and returns an ID that
// can be used for later lookup. If the same value was added previously,
// this method is a no-op and returns the ID of the previous value.
func (_f *Factory) InternColumnID(val opt.ColumnID) memo.PrivateID {
	return _f.mem.InternColumnID(val)
}

// ConstructFunc constructs an expression for the Func operator.
func (_f *Factory) ConstructFunc(
	name memo.GroupID,
	args memo.ListID,
	def memo.PrivateID,
) memo.GroupID {
	_funcExpr := memo.MakeFuncExpr(name, args, def)
	_group := _f.mem.GroupByFingerprint(_funcExpr.Fingerprint())
	if _group != 0 {
		return _group
	}

	// [Concat]
	{
		if name == m.mem.InternDatum(tree.NewDString("concat")) {
			for _, _item := range _f.mem.LookupList(args) {
				item := _item
				_variableExpr := _f.mem.NormExpr(_item).AsVariable()
				if _variableExpr == nil {
					if _f.funcs.IsEmpty(item) {
						if _f.matchedRule == nil || _f.matchedRule(opt.Concat) {
							_group = _f.ConstructFunc(
								_f.mem.InternDatum(tree.NewDString("concat")),
								_f.funcs.RemoveListItem(args, item),
							)
							_f.mem.AddAltFingerprint(_funcExpr.Fingerprint(), _group)
							if _f.appliedRule != nil {
								_f.appliedRule(opt.Concat, _group, 0, 0)
							}
							return _group
						}
					}
				}
			}
		}
	}

	return _f.onConstruct(memo.Expr(_funcExpr))
}

// ConstructVariable constructs an expression for the Variable operator.
func (_f *Factory) ConstructVariable(
	col memo.PrivateID,
) memo.GroupID {
	_variableExpr := memo.MakeVariableExpr(col)
	_group := _f.mem.GroupByFingerprint(_variableExpr.Fingerprint())
	if _group != 0 {
		return _group
	}

	return _f.onConstruct(memo.Expr(_variableExpr))
}

func (f *Factory) assignPlaceholders(group memo.GroupID) (memo.GroupID, error) {
	if !f.mem.GroupProperties(group).HasPlaceholder() {
		return group, nil
	}
	expr := f.mem.NormExpr(group)
	switch expr.Operator() {
	case opt.FuncOp:
		funcExpr := expr.AsFunc()
		lb := MakeListBuilder(&f.funcs)
		name, err := f.assignPlaceholders(funcExpr.Name())
		if err != nil {
			return 0, err
		}
		for _, item := range f.mem.LookupList(funcExpr.Args()) {
			newItem, err := f.assignPlaceholders(item)
			if err != nil {
				return 0, err
			}
			lb.AddItem(newItem)
		}
		args := lb.BuildList()
		def := funcExpr.Def()
		return f.ConstructFunc(name, args, def), nil
	case opt.PlaceholderOp:
		value := expr.AsPlaceholder().Value()
		placeholder := f.mem.LookupPrivate(value).(*tree.Placeholder)
		d, err := placeholder.Eval(f.evalCtx)
		if err != nil {
			return 0, err
		}
		return f.ConstructConstVal(d), nil
	}
	panic("unhandled operator")
}

type dynConstructFunc func(f *Factory, operands memo.DynamicOperands) memo.GroupID

var dynConstructLookup [opt.NumOperators]dynConstructFunc

func init() {
	// UnknownOp
	dynConstructLookup[opt.UnknownOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		panic("op type not initialized")
	}

	// FuncOp
	dynConstructLookup[opt.FuncOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		return f.ConstructFunc(memo.GroupID(operands[0]), operands[1].ListID(), memo.PrivateID(operands[2]))
	}

	// VariableOp
	dynConstructLookup[opt.VariableOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		return f.ConstructVariable(memo.PrivateID(operands[0]))
	}

}

func (f *Factory) DynamicConstruct(op opt.Operator, operands memo.DynamicOperands) memo.GroupID {
	return dynConstructLookup[op](f, operands)
}
----
----

#
# Generate not match of op with args and not match of list. Nest matches within
# one another and use match of tag name. Match both tag and define name.
# Construct dynamic replacement and use opname literal.
#
optgen factory test.opt
define Select {
    Input Expr
    Filter Expr
}

[Join]
define InnerJoin {
    Left  Expr
    Right Expr
}

[Join]
define FullJoin {
    Left  Expr
    Right Expr
}

define Union {
    Left  Expr
    Right Expr
}

define And {
    Left  Expr
    Right Expr
}

[Test, Normalize]
(Select
    $input:^(Join|Union $r:* $s:*)
    $args:[ ... $item:(Join $t:* $u:*) ... ]
)
=>
((OpName $item) (Select $t $u) (Custom $item (OpName)))
----
----
// Code generated by optgen; [omitted]

package norm

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

// ConstructSelect constructs an expression for the Select operator.
func (_f *Factory) ConstructSelect(
	input memo.GroupID,
	filter memo.GroupID,
) memo.GroupID {
	_selectExpr := memo.MakeSelectExpr(input, filter)
	_group := _f.mem.GroupByFingerprint(_selectExpr.Fingerprint())
	if _group != 0 {
		return _group
	}

	// [Test]
	{
		_match := false
		_expr := _f.mem.NormExpr(input)
		if _expr.IsJoin() || _expr.Operator() == opt.UnionOp {
			r := _expr.ChildGroup(_f.Memo(), 0)
			s := _expr.ChildGroup(_f.Memo(), 1)
			_match = true
		}

		if !_match {
			args := filter
			for _, _item := range _f.mem.LookupList(filter) {
				item := _item
				_expr2 := _f.mem.NormExpr(_item)
				if _expr2.IsJoin() {
					t := _expr2.ChildGroup(_f.Memo(), 0)
					u := _expr2.ChildGroup(_f.Memo(), 1)
					if _f.matchedRule == nil || _f.matchedRule(opt.Test) {
						_group = _f.DynamicConstruct(
							_f.mem.NormOp(item),
							memo.DynamicOperands{
								memo.DynamicID(_f.ConstructSelect(
									t,
									u,
								)),
								memo.DynamicID(_f.funcs.Custom(item, opt.SelectOp)),
							},
						)
						_f.mem.AddAltFingerprint(_selectExpr.Fingerprint(), _group)
						if _f.appliedRule != nil {
							_f.appliedRule(opt.Test, _group, 0, 0)
						}
						return _group
					}
				}
			}
		}
	}

	return _f.onConstruct(memo.Expr(_selectExpr))
}

// ConstructInnerJoin constructs an expression for the InnerJoin operator.
func (_f *Factory) ConstructInnerJoin(
	left memo.GroupID,
	right memo.GroupID,
) memo.GroupID {
	_innerJoinExpr := memo.MakeInnerJoinExpr(left, right)
	_group := _f.mem.GroupByFingerprint(_innerJoinExpr.Fingerprint())
	if _group != 0 {
		return _group
	}

	return _f.onConstruct(memo.Expr(_innerJoinExpr))
}

// ConstructFullJoin constructs an expression for the FullJoin operator.
func (_f *Factory) ConstructFullJoin(
	left memo.GroupID,
	right memo.GroupID,
) memo.GroupID {
	_fullJoinExpr := memo.MakeFullJoinExpr(left, right)
	_group := _f.mem.GroupByFingerprint(_fullJoinExpr.Fingerprint())
	if _group != 0 {
		return _group
	}

	return _f.onConstruct(memo.Expr(_fullJoinExpr))
}

// ConstructUnion constructs an expression for the Union operator.
func (_f *Factory) ConstructUnion(
	left memo.GroupID,
	right memo.GroupID,
) memo.GroupID {
	_unionExpr := memo.MakeUnionExpr(left, right)
	_group := _f.mem.GroupByFingerprint(_unionExpr.Fingerprint())
	if _group != 0 {
		return _group
	}

	return _f.onConstruct(memo.Expr(_unionExpr))
}

// ConstructAnd constructs an expression for the And operator.
func (_f *Factory) ConstructAnd(
	left memo.GroupID,
	right memo.GroupID,
) memo.GroupID {
	_andExpr := memo.MakeAndExpr(left, right)
	_group := _f.mem.GroupByFingerprint(_andExpr.Fingerprint())
	if _group != 0 {
		return _group
	}

	return _f.onConstruct(memo.Expr(_andExpr))
}

func (f *Factory) assignPlaceholders(group memo.GroupID) (memo.GroupID, error) {
	if !f.mem.GroupProperties(group).HasPlaceholder() {
		return group, nil
	}
	expr := f.mem.NormExpr(group)
	switch expr.Operator() {
	case opt.SelectOp:
		selectExpr := expr.AsSelect()
		input, err := f.assignPlaceholders(selectExpr.Input())
		if err != nil {
			return 0, err
		}
		filter, err := f.assignPlaceholders(selectExpr.Filter())
		if err != nil {
			return 0, err
		}
		return f.ConstructSelect(input, filter), nil
	case opt.InnerJoinOp:
		innerJoinExpr := expr.AsInnerJoin()
		left, err := f.assignPlaceholders(innerJoinExpr.Left())
		if err != nil {
			return 0, err
		}
		right, err := f.assignPlaceholders(innerJoinExpr.Right())
		if err != nil {
			return 0, err
		}
		return f.ConstructInnerJoin(left, right), nil
	case opt.FullJoinOp:
		fullJoinExpr := expr.AsFullJoin()
		left, err := f.assignPlaceholders(fullJoinExpr.Left())
		if err != nil {
			return 0, err
		}
		right, err := f.assignPlaceholders(fullJoinExpr.Right())
		if err != nil {
			return 0, err
		}
		return f.ConstructFullJoin(left, right), nil
	case opt.UnionOp:
		unionExpr := expr.AsUnion()
		left, err := f.assignPlaceholders(unionExpr.Left())
		if err != nil {
			return 0, err
		}
		right, err := f.assignPlaceholders(unionExpr.Right())
		if err != nil {
			return 0, err
		}
		return f.ConstructUnion(left, right), nil
	case opt.AndOp:
		andExpr := expr.AsAnd()
		left, err := f.assignPlaceholders(andExpr.Left())
		if err != nil {
			return 0, err
		}
		right, err := f.assignPlaceholders(andExpr.Right())
		if err != nil {
			return 0, err
		}
		return f.ConstructAnd(left, right), nil
	case opt.PlaceholderOp:
		value := expr.AsPlaceholder().Value()
		placeholder := f.mem.LookupPrivate(value).(*tree.Placeholder)
		d, err := placeholder.Eval(f.evalCtx)
		if err != nil {
			return 0, err
		}
		return f.ConstructConstVal(d), nil
	}
	panic("unhandled operator")
}

type dynConstructFunc func(f *Factory, operands memo.DynamicOperands) memo.GroupID

var dynConstructLookup [opt.NumOperators]dynConstructFunc

func init() {
	// UnknownOp
	dynConstructLookup[opt.UnknownOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		panic("op type not initialized")
	}

	// SelectOp
	dynConstructLookup[opt.SelectOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		return f.ConstructSelect(memo.GroupID(operands[0]), memo.GroupID(operands[1]))
	}

	// InnerJoinOp
	dynConstructLookup[opt.InnerJoinOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		return f.ConstructInnerJoin(memo.GroupID(operands[0]), memo.GroupID(operands[1]))
	}

	// FullJoinOp
	dynConstructLookup[opt.FullJoinOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		return f.ConstructFullJoin(memo.GroupID(operands[0]), memo.GroupID(operands[1]))
	}

	// UnionOp
	dynConstructLookup[opt.UnionOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		return f.ConstructUnion(memo.GroupID(operands[0]), memo.GroupID(operands[1]))
	}

	// AndOp
	dynConstructLookup[opt.AndOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		return f.ConstructAnd(memo.GroupID(operands[0]), memo.GroupID(operands[1]))
	}

}

func (f *Factory) DynamicConstruct(op opt.Operator, operands memo.DynamicOperands) memo.GroupID {
	return dynConstructLookup[op](f, operands)
}
----
----

#
# Construct dynamic replacement that uses private shared by both operators.
#
optgen factory test.opt

define Select {
    Input  Expr
    Filter Expr
}

[GroupByOps]
define GroupBy {
    Input Expr
    Aggs  Expr
    Def   GroupByDef
}

[GroupByOps]
define ScalarGroupBy {
    Input Expr
    Aggs  Expr
    Def   GroupByDef
}

define LookupJoin {
    Input Expr
    On    Expr
    Def   LookupJoinDef
}

[Test, Normalize]
(Select
    $input:(LookupJoin | GroupByOps
        $expr1:*
        $expr2:*
        $private:*
    )
    (True)
)
=>
((OpName $input) $expr1 $expr2 $private)
----
----
// Code generated by optgen; [omitted]

package norm

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

// InternGroupByDef adds the given value to the memo and returns an ID that
// can be used for later lookup. If the same value was added previously,
// this method is a no-op and returns the ID of the previous value.
func (_f *Factory) InternGroupByDef(val *memo.GroupByDef) memo.PrivateID {
	return _f.mem.InternGroupByDef(val)
}

// InternLookupJoinDef adds the given value to the memo and returns an ID that
// can be used for later lookup. If the same value was added previously,
// this method is a no-op and returns the ID of the previous value.
func (_f *Factory) InternLookupJoinDef(val *memo.LookupJoinDef) memo.PrivateID {
	return _f.mem.InternLookupJoinDef(val)
}

// ConstructSelect constructs an expression for the Select operator.
func (_f *Factory) ConstructSelect(
	input memo.GroupID,
	filter memo.GroupID,
) memo.GroupID {
	_selectExpr := memo.MakeSelectExpr(input, filter)
	_group := _f.mem.GroupByFingerprint(_selectExpr.Fingerprint())
	if _group != 0 {
		return _group
	}

	// [Test]
	{
		_expr := _f.mem.NormExpr(input)
		if _expr.Operator() == opt.LookupJoinOp || _expr.IsGroupByOps() {
			expr1 := _expr.ChildGroup(_f.Memo(), 0)
			expr2 := _expr.ChildGroup(_f.Memo(), 1)
			private := _expr.PrivateID()
			if _f.funcs.True() {
				if _f.matchedRule == nil || _f.matchedRule(opt.Test) {
					_group = _f.DynamicConstruct(
						_f.mem.NormOp(input),
						memo.DynamicOperands{
							memo.DynamicID(expr1),
							memo.DynamicID(expr2),
							memo.DynamicID(private),
						},
					)
					_f.mem.AddAltFingerprint(_selectExpr.Fingerprint(), _group)
					if _f.appliedRule != nil {
						_f.appliedRule(opt.Test, _group, 0, 0)
					}
					return _group
				}
			}
		}
	}

	return _f.onConstruct(memo.Expr(_selectExpr))
}

// ConstructGroupBy constructs an expression for the GroupBy operator.
func (_f *Factory) ConstructGroupBy(
	input memo.GroupID,
	aggs memo.GroupID,
	def memo.PrivateID,
) memo.GroupID {
	_groupByExpr := memo.MakeGroupByExpr(input, aggs, def)
	_group := _f.mem.GroupByFingerprint(_groupByExpr.Fingerprint())
	if _group != 0 {
		return _group
	}

	return _f.onConstruct(memo.Expr(_groupByExpr))
}

// ConstructScalarGroupBy constructs an expression for the ScalarGroupBy operator.
func (_f *Factory) ConstructScalarGroupBy(
	input memo.GroupID,
	aggs memo.GroupID,
	def memo.PrivateID,
) memo.GroupID {
	_scalarGroupByExpr := memo.MakeScalarGroupByExpr(input, aggs, def)
	_group := _f.mem.GroupByFingerprint(_scalarGroupByExpr.Fingerprint())
	if _group != 0 {
		return _group
	}

	return _f.onConstruct(memo.Expr(_scalarGroupByExpr))
}

// ConstructLookupJoin constructs an expression for the LookupJoin operator.
func (_f *Factory) ConstructLookupJoin(
	input memo.GroupID,
	on memo.GroupID,
	def memo.PrivateID,
) memo.GroupID {
	_lookupJoinExpr := memo.MakeLookupJoinExpr(input, on, def)
	_group := _f.mem.GroupByFingerprint(_lookupJoinExpr.Fingerprint())
	if _group != 0 {
		return _group
	}

	return _f.onConstruct(memo.Expr(_lookupJoinExpr))
}

func (f *Factory) assignPlaceholders(group memo.GroupID) (memo.GroupID, error) {
	if !f.mem.GroupProperties(group).HasPlaceholder() {
		return group, nil
	}
	expr := f.mem.NormExpr(group)
	switch expr.Operator() {
	case opt.SelectOp:
		selectExpr := expr.AsSelect()
		input, err := f.assignPlaceholders(selectExpr.Input())
		if err != nil {
			return 0, err
		}
		filter, err := f.assignPlaceholders(selectExpr.Filter())
		if err != nil {
			return 0, err
		}
		return f.ConstructSelect(input, filter), nil
	case opt.GroupByOp:
		groupByExpr := expr.AsGroupBy()
		input, err := f.assignPlaceholders(groupByExpr.Input())
		if err != nil {
			return 0, err
		}
		aggs, err := f.assignPlaceholders(groupByExpr.Aggs())
		if err != nil {
			return 0, err
		}
		def := groupByExpr.Def()
		return f.ConstructGroupBy(input, aggs, def), nil
	case opt.ScalarGroupByOp:
		scalarGroupByExpr := expr.AsScalarGroupBy()
		input, err := f.assignPlaceholders(scalarGroupByExpr.Input())
		if err != nil {
			return 0, err
		}
		aggs, err := f.assignPlaceholders(scalarGroupByExpr.Aggs())
		if err != nil {
			return 0, err
		}
		def := scalarGroupByExpr.Def()
		return f.ConstructScalarGroupBy(input, aggs, def), nil
	case opt.LookupJoinOp:
		lookupJoinExpr := expr.AsLookupJoin()
		input, err := f.assignPlaceholders(lookupJoinExpr.Input())
		if err != nil {
			return 0, err
		}
		on, err := f.assignPlaceholders(lookupJoinExpr.On())
		if err != nil {
			return 0, err
		}
		def := lookupJoinExpr.Def()
		return f.ConstructLookupJoin(input, on, def), nil
	case opt.PlaceholderOp:
		value := expr.AsPlaceholder().Value()
		placeholder := f.mem.LookupPrivate(value).(*tree.Placeholder)
		d, err := placeholder.Eval(f.evalCtx)
		if err != nil {
			return 0, err
		}
		return f.ConstructConstVal(d), nil
	}
	panic("unhandled operator")
}

type dynConstructFunc func(f *Factory, operands memo.DynamicOperands) memo.GroupID

var dynConstructLookup [opt.NumOperators]dynConstructFunc

func init() {
	// UnknownOp
	dynConstructLookup[opt.UnknownOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		panic("op type not initialized")
	}

	// SelectOp
	dynConstructLookup[opt.SelectOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		return f.ConstructSelect(memo.GroupID(operands[0]), memo.GroupID(operands[1]))
	}

	// GroupByOp
	dynConstructLookup[opt.GroupByOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		return f.ConstructGroupBy(memo.GroupID(operands[0]), memo.GroupID(operands[1]), memo.PrivateID(operands[2]))
	}

	// ScalarGroupByOp
	dynConstructLookup[opt.ScalarGroupByOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		return f.ConstructScalarGroupBy(memo.GroupID(operands[0]), memo.GroupID(operands[1]), memo.PrivateID(operands[2]))
	}

	// LookupJoinOp
	dynConstructLookup[opt.LookupJoinOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		return f.ConstructLookupJoin(memo.GroupID(operands[0]), memo.GroupID(operands[1]), memo.PrivateID(operands[2]))
	}

}

func (f *Factory) DynamicConstruct(op opt.Operator, operands memo.DynamicOperands) memo.GroupID {
	return dynConstructLookup[op](f, operands)
}
----
----
#
# Generate match for all list match operators.
#
optgen factory test.opt
define List {
    Items ExprList
}

[List, Normalize]
(List $any:[
    ...
    (List $first:[
        (List $last:[
            ...
            (List $single:[
                (List $empty:[])
            ])
        ])
        ...
    ])
    ...
])
=>
(Construct $any $first $last $single $empty)
----
----
// Code generated by optgen; [omitted]

package norm

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

// ConstructList constructs an expression for the List operator.
func (_f *Factory) ConstructList(
	items memo.ListID,
) memo.GroupID {
	_listExpr := memo.MakeListExpr(items)
	_group := _f.mem.GroupByFingerprint(_listExpr.Fingerprint())
	if _group != 0 {
		return _group
	}

	// [List]
	{
		any := items
		for _, _item := range _f.mem.LookupList(items) {
			_listExpr2 := _f.mem.NormExpr(_item).AsList()
			if _listExpr2 != nil {
				first := _listExpr2.Items()
				if _listExpr2.Items().Length > 0 {
					_item := _f.mem.LookupList(_listExpr2.Items())[0]
					_listExpr3 := _f.mem.NormExpr(_item).AsList()
					if _listExpr3 != nil {
						last := _listExpr3.Items()
						if _listExpr3.Items().Length > 0 {
							_item := _f.mem.LookupList(_listExpr3.Items())[_listExpr3.Items().Length-1]
							_listExpr4 := _f.mem.NormExpr(_item).AsList()
							if _listExpr4 != nil {
								single := _listExpr4.Items()
								if _listExpr4.Items().Length == 1 {
									_item := _f.mem.LookupList(_listExpr4.Items())[0]
									_listExpr5 := _f.mem.NormExpr(_item).AsList()
									if _listExpr5 != nil {
										empty := _listExpr5.Items()
										if _listExpr5.Items().Length == 0 {
											if _f.matchedRule == nil || _f.matchedRule(opt.List) {
												_group = _f.funcs.Construct(any, first, last, single, empty)
												_f.mem.AddAltFingerprint(_listExpr.Fingerprint(), _group)
												if _f.appliedRule != nil {
													_f.appliedRule(opt.List, _group, 0, 0)
												}
												return _group
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}
	}

	return _f.onConstruct(memo.Expr(_listExpr))
}

func (f *Factory) assignPlaceholders(group memo.GroupID) (memo.GroupID, error) {
	if !f.mem.GroupProperties(group).HasPlaceholder() {
		return group, nil
	}
	expr := f.mem.NormExpr(group)
	switch expr.Operator() {
	case opt.ListOp:
		listExpr := expr.AsList()
		lb := MakeListBuilder(&f.funcs)
		for _, item := range f.mem.LookupList(listExpr.Items()) {
			newItem, err := f.assignPlaceholders(item)
			if err != nil {
				return 0, err
			}
			lb.AddItem(newItem)
		}
		items := lb.BuildList()
		return f.ConstructList(items), nil
	case opt.PlaceholderOp:
		value := expr.AsPlaceholder().Value()
		placeholder := f.mem.LookupPrivate(value).(*tree.Placeholder)
		d, err := placeholder.Eval(f.evalCtx)
		if err != nil {
			return 0, err
		}
		return f.ConstructConstVal(d), nil
	}
	panic("unhandled operator")
}

type dynConstructFunc func(f *Factory, operands memo.DynamicOperands) memo.GroupID

var dynConstructLookup [opt.NumOperators]dynConstructFunc

func init() {
	// UnknownOp
	dynConstructLookup[opt.UnknownOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		panic("op type not initialized")
	}

	// ListOp
	dynConstructLookup[opt.ListOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		return f.ConstructList(operands[0].ListID())
	}

}

func (f *Factory) DynamicConstruct(op opt.Operator, operands memo.DynamicOperands) memo.GroupID {
	return dynConstructLookup[op](f, operands)
}
----
----

#
# Generate list construction operators.
#
optgen factory test.opt
define Join {
    Left  Expr
    Right Expr
    On    Expr
}

[ConstructList, Normalize]
(Join $left:* $right:* $on:*)
=>
(Construct [] [ $left ] [ $left $right] [ [$on] ])
----
----
// Code generated by optgen; [omitted]

package norm

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

// ConstructJoin constructs an expression for the Join operator.
func (_f *Factory) ConstructJoin(
	left memo.GroupID,
	right memo.GroupID,
	on memo.GroupID,
) memo.GroupID {
	_joinExpr := memo.MakeJoinExpr(left, right, on)
	_group := _f.mem.GroupByFingerprint(_joinExpr.Fingerprint())
	if _group != 0 {
		return _group
	}

	// [ConstructList]
	{
		if _f.matchedRule == nil || _f.matchedRule(opt.ConstructList) {
			_group = _f.funcs.Construct(_f.mem.InternList([]memo.GroupID{}), _f.mem.InternList([]memo.GroupID{left}), _f.mem.InternList([]memo.GroupID{left, right}), _f.mem.InternList([]memo.GroupID{_f.mem.InternList([]memo.GroupID{on})}))
			_f.mem.AddAltFingerprint(_joinExpr.Fingerprint(), _group)
			if _f.appliedRule != nil {
				_f.appliedRule(opt.ConstructList, _group, 0, 0)
			}
			return _group
		}
	}

	return _f.onConstruct(memo.Expr(_joinExpr))
}

func (f *Factory) assignPlaceholders(group memo.GroupID) (memo.GroupID, error) {
	if !f.mem.GroupProperties(group).HasPlaceholder() {
		return group, nil
	}
	expr := f.mem.NormExpr(group)
	switch expr.Operator() {
	case opt.JoinOp:
		joinExpr := expr.AsJoin()
		left, err := f.assignPlaceholders(joinExpr.Left())
		if err != nil {
			return 0, err
		}
		right, err := f.assignPlaceholders(joinExpr.Right())
		if err != nil {
			return 0, err
		}
		on, err := f.assignPlaceholders(joinExpr.On())
		if err != nil {
			return 0, err
		}
		return f.ConstructJoin(left, right, on), nil
	case opt.PlaceholderOp:
		value := expr.AsPlaceholder().Value()
		placeholder := f.mem.LookupPrivate(value).(*tree.Placeholder)
		d, err := placeholder.Eval(f.evalCtx)
		if err != nil {
			return 0, err
		}
		return f.ConstructConstVal(d), nil
	}
	panic("unhandled operator")
}

type dynConstructFunc func(f *Factory, operands memo.DynamicOperands) memo.GroupID

var dynConstructLookup [opt.NumOperators]dynConstructFunc

func init() {
	// UnknownOp
	dynConstructLookup[opt.UnknownOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		panic("op type not initialized")
	}

	// JoinOp
	dynConstructLookup[opt.JoinOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		return f.ConstructJoin(memo.GroupID(operands[0]), memo.GroupID(operands[1]), memo.GroupID(operands[2]))
	}

}

func (f *Factory) DynamicConstruct(op opt.Operator, operands memo.DynamicOperands) memo.GroupID {
	return dynConstructLookup[op](f, operands)
}
----
----

#
# Generate no match for all list match operators that support it.
#
optgen factory test.opt
define List {
    Items ExprList
}

define Op {
    Empty  Expr
    Single Expr
}

[ListNot, Normalize]
(Op
    $empty:(List ^[])
    $single:(List ^[ * ])
)
=>
(Op $empty $single)
----
----
// Code generated by optgen; [omitted]

package norm

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

// ConstructList constructs an expression for the List operator.
func (_f *Factory) ConstructList(
	items memo.ListID,
) memo.GroupID {
	_listExpr := memo.MakeListExpr(items)
	_group := _f.mem.GroupByFingerprint(_listExpr.Fingerprint())
	if _group != 0 {
		return _group
	}

	return _f.onConstruct(memo.Expr(_listExpr))
}

// ConstructOp constructs an expression for the Op operator.
func (_f *Factory) ConstructOp(
	empty memo.GroupID,
	single memo.GroupID,
) memo.GroupID {
	_opExpr := memo.MakeOpExpr(empty, single)
	_group := _f.mem.GroupByFingerprint(_opExpr.Fingerprint())
	if _group != 0 {
		return _group
	}

	// [ListNot]
	{
		_listExpr := _f.mem.NormExpr(empty).AsList()
		if _listExpr != nil {
			if _listExpr.Items().Length != 0 {
				_listExpr2 := _f.mem.NormExpr(single).AsList()
				if _listExpr2 != nil {
					if _listExpr2.Items().Length != 1 {
						if _f.matchedRule == nil || _f.matchedRule(opt.ListNot) {
							_group = _f.ConstructOp(
								empty,
								single,
							)
							_f.mem.AddAltFingerprint(_opExpr.Fingerprint(), _group)
							if _f.appliedRule != nil {
								_f.appliedRule(opt.ListNot, _group, 0, 0)
							}
							return _group
						}
					}
				}
			}
		}
	}

	return _f.onConstruct(memo.Expr(_opExpr))
}

func (f *Factory) assignPlaceholders(group memo.GroupID) (memo.GroupID, error) {
	if !f.mem.GroupProperties(group).HasPlaceholder() {
		return group, nil
	}
	expr := f.mem.NormExpr(group)
	switch expr.Operator() {
	case opt.ListOp:
		listExpr := expr.AsList()
		lb := MakeListBuilder(&f.funcs)
		for _, item := range f.mem.LookupList(listExpr.Items()) {
			newItem, err := f.assignPlaceholders(item)
			if err != nil {
				return 0, err
			}
			lb.AddItem(newItem)
		}
		items := lb.BuildList()
		return f.ConstructList(items), nil
	case opt.OpOp:
		opExpr := expr.AsOp()
		empty, err := f.assignPlaceholders(opExpr.Empty())
		if err != nil {
			return 0, err
		}
		single, err := f.assignPlaceholders(opExpr.Single())
		if err != nil {
			return 0, err
		}
		return f.ConstructOp(empty, single), nil
	case opt.PlaceholderOp:
		value := expr.AsPlaceholder().Value()
		placeholder := f.mem.LookupPrivate(value).(*tree.Placeholder)
		d, err := placeholder.Eval(f.evalCtx)
		if err != nil {
			return 0, err
		}
		return f.ConstructConstVal(d), nil
	}
	panic("unhandled operator")
}

type dynConstructFunc func(f *Factory, operands memo.DynamicOperands) memo.GroupID

var dynConstructLookup [opt.NumOperators]dynConstructFunc

func init() {
	// UnknownOp
	dynConstructLookup[opt.UnknownOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		panic("op type not initialized")
	}

	// ListOp
	dynConstructLookup[opt.ListOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		return f.ConstructList(operands[0].ListID())
	}

	// OpOp
	dynConstructLookup[opt.OpOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		return f.ConstructOp(memo.GroupID(operands[0]), memo.GroupID(operands[1]))
	}

}

func (f *Factory) DynamicConstruct(op opt.Operator, operands memo.DynamicOperands) memo.GroupID {
	return dynConstructLookup[op](f, operands)
}
----
----

#
# Generate match and nomatch for both constant and dynamic match cases, with
# and without child matchers.
#
optgen factory test.opt
define Eq {
    Left  Expr
    Right Expr
}

define Ne {
    Left  Expr
    Right Expr
}

[Constant, Normalize]
(Eq
    (Eq (Eq) ^(Eq))
    ^(Eq (Eq) ^(Eq))
)
=>
(Eq "foo" "bar")

[Dynamic, Normalize]
(Ne
    (Eq|Ne (Eq|Ne) ^(Eq|Ne))
    ^(Eq|Ne (Eq|Ne) ^(Eq|Ne))
)
=>
(Eq "foo" "bar")
----
----
// Code generated by optgen; [omitted]

package norm

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

// ConstructEq constructs an expression for the Eq operator.
func (_f *Factory) ConstructEq(
	left memo.GroupID,
	right memo.GroupID,
) memo.GroupID {
	_eqExpr := memo.MakeEqExpr(left, right)
	_group := _f.mem.GroupByFingerprint(_eqExpr.Fingerprint())
	if _group != 0 {
		return _group
	}

	// [Constant]
	{
		_eqExpr2 := _f.mem.NormExpr(left).AsEq()
		if _eqExpr2 != nil {
			_eqExpr3 := _f.mem.NormExpr(_eqExpr2.Left()).AsEq()
			if _eqExpr3 != nil {
				_eqExpr4 := _f.mem.NormExpr(_eqExpr2.Right()).AsEq()
				if _eqExpr4 == nil {
					_match := false
					_eqExpr5 := _f.mem.NormExpr(right).AsEq()
					if _eqExpr5 != nil {
						_eqExpr6 := _f.mem.NormExpr(_eqExpr5.Left()).AsEq()
						if _eqExpr6 != nil {
							_eqExpr7 := _f.mem.NormExpr(_eqExpr5.Right()).AsEq()
							if _eqExpr7 == nil {
								_match = true
							}
						}
					}

					if !_match {
						if _f.matchedRule == nil || _f.matchedRule(opt.Constant) {
							_group = _f.ConstructEq(
								_f.mem.InternDatum(tree.NewDString("foo")),
								_f.mem.InternDatum(tree.NewDString("bar")),
							)
							_f.mem.AddAltFingerprint(_eqExpr.Fingerprint(), _group)
							if _f.appliedRule != nil {
								_f.appliedRule(opt.Constant, _group, 0, 0)
							}
							return _group
						}
					}
				}
			}
		}
	}

	return _f.onConstruct(memo.Expr(_eqExpr))
}

// ConstructNe constructs an expression for the Ne operator.
func (_f *Factory) ConstructNe(
	left memo.GroupID,
	right memo.GroupID,
) memo.GroupID {
	_neExpr := memo.MakeNeExpr(left, right)
	_group := _f.mem.GroupByFingerprint(_neExpr.Fingerprint())
	if _group != 0 {
		return _group
	}

	// [Dynamic]
	{
		_expr := _f.mem.NormExpr(left)
		if _expr.Operator() == opt.EqOp || _expr.Operator() == opt.NeOp {
			_expr2 := _f.mem.NormExpr(_expr.ChildGroup(_f.Memo(), 0))
			if _expr2.Operator() == opt.EqOp || _expr2.Operator() == opt.NeOp {
				_expr3 := _f.mem.NormExpr(_expr.ChildGroup(_f.Memo(), 1))
				if !(_expr3.Operator() == opt.EqOp || _expr3.Operator() == opt.NeOp) {
					_match := false
					_expr4 := _f.mem.NormExpr(right)
					if _expr4.Operator() == opt.EqOp || _expr4.Operator() == opt.NeOp {
						_expr5 := _f.mem.NormExpr(_expr4.ChildGroup(_f.Memo(), 0))
						if _expr5.Operator() == opt.EqOp || _expr5.Operator() == opt.NeOp {
							_expr6 := _f.mem.NormExpr(_expr4.ChildGroup(_f.Memo(), 1))
							if !(_expr6.Operator() == opt.EqOp || _expr6.Operator() == opt.NeOp) {
								_match = true
							}
						}
					}

					if !_match {
						if _f.matchedRule == nil || _f.matchedRule(opt.Dynamic) {
							_group = _f.ConstructEq(
								_f.mem.InternDatum(tree.NewDString("foo")),
								_f.mem.InternDatum(tree.NewDString("bar")),
							)
							_f.mem.AddAltFingerprint(_neExpr.Fingerprint(), _group)
							if _f.appliedRule != nil {
								_f.appliedRule(opt.Dynamic, _group, 0, 0)
							}
							return _group
						}
					}
				}
			}
		}
	}

	return _f.onConstruct(memo.Expr(_neExpr))
}

func (f *Factory) assignPlaceholders(group memo.GroupID) (memo.GroupID, error) {
	if !f.mem.GroupProperties(group).HasPlaceholder() {
		return group, nil
	}
	expr := f.mem.NormExpr(group)
	switch expr.Operator() {
	case opt.EqOp:
		eqExpr := expr.AsEq()
		left, err := f.assignPlaceholders(eqExpr.Left())
		if err != nil {
			return 0, err
		}
		right, err := f.assignPlaceholders(eqExpr.Right())
		if err != nil {
			return 0, err
		}
		return f.ConstructEq(left, right), nil
	case opt.NeOp:
		neExpr := expr.AsNe()
		left, err := f.assignPlaceholders(neExpr.Left())
		if err != nil {
			return 0, err
		}
		right, err := f.assignPlaceholders(neExpr.Right())
		if err != nil {
			return 0, err
		}
		return f.ConstructNe(left, right), nil
	case opt.PlaceholderOp:
		value := expr.AsPlaceholder().Value()
		placeholder := f.mem.LookupPrivate(value).(*tree.Placeholder)
		d, err := placeholder.Eval(f.evalCtx)
		if err != nil {
			return 0, err
		}
		return f.ConstructConstVal(d), nil
	}
	panic("unhandled operator")
}

type dynConstructFunc func(f *Factory, operands memo.DynamicOperands) memo.GroupID

var dynConstructLookup [opt.NumOperators]dynConstructFunc

func init() {
	// UnknownOp
	dynConstructLookup[opt.UnknownOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		panic("op type not initialized")
	}

	// EqOp
	dynConstructLookup[opt.EqOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		return f.ConstructEq(memo.GroupID(operands[0]), memo.GroupID(operands[1]))
	}

	// NeOp
	dynConstructLookup[opt.NeOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		return f.ConstructNe(memo.GroupID(operands[0]), memo.GroupID(operands[1]))
	}

}

func (f *Factory) DynamicConstruct(op opt.Operator, operands memo.DynamicOperands) memo.GroupID {
	return dynConstructLookup[op](f, operands)
}
----
----

#
# Generate nested custom matching functions, including OpName.
#
optgen factory test.opt
[Binary]
define Plus {
    Left  Expr
    Right Expr
}

[Binary]
define Minus {
    Left  Expr
    Right Expr
}

define Null {}

[Fold, Normalize]
(Binary
    $left:*
    (Null) & ^(HasNullableArgs (OpName) (AnotherFunc (OpName $left)))
)
=>
(Null)
----
----
// Code generated by optgen; [omitted]

package norm

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

// ConstructPlus constructs an expression for the Plus operator.
func (_f *Factory) ConstructPlus(
	left memo.GroupID,
	right memo.GroupID,
) memo.GroupID {
	_plusExpr := memo.MakePlusExpr(left, right)
	_group := _f.mem.GroupByFingerprint(_plusExpr.Fingerprint())
	if _group != 0 {
		return _group
	}

	// [Fold]
	{
		_nullExpr := _f.mem.NormExpr(right).AsNull()
		if _nullExpr != nil {
			if !_f.funcs.HasNullableArgs(opt.PlusOp, _f.funcs.AnotherFunc(_f.mem.NormExpr(left).Operator())) {
				if _f.matchedRule == nil || _f.matchedRule(opt.Fold) {
					_group = _f.ConstructNull()
					_f.mem.AddAltFingerprint(_plusExpr.Fingerprint(), _group)
					if _f.appliedRule != nil {
						_f.appliedRule(opt.Fold, _group, 0, 0)
					}
					return _group
				}
			}
		}
	}

	return _f.onConstruct(memo.Expr(_plusExpr))
}

// ConstructMinus constructs an expression for the Minus operator.
func (_f *Factory) ConstructMinus(
	left memo.GroupID,
	right memo.GroupID,
) memo.GroupID {
	_minusExpr := memo.MakeMinusExpr(left, right)
	_group := _f.mem.GroupByFingerprint(_minusExpr.Fingerprint())
	if _group != 0 {
		return _group
	}

	// [Fold]
	{
		_nullExpr := _f.mem.NormExpr(right).AsNull()
		if _nullExpr != nil {
			if !_f.funcs.HasNullableArgs(opt.MinusOp, _f.funcs.AnotherFunc(_f.mem.NormExpr(left).Operator())) {
				if _f.matchedRule == nil || _f.matchedRule(opt.Fold) {
					_group = _f.ConstructNull()
					_f.mem.AddAltFingerprint(_minusExpr.Fingerprint(), _group)
					if _f.appliedRule != nil {
						_f.appliedRule(opt.Fold, _group, 0, 0)
					}
					return _group
				}
			}
		}
	}

	return _f.onConstruct(memo.Expr(_minusExpr))
}

// ConstructNull constructs an expression for the Null operator.
func (_f *Factory) ConstructNull() memo.GroupID {
	_nullExpr := memo.MakeNullExpr()
	_group := _f.mem.GroupByFingerprint(_nullExpr.Fingerprint())
	if _group != 0 {
		return _group
	}

	return _f.onConstruct(memo.Expr(_nullExpr))
}

func (f *Factory) assignPlaceholders(group memo.GroupID) (memo.GroupID, error) {
	if !f.mem.GroupProperties(group).HasPlaceholder() {
		return group, nil
	}
	expr := f.mem.NormExpr(group)
	switch expr.Operator() {
	case opt.PlusOp:
		plusExpr := expr.AsPlus()
		left, err := f.assignPlaceholders(plusExpr.Left())
		if err != nil {
			return 0, err
		}
		right, err := f.assignPlaceholders(plusExpr.Right())
		if err != nil {
			return 0, err
		}
		return f.ConstructPlus(left, right), nil
	case opt.MinusOp:
		minusExpr := expr.AsMinus()
		left, err := f.assignPlaceholders(minusExpr.Left())
		if err != nil {
			return 0, err
		}
		right, err := f.assignPlaceholders(minusExpr.Right())
		if err != nil {
			return 0, err
		}
		return f.ConstructMinus(left, right), nil
	case opt.PlaceholderOp:
		value := expr.AsPlaceholder().Value()
		placeholder := f.mem.LookupPrivate(value).(*tree.Placeholder)
		d, err := placeholder.Eval(f.evalCtx)
		if err != nil {
			return 0, err
		}
		return f.ConstructConstVal(d), nil
	}
	panic("unhandled operator")
}

type dynConstructFunc func(f *Factory, operands memo.DynamicOperands) memo.GroupID

var dynConstructLookup [opt.NumOperators]dynConstructFunc

func init() {
	// UnknownOp
	dynConstructLookup[opt.UnknownOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		panic("op type not initialized")
	}

	// PlusOp
	dynConstructLookup[opt.PlusOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		return f.ConstructPlus(memo.GroupID(operands[0]), memo.GroupID(operands[1]))
	}

	// MinusOp
	dynConstructLookup[opt.MinusOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		return f.ConstructMinus(memo.GroupID(operands[0]), memo.GroupID(operands[1]))
	}

	// NullOp
	dynConstructLookup[opt.NullOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		return f.ConstructNull()
	}

}

func (f *Factory) DynamicConstruct(op opt.Operator, operands memo.DynamicOperands) memo.GroupID {
	return dynConstructLookup[op](f, operands)
}
----
----

#
# Generate multiple op matchers combined into a boolean expression.
#
optgen factory test.opt
[Comparison]
define Lt {
    Left  Expr
    Right Expr
}

[Comparison]
define Gt {
    Left  Expr
    Right Expr
}

[Comparison]
define Contains {
    Left  Expr
    Right Expr
}

define Not {
    Input Expr
}

[Invert, Normalize]
(Not $input:(Comparison $left:* $right:*) & ^(Contains) & (SomeOtherCondition $input))
=>
(Invert (OpName $input) $left $right)
----
----
// Code generated by optgen; [omitted]

package norm

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

// ConstructLt constructs an expression for the Lt operator.
func (_f *Factory) ConstructLt(
	left memo.GroupID,
	right memo.GroupID,
) memo.GroupID {
	_ltExpr := memo.MakeLtExpr(left, right)
	_group := _f.mem.GroupByFingerprint(_ltExpr.Fingerprint())
	if _group != 0 {
		return _group
	}

	return _f.onConstruct(memo.Expr(_ltExpr))
}

// ConstructGt constructs an expression for the Gt operator.
func (_f *Factory) ConstructGt(
	left memo.GroupID,
	right memo.GroupID,
) memo.GroupID {
	_gtExpr := memo.MakeGtExpr(left, right)
	_group := _f.mem.GroupByFingerprint(_gtExpr.Fingerprint())
	if _group != 0 {
		return _group
	}

	return _f.onConstruct(memo.Expr(_gtExpr))
}

// ConstructContains constructs an expression for the Contains operator.
func (_f *Factory) ConstructContains(
	left memo.GroupID,
	right memo.GroupID,
) memo.GroupID {
	_containsExpr := memo.MakeContainsExpr(left, right)
	_group := _f.mem.GroupByFingerprint(_containsExpr.Fingerprint())
	if _group != 0 {
		return _group
	}

	return _f.onConstruct(memo.Expr(_containsExpr))
}

// ConstructNot constructs an expression for the Not operator.
func (_f *Factory) ConstructNot(
	input memo.GroupID,
) memo.GroupID {
	_notExpr := memo.MakeNotExpr(input)
	_group := _f.mem.GroupByFingerprint(_notExpr.Fingerprint())
	if _group != 0 {
		return _group
	}

	// [Invert]
	{
		_expr := _f.mem.NormExpr(input)
		if _expr.IsComparison() {
			left := _expr.ChildGroup(_f.Memo(), 0)
			right := _expr.ChildGroup(_f.Memo(), 1)
			_containsExpr := _f.mem.NormExpr(input).AsContains()
			if _containsExpr == nil {
				if _f.funcs.SomeOtherCondition(input) {
					if _f.matchedRule == nil || _f.matchedRule(opt.Invert) {
						_group = _f.funcs.Invert(_f.mem.NormExpr(input).Operator(), left, right)
						_f.mem.AddAltFingerprint(_notExpr.Fingerprint(), _group)
						if _f.appliedRule != nil {
							_f.appliedRule(opt.Invert, _group, 0, 0)
						}
						return _group
					}
				}
			}
		}
	}

	return _f.onConstruct(memo.Expr(_notExpr))
}

func (f *Factory) assignPlaceholders(group memo.GroupID) (memo.GroupID, error) {
	if !f.mem.GroupProperties(group).HasPlaceholder() {
		return group, nil
	}
	expr := f.mem.NormExpr(group)
	switch expr.Operator() {
	case opt.LtOp:
		ltExpr := expr.AsLt()
		left, err := f.assignPlaceholders(ltExpr.Left())
		if err != nil {
			return 0, err
		}
		right, err := f.assignPlaceholders(ltExpr.Right())
		if err != nil {
			return 0, err
		}
		return f.ConstructLt(left, right), nil
	case opt.GtOp:
		gtExpr := expr.AsGt()
		left, err := f.assignPlaceholders(gtExpr.Left())
		if err != nil {
			return 0, err
		}
		right, err := f.assignPlaceholders(gtExpr.Right())
		if err != nil {
			return 0, err
		}
		return f.ConstructGt(left, right), nil
	case opt.ContainsOp:
		containsExpr := expr.AsContains()
		left, err := f.assignPlaceholders(containsExpr.Left())
		if err != nil {
			return 0, err
		}
		right, err := f.assignPlaceholders(containsExpr.Right())
		if err != nil {
			return 0, err
		}
		return f.ConstructContains(left, right), nil
	case opt.NotOp:
		notExpr := expr.AsNot()
		input, err := f.assignPlaceholders(notExpr.Input())
		if err != nil {
			return 0, err
		}
		return f.ConstructNot(input), nil
	case opt.PlaceholderOp:
		value := expr.AsPlaceholder().Value()
		placeholder := f.mem.LookupPrivate(value).(*tree.Placeholder)
		d, err := placeholder.Eval(f.evalCtx)
		if err != nil {
			return 0, err
		}
		return f.ConstructConstVal(d), nil
	}
	panic("unhandled operator")
}

type dynConstructFunc func(f *Factory, operands memo.DynamicOperands) memo.GroupID

var dynConstructLookup [opt.NumOperators]dynConstructFunc

func init() {
	// UnknownOp
	dynConstructLookup[opt.UnknownOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		panic("op type not initialized")
	}

	// LtOp
	dynConstructLookup[opt.LtOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		return f.ConstructLt(memo.GroupID(operands[0]), memo.GroupID(operands[1]))
	}

	// GtOp
	dynConstructLookup[opt.GtOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		return f.ConstructGt(memo.GroupID(operands[0]), memo.GroupID(operands[1]))
	}

	// ContainsOp
	dynConstructLookup[opt.ContainsOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		return f.ConstructContains(memo.GroupID(operands[0]), memo.GroupID(operands[1]))
	}

	// NotOp
	dynConstructLookup[opt.NotOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		return f.ConstructNot(memo.GroupID(operands[0]))
	}

}

func (f *Factory) DynamicConstruct(op opt.Operator, operands memo.DynamicOperands) memo.GroupID {
	return dynConstructLookup[op](f, operands)
}
----
----

#
# Test rule priorities.
#
optgen factory test.opt

define Project {
    Input Expr
}

[EliminateProject5, Normalize, LowPriority]
(Project $input:*) => $input

[EliminateProject3, Normalize]
(Project $input:*) => $input

[EliminateProject1, Normalize, HighPriority]
(Project $input:*) => $input

[EliminateProject6, Normalize, LowPriority]
(Project $input:*) => $input

[EliminateProject2, Normalize, HighPriority]
(Project $input:*) => $input

[EliminateProject4, Normalize]
(Project $input:*) => $input
----
----
// Code generated by optgen; [omitted]

package norm

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

// ConstructProject constructs an expression for the Project operator.
func (_f *Factory) ConstructProject(
	input memo.GroupID,
) memo.GroupID {
	_projectExpr := memo.MakeProjectExpr(input)
	_group := _f.mem.GroupByFingerprint(_projectExpr.Fingerprint())
	if _group != 0 {
		return _group
	}

	// [EliminateProject1]
	{
		if _f.matchedRule == nil || _f.matchedRule(opt.EliminateProject1) {
			_group = input
			_f.mem.AddAltFingerprint(_projectExpr.Fingerprint(), _group)
			if _f.appliedRule != nil {
				_f.appliedRule(opt.EliminateProject1, _group, 0, 0)
			}
			return _group
		}
	}

	// [EliminateProject2]
	{
		if _f.matchedRule == nil || _f.matchedRule(opt.EliminateProject2) {
			_group = input
			_f.mem.AddAltFingerprint(_projectExpr.Fingerprint(), _group)
			if _f.appliedRule != nil {
				_f.appliedRule(opt.EliminateProject2, _group, 0, 0)
			}
			return _group
		}
	}

	// [EliminateProject3]
	{
		if _f.matchedRule == nil || _f.matchedRule(opt.EliminateProject3) {
			_group = input
			_f.mem.AddAltFingerprint(_projectExpr.Fingerprint(), _group)
			if _f.appliedRule != nil {
				_f.appliedRule(opt.EliminateProject3, _group, 0, 0)
			}
			return _group
		}
	}

	// [EliminateProject4]
	{
		if _f.matchedRule == nil || _f.matchedRule(opt.EliminateProject4) {
			_group = input
			_f.mem.AddAltFingerprint(_projectExpr.Fingerprint(), _group)
			if _f.appliedRule != nil {
				_f.appliedRule(opt.EliminateProject4, _group, 0, 0)
			}
			return _group
		}
	}

	// [EliminateProject5]
	{
		if _f.matchedRule == nil || _f.matchedRule(opt.EliminateProject5) {
			_group = input
			_f.mem.AddAltFingerprint(_projectExpr.Fingerprint(), _group)
			if _f.appliedRule != nil {
				_f.appliedRule(opt.EliminateProject5, _group, 0, 0)
			}
			return _group
		}
	}

	// [EliminateProject6]
	{
		if _f.matchedRule == nil || _f.matchedRule(opt.EliminateProject6) {
			_group = input
			_f.mem.AddAltFingerprint(_projectExpr.Fingerprint(), _group)
			if _f.appliedRule != nil {
				_f.appliedRule(opt.EliminateProject6, _group, 0, 0)
			}
			return _group
		}
	}

	return _f.onConstruct(memo.Expr(_projectExpr))
}

func (f *Factory) assignPlaceholders(group memo.GroupID) (memo.GroupID, error) {
	if !f.mem.GroupProperties(group).HasPlaceholder() {
		return group, nil
	}
	expr := f.mem.NormExpr(group)
	switch expr.Operator() {
	case opt.ProjectOp:
		projectExpr := expr.AsProject()
		input, err := f.assignPlaceholders(projectExpr.Input())
		if err != nil {
			return 0, err
		}
		return f.ConstructProject(input), nil
	case opt.PlaceholderOp:
		value := expr.AsPlaceholder().Value()
		placeholder := f.mem.LookupPrivate(value).(*tree.Placeholder)
		d, err := placeholder.Eval(f.evalCtx)
		if err != nil {
			return 0, err
		}
		return f.ConstructConstVal(d), nil
	}
	panic("unhandled operator")
}

type dynConstructFunc func(f *Factory, operands memo.DynamicOperands) memo.GroupID

var dynConstructLookup [opt.NumOperators]dynConstructFunc

func init() {
	// UnknownOp
	dynConstructLookup[opt.UnknownOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		panic("op type not initialized")
	}

	// ProjectOp
	dynConstructLookup[opt.ProjectOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		return f.ConstructProject(memo.GroupID(operands[0]))
	}

}

func (f *Factory) DynamicConstruct(op opt.Operator, operands memo.DynamicOperands) memo.GroupID {
	return dynConstructLookup[op](f, operands)
}
----
----

#
# Test binding of custom function arguments and in replace pattern.
#
optgen factory test.opt

define InnerJoin {
    Left  Expr
    Right Expr
    On    Expr
}

[Binding, Normalize]
(InnerJoin
    $left:* & (CustomFunc $customArg:(CustomFunc))
    $right:*
    $on:*
)
=>
(InnerJoin
    $outerFunc:(OuterFunc $innerFunc:(InnerFunc $left))
    $rightFunc:(Func $right)
    (MakeOn $customArg $outerFunc $innerFunc $rightFunc)
)
----
----
// Code generated by optgen; [omitted]

package norm

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

// ConstructInnerJoin constructs an expression for the InnerJoin operator.
func (_f *Factory) ConstructInnerJoin(
	left memo.GroupID,
	right memo.GroupID,
	on memo.GroupID,
) memo.GroupID {
	_innerJoinExpr := memo.MakeInnerJoinExpr(left, right, on)
	_group := _f.mem.GroupByFingerprint(_innerJoinExpr.Fingerprint())
	if _group != 0 {
		return _group
	}

	// [Binding]
	{
		customArg := _f.funcs.CustomFunc()
		if _f.funcs.CustomFunc(customArg) {
			if _f.matchedRule == nil || _f.matchedRule(opt.Binding) {
				innerFunc := _f.funcs.InnerFunc(left)
				outerFunc := _f.funcs.OuterFunc(innerFunc)
				rightFunc := _f.funcs.Func(right)
				_group = _f.ConstructInnerJoin(
					outerFunc,
					rightFunc,
					_f.funcs.MakeOn(customArg, outerFunc, innerFunc, rightFunc),
				)
				_f.mem.AddAltFingerprint(_innerJoinExpr.Fingerprint(), _group)
				if _f.appliedRule != nil {
					_f.appliedRule(opt.Binding, _group, 0, 0)
				}
				return _group
			}
		}
	}

	return _f.onConstruct(memo.Expr(_innerJoinExpr))
}

func (f *Factory) assignPlaceholders(group memo.GroupID) (memo.GroupID, error) {
	if !f.mem.GroupProperties(group).HasPlaceholder() {
		return group, nil
	}
	expr := f.mem.NormExpr(group)
	switch expr.Operator() {
	case opt.InnerJoinOp:
		innerJoinExpr := expr.AsInnerJoin()
		left, err := f.assignPlaceholders(innerJoinExpr.Left())
		if err != nil {
			return 0, err
		}
		right, err := f.assignPlaceholders(innerJoinExpr.Right())
		if err != nil {
			return 0, err
		}
		on, err := f.assignPlaceholders(innerJoinExpr.On())
		if err != nil {
			return 0, err
		}
		return f.ConstructInnerJoin(left, right, on), nil
	case opt.PlaceholderOp:
		value := expr.AsPlaceholder().Value()
		placeholder := f.mem.LookupPrivate(value).(*tree.Placeholder)
		d, err := placeholder.Eval(f.evalCtx)
		if err != nil {
			return 0, err
		}
		return f.ConstructConstVal(d), nil
	}
	panic("unhandled operator")
}

type dynConstructFunc func(f *Factory, operands memo.DynamicOperands) memo.GroupID

var dynConstructLookup [opt.NumOperators]dynConstructFunc

func init() {
	// UnknownOp
	dynConstructLookup[opt.UnknownOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		panic("op type not initialized")
	}

	// InnerJoinOp
	dynConstructLookup[opt.InnerJoinOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		return f.ConstructInnerJoin(memo.GroupID(operands[0]), memo.GroupID(operands[1]), memo.GroupID(operands[2]))
	}

}

func (f *Factory) DynamicConstruct(op opt.Operator, operands memo.DynamicOperands) memo.GroupID {
	return dynConstructLookup[op](f, operands)
}
----
----

#
# Generate numeric match and replace.
#
optgen factory test.opt
define Limit {
    Input Expr
    Limit Expr
}

define Scan {
}

[LimitOne, Normalize]
(Limit $input:(Scan) 1)
=>
(LimitScan $input)

[LimitNotOne, Normalize]
(Limit $input:(Scan) ^1)
=>
(LimitScan $input)
----
----
// Code generated by optgen; [omitted]

package norm

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

// ConstructLimit constructs an expression for the Limit operator.
func (_f *Factory) ConstructLimit(
	input memo.GroupID,
	limit memo.GroupID,
) memo.GroupID {
	_limitExpr := memo.MakeLimitExpr(input, limit)
	_group := _f.mem.GroupByFingerprint(_limitExpr.Fingerprint())
	if _group != 0 {
		return _group
	}

	// [LimitOne]
	{
		_scanExpr := _f.mem.NormExpr(input).AsScan()
		if _scanExpr != nil {
			if _f.funcs.EqualsNumber(limit, 1) {
				if _f.matchedRule == nil || _f.matchedRule(opt.LimitOne) {
					_group = _f.funcs.LimitScan(input)
					_f.mem.AddAltFingerprint(_limitExpr.Fingerprint(), _group)
					if _f.appliedRule != nil {
						_f.appliedRule(opt.LimitOne, _group, 0, 0)
					}
					return _group
				}
			}
		}
	}

	// [LimitNotOne]
	{
		_scanExpr := _f.mem.NormExpr(input).AsScan()
		if _scanExpr != nil {
			if !_f.funcs.EqualsNumber(limit, 1) {
				if _f.matchedRule == nil || _f.matchedRule(opt.LimitNotOne) {
					_group = _f.funcs.LimitScan(input)
					_f.mem.AddAltFingerprint(_limitExpr.Fingerprint(), _group)
					if _f.appliedRule != nil {
						_f.appliedRule(opt.LimitNotOne, _group, 0, 0)
					}
					return _group
				}
			}
		}
	}

	return _f.onConstruct(memo.Expr(_limitExpr))
}

// ConstructScan constructs an expression for the Scan operator.
func (_f *Factory) ConstructScan() memo.GroupID {
	_scanExpr := memo.MakeScanExpr()
	_group := _f.mem.GroupByFingerprint(_scanExpr.Fingerprint())
	if _group != 0 {
		return _group
	}

	return _f.onConstruct(memo.Expr(_scanExpr))
}

func (f *Factory) assignPlaceholders(group memo.GroupID) (memo.GroupID, error) {
	if !f.mem.GroupProperties(group).HasPlaceholder() {
		return group, nil
	}
	expr := f.mem.NormExpr(group)
	switch expr.Operator() {
	case opt.LimitOp:
		limitExpr := expr.AsLimit()
		input, err := f.assignPlaceholders(limitExpr.Input())
		if err != nil {
			return 0, err
		}
		limit, err := f.assignPlaceholders(limitExpr.Limit())
		if err != nil {
			return 0, err
		}
		return f.ConstructLimit(input, limit), nil
	case opt.PlaceholderOp:
		value := expr.AsPlaceholder().Value()
		placeholder := f.mem.LookupPrivate(value).(*tree.Placeholder)
		d, err := placeholder.Eval(f.evalCtx)
		if err != nil {
			return 0, err
		}
		return f.ConstructConstVal(d), nil
	}
	panic("unhandled operator")
}

type dynConstructFunc func(f *Factory, operands memo.DynamicOperands) memo.GroupID

var dynConstructLookup [opt.NumOperators]dynConstructFunc

func init() {
	// UnknownOp
	dynConstructLookup[opt.UnknownOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		panic("op type not initialized")
	}

	// LimitOp
	dynConstructLookup[opt.LimitOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		return f.ConstructLimit(memo.GroupID(operands[0]), memo.GroupID(operands[1]))
	}

	// ScanOp
	dynConstructLookup[opt.ScanOp] = func(f *Factory, operands memo.DynamicOperands) memo.GroupID {
		return f.ConstructScan()
	}

}

func (f *Factory) DynamicConstruct(op opt.Operator, operands memo.DynamicOperands) memo.GroupID {
	return dynConstructLookup[op](f, operands)
}
----
----
