github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/executor/new_executor.go (about)

     1  // Copyright 2016 PingCAP, 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  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package executor
    15  
    16  import (
    17  	"github.com/insionng/yougam/libraries/juju/errors"
    18  	"github.com/insionng/yougam/libraries/pingcap/tidb/ast"
    19  	"github.com/insionng/yougam/libraries/pingcap/tidb/context"
    20  	"github.com/insionng/yougam/libraries/pingcap/tidb/evaluator"
    21  	"github.com/insionng/yougam/libraries/pingcap/tidb/util/codec"
    22  	"github.com/insionng/yougam/libraries/pingcap/tidb/util/types"
    23  )
    24  
    25  // HashJoinExec implements the hash join algorithm.
    26  type HashJoinExec struct {
    27  	hashTable    map[string][]*Row
    28  	smallHashKey []ast.ExprNode
    29  	bigHashKey   []ast.ExprNode
    30  	smallExec    Executor
    31  	bigExec      Executor
    32  	prepared     bool
    33  	fields       []*ast.ResultField
    34  	ctx          context.Context
    35  	smallFilter  ast.ExprNode
    36  	bigFilter    ast.ExprNode
    37  	otherFilter  ast.ExprNode
    38  	outter       bool
    39  	leftSmall    bool
    40  	matchedRows  []*Row
    41  	cursor       int
    42  }
    43  
    44  func joinTwoRow(a *Row, b *Row) *Row {
    45  	ret := &Row{
    46  		RowKeys: make([]*RowKeyEntry, 0, len(a.RowKeys)+len(b.RowKeys)),
    47  		Data:    make([]types.Datum, 0, len(a.Data)+len(b.Data)),
    48  	}
    49  	ret.RowKeys = append(ret.RowKeys, a.RowKeys...)
    50  	ret.RowKeys = append(ret.RowKeys, b.RowKeys...)
    51  	ret.Data = append(ret.Data, a.Data...)
    52  	ret.Data = append(ret.Data, b.Data...)
    53  	return ret
    54  }
    55  
    56  func (e *HashJoinExec) getHashKey(exprs []ast.ExprNode) ([]byte, error) {
    57  	vals := make([]types.Datum, 0, len(exprs))
    58  	for _, expr := range exprs {
    59  		v, err := evaluator.Eval(e.ctx, expr)
    60  		if err != nil {
    61  			return nil, errors.Trace(err)
    62  		}
    63  		vals = append(vals, v)
    64  	}
    65  	if len(vals) == 0 {
    66  		return []byte{}, nil
    67  	}
    68  
    69  	return codec.EncodeValue([]byte{}, vals...)
    70  }
    71  
    72  // Fields implements Executor Fields interface.
    73  func (e *HashJoinExec) Fields() []*ast.ResultField {
    74  	return e.fields
    75  }
    76  
    77  // Close implements Executor Close interface.
    78  func (e *HashJoinExec) Close() error {
    79  	e.hashTable = nil
    80  	e.matchedRows = nil
    81  	return nil
    82  }
    83  
    84  func (e *HashJoinExec) prepare() error {
    85  	e.hashTable = make(map[string][]*Row)
    86  	e.cursor = 0
    87  	for {
    88  		row, err := e.smallExec.Next()
    89  		if err != nil {
    90  			return errors.Trace(err)
    91  		}
    92  		if row == nil {
    93  			e.smallExec.Close()
    94  			break
    95  		}
    96  
    97  		matched := true
    98  		if e.smallFilter != nil {
    99  			matched, err = evaluator.EvalBool(e.ctx, e.smallFilter)
   100  			if err != nil {
   101  				return errors.Trace(err)
   102  			}
   103  			if !matched {
   104  				continue
   105  			}
   106  		}
   107  		hashcode, err := e.getHashKey(e.smallHashKey)
   108  		if err != nil {
   109  			return errors.Trace(err)
   110  		}
   111  		if rows, ok := e.hashTable[string(hashcode)]; !ok {
   112  			e.hashTable[string(hashcode)] = []*Row{row}
   113  		} else {
   114  			e.hashTable[string(hashcode)] = append(rows, row)
   115  		}
   116  	}
   117  
   118  	e.prepared = true
   119  	return nil
   120  }
   121  
   122  func (e *HashJoinExec) constructMatchedRows(bigRow *Row) (matchedRows []*Row, err error) {
   123  	hashcode, err := e.getHashKey(e.bigHashKey)
   124  	if err != nil {
   125  		return nil, errors.Trace(err)
   126  	}
   127  
   128  	rows, ok := e.hashTable[string(hashcode)]
   129  	if !ok {
   130  		return
   131  	}
   132  	// match eq condition
   133  	for _, smallRow := range rows {
   134  		//TODO: remove result fields in order to reduce memory copy cost.
   135  		otherMatched := true
   136  		if e.otherFilter != nil {
   137  			startKey := 0
   138  			if !e.leftSmall {
   139  				startKey = len(bigRow.Data)
   140  			}
   141  			for i, data := range smallRow.Data {
   142  				e.fields[i+startKey].Expr.SetValue(data.GetValue())
   143  			}
   144  			otherMatched, err = evaluator.EvalBool(e.ctx, e.otherFilter)
   145  		}
   146  		if err != nil {
   147  			return nil, errors.Trace(err)
   148  		}
   149  		if otherMatched {
   150  			if e.leftSmall {
   151  				matchedRows = append(matchedRows, joinTwoRow(smallRow, bigRow))
   152  			} else {
   153  				matchedRows = append(matchedRows, joinTwoRow(bigRow, smallRow))
   154  			}
   155  		}
   156  	}
   157  
   158  	return matchedRows, nil
   159  }
   160  
   161  func (e *HashJoinExec) fillNullRow(bigRow *Row) (returnRow *Row, err error) {
   162  	smallRow := &Row{
   163  		RowKeys: make([]*RowKeyEntry, len(e.smallExec.Fields())),
   164  		Data:    make([]types.Datum, len(e.smallExec.Fields())),
   165  	}
   166  
   167  	for _, data := range smallRow.Data {
   168  		data.SetNull()
   169  	}
   170  	if e.leftSmall {
   171  		returnRow = joinTwoRow(smallRow, bigRow)
   172  	} else {
   173  		returnRow = joinTwoRow(bigRow, smallRow)
   174  	}
   175  	for i, data := range returnRow.Data {
   176  		e.fields[i].Expr.SetValue(data.GetValue())
   177  	}
   178  	return returnRow, nil
   179  }
   180  
   181  func (e *HashJoinExec) returnRecord() (ret *Row, ok bool) {
   182  	if e.cursor >= len(e.matchedRows) {
   183  		return nil, false
   184  	}
   185  	for i, data := range e.matchedRows[e.cursor].Data {
   186  		e.fields[i].Expr.SetValue(data.GetValue())
   187  	}
   188  	e.cursor++
   189  	return e.matchedRows[e.cursor-1], true
   190  }
   191  
   192  // Next implements Executor Next interface.
   193  func (e *HashJoinExec) Next() (*Row, error) {
   194  	if !e.prepared {
   195  		if err := e.prepare(); err != nil {
   196  			return nil, errors.Trace(err)
   197  		}
   198  	}
   199  
   200  	row, ok := e.returnRecord()
   201  	if ok {
   202  		return row, nil
   203  	}
   204  
   205  	for {
   206  		bigRow, err := e.bigExec.Next()
   207  		if err != nil {
   208  			return nil, errors.Trace(err)
   209  		}
   210  		if bigRow == nil {
   211  			e.bigExec.Close()
   212  			return nil, nil
   213  		}
   214  
   215  		var matchedRows []*Row
   216  		bigMatched := true
   217  		if e.bigFilter != nil {
   218  			bigMatched, err = evaluator.EvalBool(e.ctx, e.bigFilter)
   219  			if err != nil {
   220  				return nil, errors.Trace(err)
   221  			}
   222  		}
   223  		if bigMatched {
   224  			matchedRows, err = e.constructMatchedRows(bigRow)
   225  			if err != nil {
   226  				return nil, errors.Trace(err)
   227  			}
   228  		}
   229  		e.matchedRows = matchedRows
   230  		e.cursor = 0
   231  		row, ok := e.returnRecord()
   232  		if ok {
   233  			return row, nil
   234  		} else if e.outter {
   235  			return e.fillNullRow(bigRow)
   236  		}
   237  	}
   238  }