github.com/vedadiyan/sqlparser@v1.0.0/pkg/sqltypes/result.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package sqltypes 18 19 import ( 20 "crypto/sha256" 21 "fmt" 22 "reflect" 23 24 "google.golang.org/protobuf/proto" 25 26 querypb "github.com/vedadiyan/sqlparser/pkg/query" 27 ) 28 29 // Result represents a query result. 30 type Result struct { 31 Fields []*querypb.Field `json:"fields"` 32 RowsAffected uint64 `json:"rows_affected"` 33 InsertID uint64 `json:"insert_id"` 34 Rows []Row `json:"rows"` 35 SessionStateChanges string `json:"session_state_changes"` 36 StatusFlags uint16 `json:"status_flags"` 37 Info string `json:"info"` 38 } 39 40 //goland:noinspection GoUnusedConst 41 const ( 42 ServerStatusInTrans = 0x0001 43 ServerStatusAutocommit = 0x0002 44 ServerMoreResultsExists = 0x0008 45 ServerStatusNoGoodIndexUsed = 0x0010 46 ServerStatusNoIndexUsed = 0x0020 47 ServerStatusCursorExists = 0x0040 48 ServerStatusLastRowSent = 0x0080 49 ServerStatusDbDropped = 0x0100 50 ServerStatusNoBackslashEscapes = 0x0200 51 ServerStatusMetadataChanged = 0x0400 52 ServerQueryWasSlow = 0x0800 53 ServerPsOutParams = 0x1000 54 ServerStatusInTransReadonly = 0x2000 55 ServerSessionStateChanged = 0x4000 56 ) 57 58 // ResultStream is an interface for receiving Result. It is used for 59 // RPC interfaces. 60 type ResultStream interface { 61 // Recv returns the next result on the stream. 62 // It will return io.EOF if the stream ended. 63 Recv() (*Result, error) 64 } 65 66 // Repair fixes the type info in the rows 67 // to conform to the supplied field types. 68 func (result *Result) Repair(fields []*querypb.Field) { 69 // Usage of j is intentional. 70 for j, f := range fields { 71 for _, r := range result.Rows { 72 if r[j].typ != Null { 73 r[j].typ = f.Type 74 } 75 } 76 } 77 } 78 79 // ReplaceKeyspace replaces all the non-empty Database identifiers in the result 80 // set with the given keyspace name 81 func (result *Result) ReplaceKeyspace(keyspace string) { 82 // Change database name in mysql output to the keyspace name 83 for _, f := range result.Fields { 84 if f.Database != "" { 85 f.Database = keyspace 86 } 87 } 88 } 89 90 // Copy creates a deep copy of Result. 91 func (result *Result) Copy() *Result { 92 out := &Result{ 93 RowsAffected: result.RowsAffected, 94 InsertID: result.InsertID, 95 SessionStateChanges: result.SessionStateChanges, 96 StatusFlags: result.StatusFlags, 97 Info: result.Info, 98 } 99 if result.Fields != nil { 100 out.Fields = make([]*querypb.Field, len(result.Fields)) 101 for i, f := range result.Fields { 102 out.Fields[i] = proto.Clone(f).(*querypb.Field) 103 } 104 } 105 if result.Rows != nil { 106 out.Rows = make([][]Value, 0, len(result.Rows)) 107 for _, r := range result.Rows { 108 out.Rows = append(out.Rows, CopyRow(r)) 109 } 110 } 111 return out 112 } 113 114 // ShallowCopy creates a shallow copy of Result. 115 func (result *Result) ShallowCopy() *Result { 116 return &Result{ 117 Fields: result.Fields, 118 InsertID: result.InsertID, 119 RowsAffected: result.RowsAffected, 120 Info: result.Info, 121 SessionStateChanges: result.SessionStateChanges, 122 Rows: result.Rows, 123 } 124 } 125 126 // Metadata creates a shallow copy of Result without the rows useful 127 // for sending as a first packet in streaming results. 128 func (result *Result) Metadata() *Result { 129 return &Result{ 130 Fields: result.Fields, 131 InsertID: result.InsertID, 132 RowsAffected: result.RowsAffected, 133 Info: result.Info, 134 SessionStateChanges: result.SessionStateChanges, 135 } 136 } 137 138 // CopyRow makes a copy of the row. 139 func CopyRow(r []Value) []Value { 140 // The raw bytes of the values are supposed to be treated as read-only. 141 // So, there's no need to copy them. 142 out := make([]Value, len(r)) 143 copy(out, r) 144 return out 145 } 146 147 // Truncate returns a new Result with all the rows truncated 148 // to the specified number of columns. 149 func (result *Result) Truncate(l int) *Result { 150 if l == 0 { 151 return result 152 } 153 154 out := &Result{ 155 InsertID: result.InsertID, 156 RowsAffected: result.RowsAffected, 157 Info: result.Info, 158 SessionStateChanges: result.SessionStateChanges, 159 } 160 if result.Fields != nil { 161 out.Fields = result.Fields[:l] 162 } 163 if result.Rows != nil { 164 out.Rows = make([][]Value, 0, len(result.Rows)) 165 for _, r := range result.Rows { 166 out.Rows = append(out.Rows, r[:l]) 167 } 168 } 169 return out 170 } 171 172 // FieldsEqual compares two arrays of fields. 173 // reflect.DeepEqual shouldn't be used because of the protos. 174 func FieldsEqual(f1, f2 []*querypb.Field) bool { 175 if len(f1) != len(f2) { 176 return false 177 } 178 for i, f := range f1 { 179 if !proto.Equal(f, f2[i]) { 180 return false 181 } 182 } 183 return true 184 } 185 186 // Equal compares the Result with another one. 187 // reflect.DeepEqual shouldn't be used because of the protos. 188 func (result *Result) Equal(other *Result) bool { 189 // Check for nil cases 190 if result == nil { 191 return other == nil 192 } 193 if other == nil { 194 return false 195 } 196 197 // Compare Fields, RowsAffected, InsertID, Rows. 198 return FieldsEqual(result.Fields, other.Fields) && 199 result.RowsAffected == other.RowsAffected && 200 result.InsertID == other.InsertID && 201 reflect.DeepEqual(result.Rows, other.Rows) 202 } 203 204 // ResultsEqual compares two arrays of Result. 205 // reflect.DeepEqual shouldn't be used because of the protos. 206 func ResultsEqual(r1, r2 []Result) bool { 207 if len(r1) != len(r2) { 208 return false 209 } 210 for i, r := range r1 { 211 if !r.Equal(&r2[i]) { 212 return false 213 } 214 } 215 return true 216 } 217 218 // ResultsEqualUnordered compares two unordered arrays of Result. 219 func ResultsEqualUnordered(r1, r2 []Result) bool { 220 if len(r1) != len(r2) { 221 return false 222 } 223 224 // allRows is a hash map that contains a row hashed as a key and 225 // the number of occurrence as the value. we use this map to ensure 226 // equality between the two result sets. when analyzing r1, we 227 // increment each key's value by one for each row's occurrence, and 228 // then we decrement it by one each time we see the same key in r2. 229 // if one of the key's value is not equal to zero, then r1 and r2 do 230 // not match. 231 allRows := map[string]int{} 232 countRows := 0 233 for _, r := range r1 { 234 saveRowsAnalysis(r, allRows, &countRows, true) 235 } 236 for _, r := range r2 { 237 saveRowsAnalysis(r, allRows, &countRows, false) 238 } 239 if countRows != 0 { 240 return false 241 } 242 for _, i := range allRows { 243 if i != 0 { 244 return false 245 } 246 } 247 return true 248 } 249 250 func saveRowsAnalysis(r Result, allRows map[string]int, totalRows *int, increment bool) { 251 for _, row := range r.Rows { 252 newHash := hashCodeForRow(row) 253 if increment { 254 allRows[newHash]++ 255 } else { 256 allRows[newHash]-- 257 } 258 } 259 if increment { 260 *totalRows += int(r.RowsAffected) 261 } else { 262 *totalRows -= int(r.RowsAffected) 263 } 264 } 265 266 func hashCodeForRow(val []Value) string { 267 h := sha256.New() 268 h.Write([]byte(fmt.Sprintf("%v", val))) 269 270 return fmt.Sprintf("%x", h.Sum(nil)) 271 } 272 273 // MakeRowTrusted converts a *querypb.Row to []Value based on the types 274 // in fields. It does not sanity check the values against the type. 275 // Every place this function is called, a comment is needed that explains 276 // why it's justified. 277 func MakeRowTrusted(fields []*querypb.Field, row *querypb.Row) []Value { 278 sqlRow := make([]Value, len(row.Lengths)) 279 var offset int64 280 for i, length := range row.Lengths { 281 if length < 0 { 282 continue 283 } 284 sqlRow[i] = MakeTrusted(fields[i].Type, row.Values[offset:offset+length]) 285 offset += length 286 } 287 return sqlRow 288 } 289 290 // IncludeFieldsOrDefault normalizes the passed Execution Options. 291 // It returns the default value if options is nil. 292 func IncludeFieldsOrDefault(options *querypb.ExecuteOptions) querypb.ExecuteOptions_IncludedFields { 293 if options == nil { 294 return querypb.ExecuteOptions_TYPE_AND_NAME 295 } 296 297 return options.IncludedFields 298 } 299 300 // StripMetadata will return a new Result that has the same Rows, 301 // but the Field objects will have their non-critical metadata emptied. Note we don't 302 // proto.Copy each Field for performance reasons, but we only copy the 303 // individual fields. 304 func (result *Result) StripMetadata(incl querypb.ExecuteOptions_IncludedFields) *Result { 305 if incl == querypb.ExecuteOptions_ALL || len(result.Fields) == 0 { 306 return result 307 } 308 r := *result 309 r.Fields = make([]*querypb.Field, len(result.Fields)) 310 newFieldsArray := make([]querypb.Field, len(result.Fields)) 311 for i, f := range result.Fields { 312 r.Fields[i] = &newFieldsArray[i] 313 newFieldsArray[i].Type = f.Type 314 if incl == querypb.ExecuteOptions_TYPE_AND_NAME { 315 newFieldsArray[i].Name = f.Name 316 } 317 } 318 return &r 319 } 320 321 // AppendResult will combine the Results Objects of one result 322 // to another result.Note currently it doesn't handle cases like 323 // if two results have different fields.We will enhance this function. 324 func (result *Result) AppendResult(src *Result) { 325 if src.RowsAffected == 0 && len(src.Rows) == 0 && len(src.Fields) == 0 { 326 return 327 } 328 if result.Fields == nil { 329 result.Fields = src.Fields 330 } 331 result.RowsAffected += src.RowsAffected 332 if src.InsertID != 0 { 333 result.InsertID = src.InsertID 334 } 335 result.Rows = append(result.Rows, src.Rows...) 336 } 337 338 // Named returns a NamedResult based on this struct 339 func (result *Result) Named() *NamedResult { 340 return ToNamedResult(result) 341 } 342 343 // IsMoreResultsExists returns true if the status flag has SERVER_MORE_RESULTS_EXISTS set 344 func (result *Result) IsMoreResultsExists() bool { 345 return result.StatusFlags&ServerMoreResultsExists == ServerMoreResultsExists 346 } 347 348 // IsInTransaction returns true if the status flag has SERVER_STATUS_IN_TRANS set 349 func (result *Result) IsInTransaction() bool { 350 return result.StatusFlags&ServerStatusInTrans == ServerStatusInTrans 351 }