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  }