github.com/whtcorpsinc/MilevaDB-Prod@v0.0.0-20211104133533-f57f4be3b597/allegrosql/stream.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 allegrosql 15 16 import ( 17 "context" 18 "time" 19 20 "github.com/whtcorpsinc/BerolinaSQL/terror" 21 "github.com/whtcorpsinc/errors" 22 "github.com/whtcorpsinc/fidelpb/go-fidelpb" 23 "github.com/whtcorpsinc/milevadb/ekv" 24 "github.com/whtcorpsinc/milevadb/metrics" 25 "github.com/whtcorpsinc/milevadb/soliton/chunk" 26 "github.com/whtcorpsinc/milevadb/soliton/codec" 27 "github.com/whtcorpsinc/milevadb/statistics" 28 "github.com/whtcorpsinc/milevadb/stochastikctx" 29 "github.com/whtcorpsinc/milevadb/types" 30 ) 31 32 // streamResult implements the SelectResult interface. 33 type streamResult struct { 34 label string 35 sqlType string 36 37 resp ekv.Response 38 rowLen int 39 fieldTypes []*types.FieldType 40 ctx stochastikctx.Context 41 42 // NOTE: curr == nil means stream finish, while len(curr.RowsData) == 0 doesn't. 43 curr *fidelpb.Chunk 44 partialCount int64 45 feedback *statistics.QueryFeedback 46 47 fetchDuration time.Duration 48 durationReported bool 49 } 50 51 func (r *streamResult) Fetch(context.Context) {} 52 53 func (r *streamResult) Next(ctx context.Context, chk *chunk.Chunk) error { 54 chk.Reset() 55 for !chk.IsFull() { 56 err := r.readDataIfNecessary(ctx) 57 if err != nil { 58 return err 59 } 60 if r.curr == nil { 61 return nil 62 } 63 64 err = r.flushToChunk(chk) 65 if err != nil { 66 return err 67 } 68 } 69 return nil 70 } 71 72 // readDataFromResponse read the data to result. Returns true means the resp is finished. 73 func (r *streamResult) readDataFromResponse(ctx context.Context, resp ekv.Response, result *fidelpb.Chunk) (bool, error) { 74 startTime := time.Now() 75 resultSubset, err := resp.Next(ctx) 76 duration := time.Since(startTime) 77 r.fetchDuration += duration 78 if err != nil { 79 return false, err 80 } 81 if resultSubset == nil { 82 if !r.durationReported { 83 // TODO: Add a label to distinguish between success or failure. 84 // https://github.com/whtcorpsinc/milevadb/issues/11397 85 metrics.DistALLEGROSQLQueryHistogram.WithLabelValues(r.label, r.sqlType).Observe(r.fetchDuration.Seconds()) 86 r.durationReported = true 87 } 88 return true, nil 89 } 90 91 var stream fidelpb.StreamResponse 92 err = stream.Unmarshal(resultSubset.GetData()) 93 if err != nil { 94 return false, errors.Trace(err) 95 } 96 if stream.Error != nil { 97 return false, errors.Errorf("stream response error: [%d]%s\n", stream.Error.Code, stream.Error.Msg) 98 } 99 for _, warning := range stream.Warnings { 100 r.ctx.GetStochastikVars().StmtCtx.AppendWarning(terror.ClassEinsteinDB.Synthesize(terror.ErrCode(warning.Code), warning.Msg)) 101 } 102 103 err = result.Unmarshal(stream.Data) 104 if err != nil { 105 return false, errors.Trace(err) 106 } 107 r.feedback.UFIDelate(resultSubset.GetStartKey(), stream.OutputCounts) 108 r.partialCount++ 109 110 hasStats, ok := resultSubset.(CopRuntimeStats) 111 if ok { 112 copStats := hasStats.GetCopRuntimeStats() 113 if copStats != nil { 114 copStats.CopTime = duration 115 r.ctx.GetStochastikVars().StmtCtx.MergeInterDircDetails(&copStats.InterDircDetails, nil) 116 } 117 } 118 return false, nil 119 } 120 121 // readDataIfNecessary ensures there are some data in current chunk. If no more data, r.curr == nil. 122 func (r *streamResult) readDataIfNecessary(ctx context.Context) error { 123 if r.curr != nil && len(r.curr.RowsData) > 0 { 124 return nil 125 } 126 127 tmp := new(fidelpb.Chunk) 128 finish, err := r.readDataFromResponse(ctx, r.resp, tmp) 129 if err != nil { 130 return err 131 } 132 if finish { 133 r.curr = nil 134 return nil 135 } 136 r.curr = tmp 137 return nil 138 } 139 140 func (r *streamResult) flushToChunk(chk *chunk.Chunk) (err error) { 141 remainRowsData := r.curr.RowsData 142 causetDecoder := codec.NewCausetDecoder(chk, r.ctx.GetStochastikVars().Location()) 143 for !chk.IsFull() && len(remainRowsData) > 0 { 144 for i := 0; i < r.rowLen; i++ { 145 remainRowsData, err = causetDecoder.DecodeOne(remainRowsData, i, r.fieldTypes[i]) 146 if err != nil { 147 return err 148 } 149 } 150 } 151 r.curr.RowsData = remainRowsData 152 if len(remainRowsData) == 0 { 153 r.curr = nil // Current chunk is finished. 154 } 155 return nil 156 } 157 158 func (r *streamResult) NextRaw(ctx context.Context) ([]byte, error) { 159 r.partialCount++ 160 r.feedback.Invalidate() 161 resultSubset, err := r.resp.Next(ctx) 162 if resultSubset == nil || err != nil { 163 return nil, err 164 } 165 return resultSubset.GetData(), err 166 } 167 168 func (r *streamResult) Close() error { 169 if r.feedback.Actual() > 0 { 170 metrics.DistALLEGROSQLScanKeysHistogram.Observe(float64(r.feedback.Actual())) 171 } 172 metrics.DistALLEGROSQLPartialCountHistogram.Observe(float64(r.partialCount)) 173 return nil 174 }