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 }