github.com/dolthub/go-mysql-server@v0.18.0/sql/memo/memo.go (about)

     1  // Copyright 2022 Dolthub, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package memo
    16  
    17  import (
    18  	"fmt"
    19  	"strings"
    20  
    21  	"github.com/dolthub/go-mysql-server/sql"
    22  	"github.com/dolthub/go-mysql-server/sql/expression"
    23  	"github.com/dolthub/go-mysql-server/sql/plan"
    24  	"github.com/dolthub/go-mysql-server/sql/transform"
    25  )
    26  
    27  type GroupId uint16
    28  type TableId uint16
    29  
    30  type TableAndColumn struct {
    31  	tableName  string
    32  	columnName string
    33  }
    34  
    35  // Memo collects a forest of query plans structured by logical and
    36  // physical equivalency. Logically equivalent plans, represented by
    37  // an exprGroup, produce the same rows (possibly unordered) and schema.
    38  // Physical plans are stored in a linked list within an expression group.
    39  type Memo struct {
    40  	cnt  uint16
    41  	root *ExprGroup
    42  
    43  	hints *joinHints
    44  
    45  	c         Coster
    46  	statsProv sql.StatsProvider
    47  	Ctx       *sql.Context
    48  	scope     *plan.Scope
    49  	scopeLen  int
    50  
    51  	TableProps *tableProps
    52  }
    53  
    54  func NewMemo(ctx *sql.Context, stats sql.StatsProvider, s *plan.Scope, scopeLen int, cost Coster) *Memo {
    55  	return &Memo{
    56  		Ctx:        ctx,
    57  		c:          cost,
    58  		statsProv:  stats,
    59  		scope:      s,
    60  		scopeLen:   scopeLen,
    61  		TableProps: newTableProps(),
    62  		hints:      &joinHints{},
    63  	}
    64  }
    65  
    66  type MemoErr struct {
    67  	Err error
    68  }
    69  
    70  func (m *Memo) HandleErr(err error) {
    71  	panic(MemoErr{Err: err})
    72  }
    73  
    74  func (m *Memo) Root() *ExprGroup {
    75  	return m.root
    76  }
    77  
    78  func (m *Memo) StatsProvider() sql.StatsProvider {
    79  	return m.statsProv
    80  }
    81  
    82  // newExprGroup creates a new logical expression group to encapsulate the
    83  // action of a SQL clause.
    84  // TODO: this is supposed to deduplicate logically equivalent table scans
    85  // and scalar expressions, replacing references with a pointer. Currently
    86  // a hacky format to quickly support memoizing join trees.
    87  func (m *Memo) NewExprGroup(rel exprType) *ExprGroup {
    88  	m.cnt++
    89  	id := GroupId(m.cnt)
    90  	grp := newExprGroup(m, id, rel)
    91  
    92  	if s, ok := rel.(SourceRel); ok {
    93  		m.TableProps.addTable(s.Name(), id)
    94  	}
    95  	return grp
    96  }
    97  
    98  func (m *Memo) memoizeSourceRel(rel SourceRel) *ExprGroup {
    99  	grp := m.NewExprGroup(rel)
   100  	return grp
   101  }
   102  
   103  func (m *Memo) getTableId(table string) (GroupId, bool) {
   104  	return m.TableProps.GetId(table)
   105  }
   106  
   107  func (m *Memo) MemoizeLeftJoin(grp, left, right *ExprGroup, op plan.JoinType, filter []sql.Expression) *ExprGroup {
   108  	newJoin := &LeftJoin{
   109  		JoinBase: &JoinBase{
   110  			relBase: &relBase{},
   111  			Left:    left,
   112  			Right:   right,
   113  			Op:      op,
   114  			Filter:  filter,
   115  		},
   116  	}
   117  	// todo intern relExprs? add to appropriate group?
   118  	if grp == nil {
   119  		return m.NewExprGroup(newJoin)
   120  	}
   121  	newJoin.g = grp
   122  	grp.Prepend(newJoin)
   123  	return grp
   124  }
   125  
   126  func (m *Memo) MemoizeInnerJoin(grp, left, right *ExprGroup, op plan.JoinType, filter []sql.Expression) *ExprGroup {
   127  	newJoin := &InnerJoin{
   128  		JoinBase: &JoinBase{
   129  			relBase: &relBase{},
   130  			Left:    left,
   131  			Right:   right,
   132  			Op:      op,
   133  			Filter:  filter,
   134  		},
   135  	}
   136  	// todo intern relExprs? add to appropriate group?
   137  	if grp == nil {
   138  		return m.NewExprGroup(newJoin)
   139  	}
   140  	newJoin.g = grp
   141  	grp.Prepend(newJoin)
   142  	return grp
   143  }
   144  
   145  func (m *Memo) MemoizeLookupJoin(grp, left, right *ExprGroup, op plan.JoinType, filter []sql.Expression, lookup *IndexScan) *ExprGroup {
   146  	if right.RelProps.reqIdxCols.Difference(lookup.Index.set).Len() > 0 {
   147  		// the index lookup does not cover the requested RHS indexScan columns,
   148  		// so this physical plan is invalid.
   149  		return grp
   150  	}
   151  	newJoin := &LookupJoin{
   152  		JoinBase: &JoinBase{
   153  			relBase: &relBase{},
   154  			Left:    left,
   155  			Right:   right,
   156  			Op:      op.AsLookup(),
   157  			Filter:  filter,
   158  		},
   159  		Lookup: lookup,
   160  	}
   161  
   162  	if grp == nil {
   163  		return m.NewExprGroup(newJoin)
   164  	}
   165  	newJoin.g = grp
   166  	grp.Prepend(newJoin)
   167  
   168  	if isInjectiveLookup(lookup.Index, newJoin.JoinBase, lookup.Table.Expressions(), lookup.Table.NullMask()) {
   169  		newJoin.Injective = true
   170  	}
   171  
   172  	return grp
   173  }
   174  
   175  func (m *Memo) MemoizeHashJoin(grp *ExprGroup, join *JoinBase, toExpr, fromExpr []sql.Expression) *ExprGroup {
   176  	if join.Right.RelProps.reqIdxCols.Len() > 0 {
   177  		// HASH_JOIN's RHS will be a table scan, so this physical
   178  		// plan will not provide the requested indexScan
   179  		return grp
   180  	}
   181  	newJoin := &HashJoin{
   182  		JoinBase:   join.Copy(),
   183  		LeftAttrs:  toExpr,
   184  		RightAttrs: fromExpr,
   185  	}
   186  	newJoin.Op = newJoin.Op.AsHash()
   187  
   188  	if grp == nil {
   189  		return m.NewExprGroup(newJoin)
   190  	}
   191  	newJoin.g = grp
   192  	grp.Prepend(newJoin)
   193  
   194  	return grp
   195  }
   196  
   197  // MemoizeConcatLookupJoin creates a lookup join over a set of disjunctions.
   198  // If a LOOKUP_JOIN simulates x = v1, a concat lookup performs x in (v1, v2, v3, ...)
   199  func (m *Memo) MemoizeConcatLookupJoin(grp, left, right *ExprGroup, op plan.JoinType, filter []sql.Expression, lookups []*IndexScan) *ExprGroup {
   200  	newJoin := &ConcatJoin{
   201  		JoinBase: &JoinBase{
   202  			relBase: &relBase{},
   203  			Left:    left,
   204  			Right:   right,
   205  			Op:      op.AsLookup(),
   206  			Filter:  filter,
   207  		},
   208  		Concat: lookups,
   209  	}
   210  
   211  	if grp == nil {
   212  		return m.NewExprGroup(newJoin)
   213  	}
   214  	newJoin.g = grp
   215  	grp.Prepend(newJoin)
   216  	return grp
   217  }
   218  
   219  func (m *Memo) MemoizeRangeHeapJoin(grp, left, right *ExprGroup, op plan.JoinType, filter []sql.Expression, rangeHeap *RangeHeap) *ExprGroup {
   220  	newJoin := &RangeHeapJoin{
   221  		JoinBase: &JoinBase{
   222  			relBase: &relBase{},
   223  			Left:    left,
   224  			Right:   right,
   225  			Op:      op,
   226  			Filter:  filter,
   227  		},
   228  		RangeHeap: rangeHeap,
   229  	}
   230  	newJoin.RangeHeap.Parent = newJoin.JoinBase
   231  
   232  	if grp == nil {
   233  		return m.NewExprGroup(newJoin)
   234  	}
   235  	newJoin.g = grp
   236  	grp.Prepend(newJoin)
   237  	return grp
   238  }
   239  
   240  func (m *Memo) MemoizeMergeJoin(grp, left, right *ExprGroup, lIdx, rIdx *IndexScan, op plan.JoinType, filter []sql.Expression, swapCmp bool) *ExprGroup {
   241  	rel := &MergeJoin{
   242  		JoinBase: &JoinBase{
   243  			relBase: &relBase{},
   244  			Op:      op,
   245  			Filter:  filter,
   246  			Left:    left,
   247  			Right:   right,
   248  		},
   249  		InnerScan: lIdx,
   250  		OuterScan: rIdx,
   251  		SwapCmp:   swapCmp,
   252  	}
   253  
   254  	comparer, ok := filter[0].(*expression.Equals)
   255  	if !ok {
   256  		err := sql.ErrMergeJoinExpectsComparerFilters.New(filter[0])
   257  		m.HandleErr(err)
   258  	}
   259  
   260  	var leftCompareExprs []sql.Expression
   261  	var rightCompareExprs []sql.Expression
   262  
   263  	leftTuple, isTuple := comparer.Left().(expression.Tuple)
   264  	if isTuple {
   265  		rightTuple, _ := comparer.Right().(expression.Tuple)
   266  		leftCompareExprs = leftTuple.Children()
   267  		rightCompareExprs = rightTuple.Children()
   268  	} else {
   269  		leftCompareExprs = []sql.Expression{comparer.Left()}
   270  		rightCompareExprs = []sql.Expression{comparer.Right()}
   271  	}
   272  
   273  	if grp == nil {
   274  		grp = m.NewExprGroup(rel)
   275  		rel.Injective = isInjectiveMerge(rel, leftCompareExprs, rightCompareExprs)
   276  		return grp
   277  	}
   278  	rel.g = grp
   279  	rel.Injective = isInjectiveMerge(rel, leftCompareExprs, rightCompareExprs)
   280  	rel.CmpCnt = len(leftCompareExprs)
   281  	grp.Prepend(rel)
   282  	return grp
   283  }
   284  
   285  func (m *Memo) MemoizeProject(grp, child *ExprGroup, projections []sql.Expression) *ExprGroup {
   286  	rel := &Project{
   287  		relBase:     &relBase{},
   288  		Child:       child,
   289  		Projections: projections,
   290  	}
   291  	if grp == nil {
   292  		return m.NewExprGroup(rel)
   293  	}
   294  	rel.g = grp
   295  	grp.Prepend(rel)
   296  	return grp
   297  }
   298  
   299  // MemoizeIndexScan creates a source node that uses a specific index to
   300  // access data. IndexScans are either static and read a specific set of
   301  // ranges, or dynamic and use a lookup template that is iteratively
   302  // bound and executed during LOOKUP_JOINs.
   303  func (m *Memo) MemoizeIndexScan(grp *ExprGroup, ita *plan.IndexedTableAccess, alias string, index *Index, stat sql.Statistic) *ExprGroup {
   304  	rel := &IndexScan{
   305  		sourceBase: &sourceBase{relBase: &relBase{}},
   306  		Table:      ita,
   307  		Alias:      alias,
   308  		Index:      index,
   309  		Stats:      stat,
   310  	}
   311  	if grp == nil {
   312  		return m.NewExprGroup(rel)
   313  	}
   314  	rel.g = grp
   315  	grp.Prepend(rel)
   316  	return grp
   317  }
   318  
   319  func (m *Memo) MemoizeFilter(grp, child *ExprGroup, filters []sql.Expression) *ExprGroup {
   320  	rel := &Filter{
   321  		relBase: &relBase{},
   322  		Child:   child,
   323  		Filters: filters,
   324  	}
   325  	if grp == nil {
   326  		return m.NewExprGroup(rel)
   327  	}
   328  	rel.g = grp
   329  	grp.Prepend(rel)
   330  	return grp
   331  }
   332  
   333  func (m *Memo) MemoizeMax1Row(grp, child *ExprGroup) *ExprGroup {
   334  	rel := &Max1Row{
   335  		relBase: &relBase{},
   336  		Child:   child,
   337  	}
   338  	if grp == nil {
   339  		return m.NewExprGroup(rel)
   340  	}
   341  	rel.g = grp
   342  	grp.Prepend(rel)
   343  	return grp
   344  }
   345  
   346  // OptimizeRoot finds the implementation for the root expression
   347  // that has the lowest cost.
   348  func (m *Memo) OptimizeRoot() error {
   349  	err := m.optimizeMemoGroup(m.root)
   350  	if err != nil {
   351  		return err
   352  	}
   353  
   354  	// Certain "best" groups are incompatible.
   355  	m.root.fixConflicts()
   356  	return nil
   357  }
   358  
   359  // optimizeMemoGroup recursively builds the lowest cost plan for memo
   360  // group expressions. We optimize expressions groups independently, walking
   361  // the linked list of execution plans for a particular group only after
   362  // optimizing all subgroups. All plans within a group by definition share
   363  // the same subgroup dependencies. After finding the best implementation
   364  // for a particular group, we fix the best plan for that group and recurse
   365  // into its parents.
   366  // TODO: we should not have to cost every plan, sometimes there is a provably
   367  // best case implementation
   368  func (m *Memo) optimizeMemoGroup(grp *ExprGroup) error {
   369  	if grp.Done {
   370  		return nil
   371  	}
   372  
   373  	var err error
   374  	n := grp.First
   375  	if _, ok := n.(SourceRel); ok {
   376  		// We should order the search bottom-up so that physical operators
   377  		// always have their trees materialized. Until then, we always assume
   378  		// the indexScan child is faster than a filter option, and  correct
   379  		//  when a chosen join operator is incompatible with the indexScan
   380  		//  option.
   381  		grp.Done = true
   382  		grp.HintOk = true
   383  		grp.Best = grp.First
   384  		grp.Best.SetDistinct(NoDistinctOp)
   385  		return nil
   386  	}
   387  
   388  	for n != nil {
   389  		var cost float64
   390  		for _, g := range n.Children() {
   391  			err = m.optimizeMemoGroup(g)
   392  			if err != nil {
   393  				return err
   394  			}
   395  			cost += g.Cost
   396  		}
   397  		relCost, err := m.c.EstimateCost(m.Ctx, n, m.statsProv)
   398  		if err != nil {
   399  			return err
   400  		}
   401  
   402  		if grp.RelProps.Distinct.IsHash() {
   403  			var dCost float64
   404  			if sortedInputs(n) {
   405  				n.SetDistinct(SortedDistinctOp)
   406  			} else {
   407  				n.SetDistinct(HashDistinctOp)
   408  				d := &Distinct{Child: grp}
   409  				dCost = float64(statsForRel(d).RowCount())
   410  			}
   411  			relCost += dCost
   412  		} else {
   413  			n.SetDistinct(NoDistinctOp)
   414  		}
   415  
   416  		n.SetCost(relCost)
   417  		cost += relCost
   418  		m.updateBest(grp, n, cost)
   419  		n = n.Next()
   420  	}
   421  
   422  	grp.Done = true
   423  	if err != nil {
   424  		return err
   425  	}
   426  	return nil
   427  }
   428  
   429  // updateBest chooses the best hinted plan or the best overall plan if the
   430  // hint corresponds to  no valid plan. Ordering is applied as a global
   431  // rather than a local property.
   432  func (m *Memo) updateBest(grp *ExprGroup, n RelExpr, cost float64) {
   433  	if !m.hints.isEmpty() {
   434  		if m.hints.satisfiedBy(n) {
   435  			if !grp.HintOk {
   436  				grp.Best = n
   437  				grp.Cost = cost
   438  				grp.HintOk = true
   439  				return
   440  			}
   441  			grp.updateBest(n, cost)
   442  		} else if grp.Best == nil || !grp.HintOk {
   443  			grp.updateBest(n, cost)
   444  		}
   445  		return
   446  	}
   447  	grp.updateBest(n, cost)
   448  }
   449  
   450  func (m *Memo) BestRootPlan(ctx *sql.Context) (sql.Node, error) {
   451  	b := NewExecBuilder()
   452  	return buildBestJoinPlan(b, m.root, nil)
   453  }
   454  
   455  // buildBestJoinPlan converts group's lowest cost implementation into a
   456  // tree node with a recursive DFS.
   457  func buildBestJoinPlan(b *ExecBuilder, grp *ExprGroup, input sql.Schema) (sql.Node, error) {
   458  	if !grp.Done {
   459  		return nil, fmt.Errorf("expected expression group plans to be fixed")
   460  	}
   461  	n := grp.Best
   462  	var err error
   463  	children := make([]sql.Node, len(n.Children()))
   464  	for i, g := range n.Children() {
   465  		children[i], err = buildBestJoinPlan(b, g, input)
   466  		if err != nil {
   467  			return nil, err
   468  		}
   469  	}
   470  	return b.buildRel(n, children...)
   471  }
   472  
   473  func getProjectColset(p *Project) sql.ColSet {
   474  	var colset sql.ColSet
   475  	for _, e := range p.Projections {
   476  		transform.InspectExpr(e, func(e sql.Expression) bool {
   477  			if gf, ok := e.(*expression.GetField); ok && gf.Id() > 0 {
   478  				colset.Add(gf.Id())
   479  			}
   480  			return false
   481  		})
   482  	}
   483  	return colset
   484  }
   485  
   486  func (m *Memo) ApplyHint(hint Hint) {
   487  	switch hint.Typ {
   488  	case HintTypeJoinOrder:
   489  		m.WithJoinOrder(hint.Args)
   490  	case HintTypeJoinFixedOrder:
   491  	case HintTypeInnerJoin, HintTypeMergeJoin, HintTypeLookupJoin, HintTypeHashJoin, HintTypeSemiJoin, HintTypeAntiJoin, HintTypeLeftOuterLookupJoin:
   492  		m.WithJoinOp(hint.Typ, hint.Args[0], hint.Args[1])
   493  	case HintTypeLeftDeep:
   494  		m.hints.leftDeep = true
   495  	default:
   496  	}
   497  }
   498  
   499  func (m *Memo) WithJoinOrder(tables []string) {
   500  	// order maps groupId -> table dependencies
   501  	order := make(map[sql.TableId]uint64)
   502  	for i, t := range tables {
   503  		for _, n := range m.root.RelProps.TableIdNodes() {
   504  			if strings.EqualFold(t, n.Name()) {
   505  				order[n.Id()] = uint64(i)
   506  				break
   507  			}
   508  		}
   509  	}
   510  	hint := newJoinOrderHint(order)
   511  	hint.build(m.root)
   512  	if hint.isValid() {
   513  		m.hints.order = hint
   514  	}
   515  }
   516  
   517  func (m *Memo) WithJoinOp(op HintType, left, right string) {
   518  	var lTab, rTab sql.TableId
   519  	for _, n := range m.root.RelProps.TableIdNodes() {
   520  		if strings.EqualFold(left, n.Name()) {
   521  			lTab = n.Id()
   522  		}
   523  		if strings.EqualFold(right, n.Name()) {
   524  			rTab = n.Id()
   525  		}
   526  	}
   527  	if lTab == 0 || rTab == 0 {
   528  		return
   529  	}
   530  	hint := newjoinOpHint(op, lTab, rTab)
   531  	if !hint.isValid() {
   532  		return
   533  	}
   534  	m.hints.ops = append(m.hints.ops, hint)
   535  }
   536  
   537  func (m *Memo) String() string {
   538  	exprs := make([]string, m.cnt)
   539  	groups := make([]*ExprGroup, 0)
   540  	if m.root != nil {
   541  		r := m.root.First
   542  		for r != nil {
   543  			groups = append(groups, r.Group())
   544  			groups = append(groups, r.Children()...)
   545  			r = r.Next()
   546  		}
   547  	}
   548  	for len(groups) > 0 {
   549  		newGroups := make([]*ExprGroup, 0)
   550  		for _, g := range groups {
   551  			if exprs[int(TableIdForSource(g.Id))] != "" {
   552  				continue
   553  			}
   554  			exprs[int(TableIdForSource(g.Id))] = g.String()
   555  			newGroups = append(newGroups, g.children()...)
   556  		}
   557  		groups = newGroups
   558  	}
   559  	b := strings.Builder{}
   560  	b.WriteString("memo:\n")
   561  	beg := "├──"
   562  	for i, g := range exprs {
   563  		if i == len(exprs)-1 {
   564  			beg = "└──"
   565  		}
   566  		b.WriteString(fmt.Sprintf("%s G%d: %s\n", beg, i+1, g))
   567  	}
   568  	return b.String()
   569  }
   570  
   571  type tableProps struct {
   572  	grpToName map[GroupId]string
   573  	nameToGrp map[string]GroupId
   574  }
   575  
   576  func newTableProps() *tableProps {
   577  	return &tableProps{
   578  		grpToName: make(map[GroupId]string),
   579  		nameToGrp: make(map[string]GroupId),
   580  	}
   581  }
   582  
   583  func (p *tableProps) addTable(n string, id GroupId) {
   584  	p.grpToName[id] = n
   585  	p.nameToGrp[n] = id
   586  }
   587  
   588  func (p *tableProps) GetTable(id GroupId) (string, bool) {
   589  	n, ok := p.grpToName[id]
   590  	return n, ok
   591  }
   592  
   593  func (p *tableProps) GetId(n string) (GroupId, bool) {
   594  	id, ok := p.nameToGrp[strings.ToLower(n)]
   595  	return id, ok
   596  }
   597  
   598  // Coster types can estimate the CPU and memory cost of physical execution
   599  // operators.
   600  type Coster interface {
   601  	// EstimateCost cost returns the incremental CPU and memory cost for an
   602  	// operator, or an error. Cost is dependent on physical operator type,
   603  	// and the cardinality of inputs.
   604  	EstimateCost(*sql.Context, RelExpr, sql.StatsProvider) (float64, error)
   605  }
   606  
   607  // RelExpr wraps a sql.Node for use as a ExprGroup linked list node.
   608  // TODO: we need relExprs for every sql.Node and sql.Expression
   609  type RelExpr interface {
   610  	fmt.Stringer
   611  	exprType
   612  	Next() RelExpr
   613  	SetNext(RelExpr)
   614  	SetCost(c float64)
   615  	Cost() float64
   616  	Distinct() distinctOp
   617  	SetDistinct(distinctOp)
   618  }
   619  
   620  type relBase struct {
   621  	// g is this relation's expression group
   622  	g *ExprGroup
   623  	// n is the next RelExpr in the ExprGroup linked list
   624  	n RelExpr
   625  	// c is this relation's cost while costing and plan reify are separate
   626  	c float64
   627  	// d indicates a RelExpr should be checked for distinctness
   628  	d distinctOp
   629  }
   630  
   631  // relKey is a quick identifier for avoiding duplicate work on the same
   632  // RelExpr.
   633  // TODO: the key should be a formalized hash of 1) the operator type, and 2)
   634  // hashes of the RelExpr and ScalarExpr children.
   635  func relKey(r RelExpr) uint64 {
   636  	key := int(r.Group().Id)
   637  	i := 1<<16 - 1
   638  	for _, c := range r.Children() {
   639  		key += i * int(c.Id)
   640  		i *= 1<<16 - 1
   641  	}
   642  	return uint64(key)
   643  }
   644  
   645  type distinctOp uint8
   646  
   647  const (
   648  	unknownDistinctOp distinctOp = iota
   649  	NoDistinctOp
   650  	SortedDistinctOp
   651  	HashDistinctOp
   652  )
   653  
   654  func (d distinctOp) IsHash() bool {
   655  	return d == HashDistinctOp
   656  }
   657  
   658  func (r *relBase) Distinct() distinctOp {
   659  	return r.d
   660  }
   661  
   662  func (r *relBase) SetDistinct(d distinctOp) {
   663  	r.d = d
   664  }
   665  
   666  func (r *relBase) Group() *ExprGroup {
   667  	return r.g
   668  }
   669  
   670  func (r *relBase) SetGroup(g *ExprGroup) {
   671  	r.g = g
   672  }
   673  
   674  func (r *relBase) Next() RelExpr {
   675  	return r.n
   676  }
   677  
   678  func (r *relBase) SetNext(rel RelExpr) {
   679  	r.n = rel
   680  }
   681  
   682  func (r *relBase) SetCost(c float64) {
   683  	r.c = c
   684  }
   685  
   686  func (r *relBase) Cost() float64 {
   687  	return r.c
   688  }
   689  
   690  func DescribeStats(r RelExpr) *sql.DescribeStats {
   691  	return &sql.DescribeStats{
   692  		EstimatedRowCount: r.Group().RelProps.GetStats().RowCount(),
   693  		Cost:              r.Cost(),
   694  	}
   695  }
   696  
   697  func TableIdForSource(id GroupId) sql.TableId {
   698  	return sql.TableId(id - 1)
   699  }
   700  
   701  type exprType interface {
   702  	Group() *ExprGroup
   703  	Children() []*ExprGroup
   704  	SetGroup(g *ExprGroup)
   705  }
   706  
   707  // SourceRel represents a data source, like a tableScan, subqueryAlias,
   708  // or list of values.
   709  type SourceRel interface {
   710  	RelExpr
   711  	// outputCols retuns the output schema of this data source.
   712  	// TODO: this is more useful as a relExpr property, but we need
   713  	// this to fix up expression indexes currently
   714  	OutputCols() sql.Schema
   715  	Name() string
   716  	TableId() sql.TableId
   717  	Indexes() []*Index
   718  	SetIndexes(indexes []*Index)
   719  	TableIdNode() plan.TableIdNode
   720  }
   721  
   722  type Index struct {
   723  	// ordered list of index columns
   724  	order []sql.ColumnId
   725  	// unordered column set
   726  	set sql.ColSet
   727  	idx sql.Index
   728  }
   729  
   730  func (i *Index) Cols() []sql.ColumnId {
   731  	return i.order
   732  }
   733  
   734  func (i *Index) ColSet() sql.ColSet {
   735  	return i.set
   736  }
   737  
   738  func (i *Index) SqlIdx() sql.Index {
   739  	return i.idx
   740  }
   741  
   742  type sourceBase struct {
   743  	*relBase
   744  	indexes []*Index
   745  }
   746  
   747  func (s *sourceBase) Indexes() []*Index {
   748  	return s.indexes
   749  }
   750  
   751  func (s *sourceBase) SetIndexes(indexes []*Index) {
   752  	s.indexes = indexes
   753  }
   754  
   755  // JoinRel represents a plan.JoinNode or plan.CrossJoin. See plan.JoinType
   756  // for the full list.
   757  type JoinRel interface {
   758  	RelExpr
   759  	JoinPrivate() *JoinBase
   760  	Group() *ExprGroup
   761  }
   762  
   763  var _ JoinRel = (*AntiJoin)(nil)
   764  var _ JoinRel = (*ConcatJoin)(nil)
   765  var _ JoinRel = (*CrossJoin)(nil)
   766  var _ JoinRel = (*LeftJoin)(nil)
   767  var _ JoinRel = (*FullOuterJoin)(nil)
   768  var _ JoinRel = (*HashJoin)(nil)
   769  var _ JoinRel = (*InnerJoin)(nil)
   770  var _ JoinRel = (*LookupJoin)(nil)
   771  var _ JoinRel = (*SemiJoin)(nil)
   772  
   773  type JoinBase struct {
   774  	*relBase
   775  
   776  	Op     plan.JoinType
   777  	Filter []sql.Expression
   778  	Left   *ExprGroup
   779  	Right  *ExprGroup
   780  }
   781  
   782  func (r *JoinBase) Children() []*ExprGroup {
   783  	return []*ExprGroup{r.Left, r.Right}
   784  }
   785  
   786  func (r *JoinBase) JoinPrivate() *JoinBase {
   787  	return r
   788  }
   789  
   790  // Copy creates a JoinBase with the same underlying join expression.
   791  // note: it is important to Copy the base node to avoid cyclical
   792  // relExpr references in the ExprGroup linked list.
   793  func (r *JoinBase) Copy() *JoinBase {
   794  	return &JoinBase{
   795  		relBase: &relBase{
   796  			g: r.g,
   797  			n: r.n,
   798  			c: r.c,
   799  		},
   800  		Op:     r.Op,
   801  		Filter: r.Filter,
   802  		Left:   r.Left,
   803  		Right:  r.Right,
   804  	}
   805  }
   806  
   807  func (r *LookupJoin) Children() []*ExprGroup {
   808  	return []*ExprGroup{r.Left, r.Right}
   809  }
   810  
   811  // RangeHeap contains all the information necessary to construct a RangeHeap join.
   812  // Because both sides of the join can be implemented either by an index or a sorted node,
   813  // we require that exactly one of ValueIndex and ValueExpr is non-nil, and exactly one
   814  // of MinIndex and MinExpr is non-nil. If the index is non-nil, we will use it to construct
   815  // a plan.IndexedTableAccess. Otherwise we use the expression to construct a plan.Sort.
   816  type RangeHeap struct {
   817  	ValueIndex *IndexScan
   818  	ValueExpr  sql.Expression
   819  
   820  	MinIndex *IndexScan
   821  	MinExpr  sql.Expression
   822  
   823  	ValueCol                *expression.GetField
   824  	MinColRef               *expression.GetField
   825  	MaxColRef               *expression.GetField
   826  	RangeClosedOnLowerBound bool
   827  	RangeClosedOnUpperBound bool
   828  	Parent                  *JoinBase
   829  }