vitess.io/vitess@v0.16.2/go/sqltypes/bind_variables.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  	"bytes"
    21  	"errors"
    22  	"fmt"
    23  	"strconv"
    24  
    25  	"google.golang.org/protobuf/proto"
    26  
    27  	querypb "vitess.io/vitess/go/vt/proto/query"
    28  )
    29  
    30  type DecimalFloat float64
    31  
    32  var (
    33  	// BvSchemaName is bind variable to be sent down to vttablet for schema name.
    34  	BvSchemaName = "__vtschemaname"
    35  
    36  	// BvReplaceSchemaName is bind variable to be sent down to vttablet to replace schema name.
    37  	BvReplaceSchemaName = "__replacevtschemaname"
    38  
    39  	// NullBindVariable is a bindvar with NULL value.
    40  	NullBindVariable = &querypb.BindVariable{Type: querypb.Type_NULL_TYPE}
    41  )
    42  
    43  // ValueToProto converts Value to a *querypb.Value.
    44  func ValueToProto(v Value) *querypb.Value {
    45  	return &querypb.Value{Type: v.typ, Value: v.val}
    46  }
    47  
    48  // ProtoToValue converts a *querypb.Value to a Value.
    49  func ProtoToValue(v *querypb.Value) Value {
    50  	return MakeTrusted(v.Type, v.Value)
    51  }
    52  
    53  // BuildBindVariables builds a map[string]*querypb.BindVariable from a map[string]any
    54  func BuildBindVariables(in map[string]any) (map[string]*querypb.BindVariable, error) {
    55  	if len(in) == 0 {
    56  		return nil, nil
    57  	}
    58  
    59  	out := make(map[string]*querypb.BindVariable, len(in))
    60  	for k, v := range in {
    61  		bv, err := BuildBindVariable(v)
    62  		if err != nil {
    63  			return nil, fmt.Errorf("%s: %v", k, err)
    64  		}
    65  		out[k] = bv
    66  	}
    67  	return out, nil
    68  }
    69  
    70  // HexNumBindVariable converts bytes representing a hex number to a bind var.
    71  func HexNumBindVariable(v []byte) *querypb.BindVariable {
    72  	return ValueBindVariable(NewHexNum(v))
    73  }
    74  
    75  // HexValBindVariable converts bytes representing a hex encoded string to a bind var.
    76  func HexValBindVariable(v []byte) *querypb.BindVariable {
    77  	return ValueBindVariable(NewHexVal(v))
    78  }
    79  
    80  // BitNumBindVariable converts bytes representing a bit encoded string to a bind var.
    81  func BitNumBindVariable(v []byte) *querypb.BindVariable {
    82  	return ValueBindVariable(NewBitNum(v))
    83  }
    84  
    85  // Int8BindVariable converts an int8 to a bind var.
    86  func Int8BindVariable(v int8) *querypb.BindVariable {
    87  	return ValueBindVariable(NewInt8(v))
    88  }
    89  
    90  // Int32BindVariable converts an int32 to a bind var.
    91  func Int32BindVariable(v int32) *querypb.BindVariable {
    92  	return ValueBindVariable(NewInt32(v))
    93  }
    94  
    95  // Uint32BindVariable converts a uint32 to a bind var.
    96  func Uint32BindVariable(v uint32) *querypb.BindVariable {
    97  	return ValueBindVariable(NewUint32(v))
    98  }
    99  
   100  // BoolBindVariable converts an bool to a int64 bind var.
   101  func BoolBindVariable(v bool) *querypb.BindVariable {
   102  	if v {
   103  		return Int64BindVariable(1)
   104  	}
   105  	return Int64BindVariable(0)
   106  }
   107  
   108  // Int64BindVariable converts an int64 to a bind var.
   109  func Int64BindVariable(v int64) *querypb.BindVariable {
   110  	return ValueBindVariable(NewInt64(v))
   111  }
   112  
   113  // Uint64BindVariable converts a uint64 to a bind var.
   114  func Uint64BindVariable(v uint64) *querypb.BindVariable {
   115  	return ValueBindVariable(NewUint64(v))
   116  }
   117  
   118  // Float64BindVariable converts a float64 to a bind var.
   119  func Float64BindVariable(v float64) *querypb.BindVariable {
   120  	return ValueBindVariable(NewFloat64(v))
   121  }
   122  
   123  func DecimalBindVariable(v DecimalFloat) *querypb.BindVariable {
   124  	f := strconv.FormatFloat(float64(v), 'f', -1, 64)
   125  	return ValueBindVariable(NewDecimal(f))
   126  }
   127  
   128  // StringBindVariable converts a string to a bind var.
   129  func StringBindVariable(v string) *querypb.BindVariable {
   130  	return ValueBindVariable(NewVarChar(v))
   131  }
   132  
   133  // BytesBindVariable converts a []byte to a bind var.
   134  func BytesBindVariable(v []byte) *querypb.BindVariable {
   135  	return &querypb.BindVariable{Type: VarBinary, Value: v}
   136  }
   137  
   138  // ValueBindVariable converts a Value to a bind var.
   139  func ValueBindVariable(v Value) *querypb.BindVariable {
   140  	return &querypb.BindVariable{Type: v.typ, Value: v.val}
   141  }
   142  
   143  // BuildBindVariable builds a *querypb.BindVariable from a valid input type.
   144  func BuildBindVariable(v any) (*querypb.BindVariable, error) {
   145  	switch v := v.(type) {
   146  	case string:
   147  		return StringBindVariable(v), nil
   148  	case []byte:
   149  		return BytesBindVariable(v), nil
   150  	case bool:
   151  		if v {
   152  			return Int8BindVariable(1), nil
   153  		}
   154  		return Int8BindVariable(0), nil
   155  	case int:
   156  		return &querypb.BindVariable{
   157  			Type:  querypb.Type_INT64,
   158  			Value: strconv.AppendInt(nil, int64(v), 10),
   159  		}, nil
   160  	case int64:
   161  		return Int64BindVariable(v), nil
   162  	case uint64:
   163  		return Uint64BindVariable(v), nil
   164  	case DecimalFloat:
   165  		return DecimalBindVariable(v), nil
   166  	case float64:
   167  		return Float64BindVariable(v), nil
   168  	case nil:
   169  		return NullBindVariable, nil
   170  	case Value:
   171  		return ValueBindVariable(v), nil
   172  	case *querypb.BindVariable:
   173  		return v, nil
   174  	case []any:
   175  		bv := &querypb.BindVariable{
   176  			Type:   querypb.Type_TUPLE,
   177  			Values: make([]*querypb.Value, len(v)),
   178  		}
   179  		values := make([]querypb.Value, len(v))
   180  		for i, lv := range v {
   181  			lbv, err := BuildBindVariable(lv)
   182  			if err != nil {
   183  				return nil, err
   184  			}
   185  			values[i].Type = lbv.Type
   186  			values[i].Value = lbv.Value
   187  			bv.Values[i] = &values[i]
   188  		}
   189  		return bv, nil
   190  	case []string:
   191  		bv := &querypb.BindVariable{
   192  			Type:   querypb.Type_TUPLE,
   193  			Values: make([]*querypb.Value, len(v)),
   194  		}
   195  		values := make([]querypb.Value, len(v))
   196  		for i, lv := range v {
   197  			values[i].Type = querypb.Type_VARCHAR
   198  			values[i].Value = []byte(lv)
   199  			bv.Values[i] = &values[i]
   200  		}
   201  		return bv, nil
   202  	case [][]byte:
   203  		bv := &querypb.BindVariable{
   204  			Type:   querypb.Type_TUPLE,
   205  			Values: make([]*querypb.Value, len(v)),
   206  		}
   207  		values := make([]querypb.Value, len(v))
   208  		for i, lv := range v {
   209  			values[i].Type = querypb.Type_VARBINARY
   210  			values[i].Value = lv
   211  			bv.Values[i] = &values[i]
   212  		}
   213  		return bv, nil
   214  	case []int:
   215  		bv := &querypb.BindVariable{
   216  			Type:   querypb.Type_TUPLE,
   217  			Values: make([]*querypb.Value, len(v)),
   218  		}
   219  		values := make([]querypb.Value, len(v))
   220  		for i, lv := range v {
   221  			values[i].Type = querypb.Type_INT64
   222  			values[i].Value = strconv.AppendInt(nil, int64(lv), 10)
   223  			bv.Values[i] = &values[i]
   224  		}
   225  		return bv, nil
   226  	case []int64:
   227  		bv := &querypb.BindVariable{
   228  			Type:   querypb.Type_TUPLE,
   229  			Values: make([]*querypb.Value, len(v)),
   230  		}
   231  		values := make([]querypb.Value, len(v))
   232  		for i, lv := range v {
   233  			values[i].Type = querypb.Type_INT64
   234  			values[i].Value = strconv.AppendInt(nil, lv, 10)
   235  			bv.Values[i] = &values[i]
   236  		}
   237  		return bv, nil
   238  	case []uint64:
   239  		bv := &querypb.BindVariable{
   240  			Type:   querypb.Type_TUPLE,
   241  			Values: make([]*querypb.Value, len(v)),
   242  		}
   243  		values := make([]querypb.Value, len(v))
   244  		for i, lv := range v {
   245  			values[i].Type = querypb.Type_UINT64
   246  			values[i].Value = strconv.AppendUint(nil, lv, 10)
   247  			bv.Values[i] = &values[i]
   248  		}
   249  		return bv, nil
   250  	case []float64:
   251  		bv := &querypb.BindVariable{
   252  			Type:   querypb.Type_TUPLE,
   253  			Values: make([]*querypb.Value, len(v)),
   254  		}
   255  		values := make([]querypb.Value, len(v))
   256  		for i, lv := range v {
   257  			values[i].Type = querypb.Type_FLOAT64
   258  			values[i].Value = strconv.AppendFloat(nil, lv, 'g', -1, 64)
   259  			bv.Values[i] = &values[i]
   260  		}
   261  		return bv, nil
   262  	case []Value:
   263  		bv := &querypb.BindVariable{
   264  			Type:   querypb.Type_TUPLE,
   265  			Values: make([]*querypb.Value, len(v)),
   266  		}
   267  		values := make([]querypb.Value, len(v))
   268  		for i, lv := range v {
   269  			lbv, err := BuildBindVariable(lv)
   270  			if err != nil {
   271  				return nil, err
   272  			}
   273  			values[i].Type = lbv.Type
   274  			values[i].Value = lbv.Value
   275  			bv.Values[i] = &values[i]
   276  		}
   277  		return bv, nil
   278  	}
   279  	return nil, fmt.Errorf("type %T not supported as bind var: %v", v, v)
   280  }
   281  
   282  // ValidateBindVariables validates a map[string]*querypb.BindVariable.
   283  func ValidateBindVariables(bv map[string]*querypb.BindVariable) error {
   284  	for k, v := range bv {
   285  		if err := ValidateBindVariable(v); err != nil {
   286  			return fmt.Errorf("%s: %v", k, err)
   287  		}
   288  	}
   289  	return nil
   290  }
   291  
   292  // ValidateBindVariable returns an error if the bind variable has inconsistent
   293  // fields.
   294  func ValidateBindVariable(bv *querypb.BindVariable) error {
   295  	if bv == nil {
   296  		return errors.New("bind variable is nil")
   297  	}
   298  
   299  	if bv.Type == querypb.Type_TUPLE {
   300  		if len(bv.Values) == 0 {
   301  			return errors.New("empty tuple is not allowed")
   302  		}
   303  		for _, val := range bv.Values {
   304  			if val.Type == querypb.Type_TUPLE {
   305  				return errors.New("tuple not allowed inside another tuple")
   306  			}
   307  			if err := ValidateBindVariable(&querypb.BindVariable{Type: val.Type, Value: val.Value}); err != nil {
   308  				return err
   309  			}
   310  		}
   311  		return nil
   312  	}
   313  
   314  	// If NewValue succeeds, the value is valid.
   315  	_, err := NewValue(bv.Type, bv.Value)
   316  	return err
   317  }
   318  
   319  // BindVariableToValue converts a bind var into a Value.
   320  func BindVariableToValue(bv *querypb.BindVariable) (Value, error) {
   321  	if bv.Type == querypb.Type_TUPLE {
   322  		return NULL, errors.New("cannot convert a TUPLE bind var into a value")
   323  	}
   324  	return MakeTrusted(bv.Type, bv.Value), nil
   325  }
   326  
   327  // BindVariablesEqual compares two maps of bind variables.
   328  // For protobuf messages we have to use "proto.Equal".
   329  func BindVariablesEqual(x, y map[string]*querypb.BindVariable) bool {
   330  	return proto.Equal(&querypb.BoundQuery{BindVariables: x}, &querypb.BoundQuery{BindVariables: y})
   331  }
   332  
   333  // CopyBindVariables returns a shallow-copy of the given bindVariables map.
   334  func CopyBindVariables(bindVariables map[string]*querypb.BindVariable) map[string]*querypb.BindVariable {
   335  	result := make(map[string]*querypb.BindVariable, len(bindVariables))
   336  	for key, value := range bindVariables {
   337  		result[key] = value
   338  	}
   339  	return result
   340  }
   341  
   342  // FormatBindVariables returns a string representation of the
   343  // bind variables.
   344  //
   345  // If full is false, then large string or tuple values are truncated
   346  // to only print the lengths.
   347  //
   348  // If asJson is true, then the resulting string is a valid JSON
   349  // representation, otherwise it is the golang printed map representation.
   350  func FormatBindVariables(bindVariables map[string]*querypb.BindVariable, full, asJSON bool) string {
   351  	var out map[string]*querypb.BindVariable
   352  	if full {
   353  		out = bindVariables
   354  	} else {
   355  		// NOTE(szopa): I am getting rid of potentially large bind
   356  		// variables.
   357  		out = make(map[string]*querypb.BindVariable)
   358  		for k, v := range bindVariables {
   359  			if IsIntegral(v.Type) || IsFloat(v.Type) {
   360  				out[k] = v
   361  			} else if v.Type == querypb.Type_TUPLE {
   362  				out[k] = StringBindVariable(fmt.Sprintf("%v items", len(v.Values)))
   363  			} else {
   364  				out[k] = StringBindVariable(fmt.Sprintf("%v bytes", len(v.Value)))
   365  			}
   366  		}
   367  	}
   368  
   369  	if asJSON {
   370  		var buf bytes.Buffer
   371  		buf.WriteString("{")
   372  		first := true
   373  		for k, v := range out {
   374  			if !first {
   375  				buf.WriteString(", ")
   376  			} else {
   377  				first = false
   378  			}
   379  			if IsIntegral(v.Type) || IsFloat(v.Type) {
   380  				fmt.Fprintf(&buf, "%q: {\"type\": %q, \"value\": %v}", k, v.Type, string(v.Value))
   381  			} else {
   382  				fmt.Fprintf(&buf, "%q: {\"type\": %q, \"value\": %q}", k, v.Type, string(v.Value))
   383  			}
   384  		}
   385  		buf.WriteString("}")
   386  		return buf.String()
   387  	}
   388  
   389  	return fmt.Sprintf("%v", out)
   390  }