github.com/matrixorigin/matrixone@v1.2.0/pkg/frontend/output.go (about) 1 // Copyright 2021 Matrix Origin 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 frontend 16 17 import ( 18 "context" 19 20 "go.uber.org/zap" 21 22 "github.com/matrixorigin/matrixone/pkg/common/moerr" 23 "github.com/matrixorigin/matrixone/pkg/container/batch" 24 "github.com/matrixorigin/matrixone/pkg/container/types" 25 "github.com/matrixorigin/matrixone/pkg/container/vector" 26 ) 27 28 var _ outputPool = &outputQueue{} 29 var _ outputPool = &fakeOutputQueue{} 30 31 type outputQueue struct { 32 ses *Session 33 ctx context.Context 34 proto MysqlProtocol 35 mrs *MysqlResultSet 36 rowIdx uint64 37 length uint64 38 ep *ExportConfig 39 lineStr []byte 40 showStmtType ShowStatementType 41 } 42 43 func NewOutputQueue(ctx context.Context, ses *Session, columnCount int, mrs *MysqlResultSet, ep *ExportConfig) *outputQueue { 44 const countOfResultSet = 1 45 if mrs == nil { 46 //Create a new temporary result set per pipeline thread. 47 mrs = &MysqlResultSet{} 48 //Warning: Don't change ResultColumns in this. 49 //Reference the shared ResultColumns of the session among multi-thread. 50 sesMrs := ses.GetMysqlResultSet() 51 mrs.Columns = sesMrs.Columns 52 mrs.Name2Index = sesMrs.Name2Index 53 54 //group row 55 mrs.Data = make([][]interface{}, countOfResultSet) 56 for i := 0; i < countOfResultSet; i++ { 57 mrs.Data[i] = make([]interface{}, columnCount) 58 } 59 } 60 61 if ep == nil { 62 ep = ses.GetExportConfig() 63 } 64 65 return &outputQueue{ 66 ctx: ctx, 67 ses: ses, 68 proto: ses.GetMysqlProtocol(), 69 mrs: mrs, 70 rowIdx: 0, 71 length: uint64(countOfResultSet), 72 ep: ep, 73 showStmtType: ses.GetShowStmtType(), 74 } 75 } 76 77 func (oq *outputQueue) resetLineStr() { 78 oq.lineStr = oq.lineStr[:0] 79 } 80 81 func (oq *outputQueue) reset() {} 82 83 /* 84 getEmptyRow returns an empty space for filling data. 85 If there is no space, it flushes the data into the protocol 86 and returns an empty space then. 87 */ 88 func (oq *outputQueue) getEmptyRow() ([]interface{}, error) { 89 if oq.rowIdx >= oq.length { 90 if err := oq.flush(); err != nil { 91 return nil, err 92 } 93 } 94 95 row := oq.mrs.Data[oq.rowIdx] 96 oq.rowIdx++ 97 return row, nil 98 } 99 100 /* 101 flush will force the data flushed into the protocol. 102 */ 103 func (oq *outputQueue) flush() error { 104 105 if oq.rowIdx <= 0 { 106 return nil 107 } 108 if oq.ep.needExportToFile() { 109 if err := exportDataToCSVFile(oq); err != nil { 110 logError(oq.ses, oq.ses.GetDebugString(), 111 "Error occurred while exporting to CSV file", 112 zap.Error(err)) 113 return err 114 } 115 } else { 116 //send group of row 117 if oq.showStmtType == ShowTableStatus { 118 oq.rowIdx = 0 119 return nil 120 } 121 122 if err := oq.proto.SendResultSetTextBatchRowSpeedup(oq.mrs, oq.rowIdx); err != nil { 123 logError(oq.ses, oq.ses.GetDebugString(), 124 "Flush error", 125 zap.Error(err)) 126 return err 127 } 128 } 129 oq.rowIdx = 0 130 return nil 131 } 132 133 // extractRowFromEveryVector gets the j row from the every vector and outputs the row 134 // needCopyBytes : true -- make a copy of the bytes. else not. 135 // Case 1: needCopyBytes = false. 136 // For responding the client, we do not make a copy of the bytes. Because the data 137 // has been written into the tcp conn before the batch.Batch returned to the pipeline. 138 // Case 2: needCopyBytes = true. 139 // For the background execution, we need to make a copy of the bytes. Because the data 140 // has been saved in the session. Later the data will be used but then the batch.Batch has 141 // been returned to the pipeline and may be reused and changed by the pipeline. 142 func extractRowFromEveryVector(ctx context.Context, ses FeSession, dataSet *batch.Batch, j int, oq outputPool, needCopyBytes bool) ([]interface{}, error) { 143 row, err := oq.getEmptyRow() 144 if err != nil { 145 return nil, err 146 } 147 var rowIndex = j 148 for i, vec := range dataSet.Vecs { //col index 149 rowIndexBackup := rowIndex 150 if vec.IsConstNull() { 151 row[i] = nil 152 continue 153 } 154 if vec.IsConst() { 155 rowIndex = 0 156 } 157 158 err = extractRowFromVector(ctx, ses, vec, i, row, rowIndex, needCopyBytes) 159 if err != nil { 160 return nil, err 161 } 162 rowIndex = rowIndexBackup 163 } 164 return row, nil 165 } 166 167 // extractRowFromVector gets the rowIndex row from the i vector 168 func extractRowFromVector(ctx context.Context, ses FeSession, vec *vector.Vector, i int, row []interface{}, rowIndex int, needCopyBytes bool) error { 169 if vec.IsConstNull() || vec.GetNulls().Contains(uint64(rowIndex)) { 170 row[i] = nil 171 return nil 172 } 173 174 switch vec.GetType().Oid { //get col 175 case types.T_json: 176 row[i] = types.DecodeJson(copyBytes(vec.GetBytesAt(rowIndex), needCopyBytes)) 177 case types.T_bool: 178 row[i] = vector.GetFixedAt[bool](vec, rowIndex) 179 case types.T_bit: 180 row[i] = vector.GetFixedAt[uint64](vec, rowIndex) 181 case types.T_int8: 182 row[i] = vector.GetFixedAt[int8](vec, rowIndex) 183 case types.T_uint8: 184 row[i] = vector.GetFixedAt[uint8](vec, rowIndex) 185 case types.T_int16: 186 row[i] = vector.GetFixedAt[int16](vec, rowIndex) 187 case types.T_uint16: 188 row[i] = vector.GetFixedAt[uint16](vec, rowIndex) 189 case types.T_int32: 190 row[i] = vector.GetFixedAt[int32](vec, rowIndex) 191 case types.T_uint32: 192 row[i] = vector.GetFixedAt[uint32](vec, rowIndex) 193 case types.T_int64: 194 row[i] = vector.GetFixedAt[int64](vec, rowIndex) 195 case types.T_uint64: 196 row[i] = vector.GetFixedAt[uint64](vec, rowIndex) 197 case types.T_float32: 198 row[i] = vector.GetFixedAt[float32](vec, rowIndex) 199 case types.T_float64: 200 row[i] = vector.GetFixedAt[float64](vec, rowIndex) 201 case types.T_char, types.T_varchar, types.T_blob, types.T_text, types.T_binary, types.T_varbinary: 202 row[i] = copyBytes(vec.GetBytesAt(rowIndex), needCopyBytes) 203 case types.T_array_float32: 204 // NOTE: Don't merge it with T_varchar. You will get raw binary in the SQL output 205 //+------------------------------+ 206 //| abs(cast([1,2,3] as vecf32)) | 207 //+------------------------------+ 208 //| �? @ @@ | 209 //+------------------------------+ 210 row[i] = vector.GetArrayAt[float32](vec, rowIndex) 211 case types.T_array_float64: 212 row[i] = vector.GetArrayAt[float64](vec, rowIndex) 213 case types.T_date: 214 row[i] = vector.GetFixedAt[types.Date](vec, rowIndex) 215 case types.T_datetime: 216 scale := vec.GetType().Scale 217 row[i] = vector.GetFixedAt[types.Datetime](vec, rowIndex).String2(scale) 218 case types.T_time: 219 scale := vec.GetType().Scale 220 row[i] = vector.GetFixedAt[types.Time](vec, rowIndex).String2(scale) 221 case types.T_timestamp: 222 scale := vec.GetType().Scale 223 timeZone := ses.GetTimeZone() 224 row[i] = vector.GetFixedAt[types.Timestamp](vec, rowIndex).String2(timeZone, scale) 225 case types.T_decimal64: 226 scale := vec.GetType().Scale 227 row[i] = vector.GetFixedAt[types.Decimal64](vec, rowIndex).Format(scale) 228 case types.T_decimal128: 229 scale := vec.GetType().Scale 230 row[i] = vector.GetFixedAt[types.Decimal128](vec, rowIndex).Format(scale) 231 case types.T_uuid: 232 row[i] = vector.GetFixedAt[types.Uuid](vec, rowIndex).ToString() 233 case types.T_Rowid: 234 row[i] = vector.GetFixedAt[types.Rowid](vec, rowIndex) 235 case types.T_Blockid: 236 row[i] = vector.GetFixedAt[types.Blockid](vec, rowIndex) 237 case types.T_TS: 238 row[i] = vector.GetFixedAt[types.TS](vec, rowIndex) 239 case types.T_enum: 240 row[i] = copyBytes(vec.GetBytesAt(rowIndex), needCopyBytes) 241 default: 242 logError(ses, ses.GetDebugString(), 243 "Failed to extract row from vector, unsupported type", 244 zap.Int("typeID", int(vec.GetType().Oid))) 245 return moerr.NewInternalError(ctx, "extractRowFromVector : unsupported type %d", vec.GetType().Oid) 246 } 247 return nil 248 } 249 250 // fakeOutputQueue saves the data into the session. 251 type fakeOutputQueue struct { 252 mrs *MysqlResultSet 253 } 254 255 func newFakeOutputQueue(mrs *MysqlResultSet) outputPool { 256 return &fakeOutputQueue{mrs: mrs} 257 } 258 259 func (foq *fakeOutputQueue) resetLineStr() {} 260 261 func (foq *fakeOutputQueue) reset() {} 262 263 func (foq *fakeOutputQueue) getEmptyRow() ([]interface{}, error) { 264 row := make([]interface{}, foq.mrs.GetColumnCount()) 265 foq.mrs.AddRow(row) 266 return row, nil 267 } 268 269 func (foq *fakeOutputQueue) flush() error { 270 return nil 271 }