github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/interlock/table_reader.go (about) 1 // Copyright 2020 WHTCORPS INC, 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 interlock 15 16 import ( 17 "context" 18 "sort" 19 20 "github.com/opentracing/opentracing-go" 21 "github.com/whtcorpsinc/BerolinaSQL/perceptron" 22 "github.com/whtcorpsinc/fidelpb/go-fidelpb" 23 "github.com/whtcorpsinc/milevadb/allegrosql" 24 "github.com/whtcorpsinc/milevadb/causet" 25 causetembedded "github.com/whtcorpsinc/milevadb/causet/embedded" 26 "github.com/whtcorpsinc/milevadb/ekv" 27 "github.com/whtcorpsinc/milevadb/memex" 28 "github.com/whtcorpsinc/milevadb/soliton/chunk" 29 "github.com/whtcorpsinc/milevadb/soliton/logutil" 30 "github.com/whtcorpsinc/milevadb/soliton/memory" 31 "github.com/whtcorpsinc/milevadb/soliton/ranger" 32 "github.com/whtcorpsinc/milevadb/soliton/stringutil" 33 "github.com/whtcorpsinc/milevadb/statistics" 34 "github.com/whtcorpsinc/milevadb/stochastikctx" 35 "github.com/whtcorpsinc/milevadb/types" 36 ) 37 38 // make sure `BlockReaderInterlockingDirectorate` implements `InterlockingDirectorate`. 39 var _ InterlockingDirectorate = &BlockReaderInterlockingDirectorate{} 40 41 // selectResultHook is used to replog allegrosql.SelectWithRuntimeStats safely for testing. 42 type selectResultHook struct { 43 selectResultFunc func(ctx context.Context, sctx stochastikctx.Context, ekvReq *ekv.Request, 44 fieldTypes []*types.FieldType, fb *statistics.QueryFeedback, copCausetIDs []int) (allegrosql.SelectResult, error) 45 } 46 47 func (sr selectResultHook) SelectResult(ctx context.Context, sctx stochastikctx.Context, ekvReq *ekv.Request, 48 fieldTypes []*types.FieldType, fb *statistics.QueryFeedback, copCausetIDs []int, rootCausetID int) (allegrosql.SelectResult, error) { 49 if sr.selectResultFunc == nil { 50 return allegrosql.SelectWithRuntimeStats(ctx, sctx, ekvReq, fieldTypes, fb, copCausetIDs, rootCausetID) 51 } 52 return sr.selectResultFunc(ctx, sctx, ekvReq, fieldTypes, fb, copCausetIDs) 53 } 54 55 type ekvRangeBuilder interface { 56 buildKeyRange(pid int64) ([]ekv.KeyRange, error) 57 } 58 59 // BlockReaderInterlockingDirectorate sends PosetDag request and reads causet data from ekv layer. 60 type BlockReaderInterlockingDirectorate struct { 61 baseInterlockingDirectorate 62 63 causet causet.Block 64 65 // The source of key ranges varies from case to case. 66 // It may be calculated from PyhsicalCauset by interlockBuilder, or calculated from argument by dataBuilder; 67 // It may be calculated from ranger.Ranger, or calculated from handles. 68 // The causet ID may also change because of the partition causet, and causes the key range to change. 69 // So instead of keeping a `range` struct field, it's better to define a interface. 70 ekvRangeBuilder 71 // TODO: remove this field, use the ekvRangeBuilder interface. 72 ranges []*ranger.Range 73 74 // ekvRanges are only use for union scan. 75 ekvRanges []ekv.KeyRange 76 posetPosetDagPB *fidelpb.PosetDagRequest 77 startTS uint64 78 // defCausumns are only required by union scan and virtual defCausumn. 79 defCausumns []*perceptron.DeferredCausetInfo 80 81 // resultHandler handles the order of the result. Since (MAXInt64, MAXUint64] stores before [0, MaxInt64] physically 82 // for unsigned int. 83 resultHandler *blockResultHandler 84 feedback *statistics.QueryFeedback 85 plans []causetembedded.PhysicalCauset 86 blockCauset causetembedded.PhysicalCauset 87 88 memTracker *memory.Tracker 89 selectResultHook // for testing 90 91 keepOrder bool 92 desc bool 93 streaming bool 94 storeType ekv.StoreType 95 // corDefCausInFilter tells whether there's correlated defCausumn in filter. 96 corDefCausInFilter bool 97 // corDefCausInAccess tells whether there's correlated defCausumn in access conditions. 98 corDefCausInAccess bool 99 // virtualDeferredCausetIndex records all the indices of virtual defCausumns and sort them in definition 100 // to make sure we can compute the virtual defCausumn in right order. 101 virtualDeferredCausetIndex []int 102 // virtualDeferredCausetRetFieldTypes records the RetFieldTypes of virtual defCausumns. 103 virtualDeferredCausetRetFieldTypes []*types.FieldType 104 // batchCop indicates whether use super batch interlock request, only works for TiFlash engine. 105 batchCop bool 106 } 107 108 // Open initialzes necessary variables for using this interlock. 109 func (e *BlockReaderInterlockingDirectorate) Open(ctx context.Context) error { 110 if span := opentracing.SpanFromContext(ctx); span != nil && span.Tracer() != nil { 111 span1 := span.Tracer().StartSpan("BlockReaderInterlockingDirectorate.Open", opentracing.ChildOf(span.Context())) 112 defer span1.Finish() 113 ctx = opentracing.ContextWithSpan(ctx, span1) 114 } 115 116 e.memTracker = memory.NewTracker(e.id, -1) 117 e.memTracker.AttachTo(e.ctx.GetStochastikVars().StmtCtx.MemTracker) 118 119 var err error 120 if e.corDefCausInFilter { 121 if e.storeType == ekv.TiFlash { 122 execs, _, err := constructDistInterDircForTiFlash(e.ctx, e.blockCauset) 123 if err != nil { 124 return err 125 } 126 e.posetPosetDagPB.RootInterlockingDirectorate = execs[0] 127 } else { 128 e.posetPosetDagPB.InterlockingDirectorates, _, err = constructDistInterDirc(e.ctx, e.plans) 129 if err != nil { 130 return err 131 } 132 } 133 } 134 if e.runtimeStats != nil { 135 defCauslInterDirc := true 136 e.posetPosetDagPB.DefCauslectInterDircutionSummaries = &defCauslInterDirc 137 } 138 if e.corDefCausInAccess { 139 ts := e.plans[0].(*causetembedded.PhysicalBlockScan) 140 access := ts.AccessCondition 141 pkTP := ts.Block.GetPkDefCausInfo().FieldType 142 e.ranges, err = ranger.BuildBlockRange(access, e.ctx.GetStochastikVars().StmtCtx, &pkTP) 143 if err != nil { 144 return err 145 } 146 } 147 148 e.resultHandler = &blockResultHandler{} 149 if e.feedback != nil && e.feedback.Hist != nil { 150 // EncodeInt don't need *memex.Context. 151 var ok bool 152 e.ranges, ok = e.feedback.Hist.SplitRange(nil, e.ranges, false) 153 if !ok { 154 e.feedback.Invalidate() 155 } 156 } 157 firstPartRanges, secondPartRanges := splitRanges(e.ranges, e.keepOrder, e.desc) 158 firstResult, err := e.buildResp(ctx, firstPartRanges) 159 if err != nil { 160 e.feedback.Invalidate() 161 return err 162 } 163 if len(secondPartRanges) == 0 { 164 e.resultHandler.open(nil, firstResult) 165 return nil 166 } 167 var secondResult allegrosql.SelectResult 168 secondResult, err = e.buildResp(ctx, secondPartRanges) 169 if err != nil { 170 e.feedback.Invalidate() 171 return err 172 } 173 e.resultHandler.open(firstResult, secondResult) 174 return nil 175 } 176 177 // Next fills data into the chunk passed by its caller. 178 // The task was actually done by blockReaderHandler. 179 func (e *BlockReaderInterlockingDirectorate) Next(ctx context.Context, req *chunk.Chunk) error { 180 logutil.Eventf(ctx, "causet scan causet: %s, range: %v", stringutil.MemoizeStr(func() string { 181 var blockName string 182 if spacetime := e.causet.Meta(); spacetime != nil { 183 blockName = spacetime.Name.L 184 } 185 return blockName 186 }), e.ranges) 187 if err := e.resultHandler.nextChunk(ctx, req); err != nil { 188 e.feedback.Invalidate() 189 return err 190 } 191 192 err := FillVirtualDeferredCausetValue(e.virtualDeferredCausetRetFieldTypes, e.virtualDeferredCausetIndex, e.schemaReplicant, e.defCausumns, e.ctx, req) 193 if err != nil { 194 return err 195 } 196 197 return nil 198 } 199 200 // Close implements the InterlockingDirectorate Close interface. 201 func (e *BlockReaderInterlockingDirectorate) Close() error { 202 var err error 203 if e.resultHandler != nil { 204 err = e.resultHandler.Close() 205 } 206 e.ekvRanges = e.ekvRanges[:0] 207 e.ctx.StoreQueryFeedback(e.feedback) 208 return err 209 } 210 211 // buildResp first builds request and sends it to einsteindb using allegrosql.Select. It uses SelectResut returned by the callee 212 // to fetch all results. 213 func (e *BlockReaderInterlockingDirectorate) buildResp(ctx context.Context, ranges []*ranger.Range) (allegrosql.SelectResult, error) { 214 var builder allegrosql.RequestBuilder 215 var reqBuilder *allegrosql.RequestBuilder 216 if e.ekvRangeBuilder != nil { 217 ekvRange, err := e.ekvRangeBuilder.buildKeyRange(getPhysicalBlockID(e.causet)) 218 if err != nil { 219 return nil, err 220 } 221 reqBuilder = builder.SetKeyRanges(ekvRange) 222 } else if e.causet.Meta() != nil && e.causet.Meta().IsCommonHandle { 223 reqBuilder = builder.SetCommonHandleRanges(e.ctx.GetStochastikVars().StmtCtx, getPhysicalBlockID(e.causet), ranges) 224 } else { 225 reqBuilder = builder.SetBlockRanges(getPhysicalBlockID(e.causet), ranges, e.feedback) 226 } 227 ekvReq, err := reqBuilder. 228 SetPosetDagRequest(e.posetPosetDagPB). 229 SetStartTS(e.startTS). 230 SetDesc(e.desc). 231 SetKeepOrder(e.keepOrder). 232 SetStreaming(e.streaming). 233 SetFromStochastikVars(e.ctx.GetStochastikVars()). 234 SetMemTracker(e.memTracker). 235 SetStoreType(e.storeType). 236 SetAllowBatchCop(e.batchCop). 237 Build() 238 if err != nil { 239 return nil, err 240 } 241 e.ekvRanges = append(e.ekvRanges, ekvReq.KeyRanges...) 242 243 result, err := e.SelectResult(ctx, e.ctx, ekvReq, retTypes(e), e.feedback, getPhysicalCausetIDs(e.plans), e.id) 244 if err != nil { 245 return nil, err 246 } 247 result.Fetch(ctx) 248 return result, nil 249 } 250 251 func buildVirtualDeferredCausetIndex(schemaReplicant *memex.Schema, defCausumns []*perceptron.DeferredCausetInfo) []int { 252 virtualDeferredCausetIndex := make([]int, 0, len(defCausumns)) 253 for i, defCaus := range schemaReplicant.DeferredCausets { 254 if defCaus.VirtualExpr != nil { 255 virtualDeferredCausetIndex = append(virtualDeferredCausetIndex, i) 256 } 257 } 258 sort.Slice(virtualDeferredCausetIndex, func(i, j int) bool { 259 return causetembedded.FindDeferredCausetInfoByID(defCausumns, schemaReplicant.DeferredCausets[virtualDeferredCausetIndex[i]].ID).Offset < 260 causetembedded.FindDeferredCausetInfoByID(defCausumns, schemaReplicant.DeferredCausets[virtualDeferredCausetIndex[j]].ID).Offset 261 }) 262 return virtualDeferredCausetIndex 263 } 264 265 // buildVirtualDeferredCausetInfo saves virtual defCausumn indices and sort them in definition order 266 func (e *BlockReaderInterlockingDirectorate) buildVirtualDeferredCausetInfo() { 267 e.virtualDeferredCausetIndex = buildVirtualDeferredCausetIndex(e.Schema(), e.defCausumns) 268 if len(e.virtualDeferredCausetIndex) > 0 { 269 e.virtualDeferredCausetRetFieldTypes = make([]*types.FieldType, len(e.virtualDeferredCausetIndex)) 270 for i, idx := range e.virtualDeferredCausetIndex { 271 e.virtualDeferredCausetRetFieldTypes[i] = e.schemaReplicant.DeferredCausets[idx].RetType 272 } 273 } 274 } 275 276 type blockResultHandler struct { 277 // If the pk is unsigned and we have KeepOrder=true and want ascending order, 278 // `optionalResult` will handles the request whose range is in signed int range, and 279 // `result` will handle the request whose range is exceed signed int range. 280 // If we want descending order, `optionalResult` will handles the request whose range is exceed signed, and 281 // the `result` will handle the request whose range is in signed. 282 // Otherwise, we just set `optionalFinished` true and the `result` handles the whole ranges. 283 optionalResult allegrosql.SelectResult 284 result allegrosql.SelectResult 285 286 optionalFinished bool 287 } 288 289 func (tr *blockResultHandler) open(optionalResult, result allegrosql.SelectResult) { 290 if optionalResult == nil { 291 tr.optionalFinished = true 292 tr.result = result 293 return 294 } 295 tr.optionalResult = optionalResult 296 tr.result = result 297 tr.optionalFinished = false 298 } 299 300 func (tr *blockResultHandler) nextChunk(ctx context.Context, chk *chunk.Chunk) error { 301 if !tr.optionalFinished { 302 err := tr.optionalResult.Next(ctx, chk) 303 if err != nil { 304 return err 305 } 306 if chk.NumEvents() > 0 { 307 return nil 308 } 309 tr.optionalFinished = true 310 } 311 return tr.result.Next(ctx, chk) 312 } 313 314 func (tr *blockResultHandler) nextRaw(ctx context.Context) (data []byte, err error) { 315 if !tr.optionalFinished { 316 data, err = tr.optionalResult.NextRaw(ctx) 317 if err != nil { 318 return nil, err 319 } 320 if data != nil { 321 return data, nil 322 } 323 tr.optionalFinished = true 324 } 325 data, err = tr.result.NextRaw(ctx) 326 if err != nil { 327 return nil, err 328 } 329 return data, nil 330 } 331 332 func (tr *blockResultHandler) Close() error { 333 err := closeAll(tr.optionalResult, tr.result) 334 tr.optionalResult, tr.result = nil, nil 335 return err 336 }