github.com/team-ide/go-dialect@v1.9.20/vitess/sqltypes/value.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 implements interfaces and types that represent SQL values. 18 package sqltypes 19 20 import ( 21 "encoding/base64" 22 "encoding/hex" 23 "encoding/json" 24 "errors" 25 "fmt" 26 "strconv" 27 "strings" 28 29 "github.com/team-ide/go-dialect/vitess/bytes2" 30 "github.com/team-ide/go-dialect/vitess/hack" 31 32 querypb "github.com/team-ide/go-dialect/vitess/query" 33 "github.com/team-ide/go-dialect/vitess/vterrors" 34 "github.com/team-ide/go-dialect/vitess/vtrpc" 35 vtrpcpb "github.com/team-ide/go-dialect/vitess/vtrpc" 36 ) 37 38 var ( 39 // NULL represents the NULL value. 40 NULL = Value{} 41 42 // DontEscape tells you if a character should not be escaped. 43 DontEscape = byte(255) 44 45 nullstr = []byte("null") 46 47 // ErrIncompatibleTypeCast indicates a casting problem 48 ErrIncompatibleTypeCast = errors.New("Cannot convert value to desired type") 49 ) 50 51 // BinWriter interface is used for encoding values. 52 // Types like bytes.Buffer conform to this interface. 53 // We expect the writer objects to be in-memory buffers. 54 // So, we don't expect the write operations to fail. 55 type BinWriter interface { 56 Write([]byte) (int, error) 57 } 58 59 // Value can store any SQL value. If the value represents 60 // an integral type, the bytes are always stored as a canonical 61 // representation that matches how MySQL returns such values. 62 type Value struct { 63 typ querypb.Type 64 val []byte 65 } 66 67 // NewValue builds a Value using typ and val. If the value and typ 68 // don't match, it returns an error. 69 func NewValue(typ querypb.Type, val []byte) (v Value, err error) { 70 switch { 71 case IsSigned(typ): 72 if _, err := strconv.ParseInt(string(val), 0, 64); err != nil { 73 return NULL, err 74 } 75 return MakeTrusted(typ, val), nil 76 case IsUnsigned(typ): 77 if _, err := strconv.ParseUint(string(val), 0, 64); err != nil { 78 return NULL, err 79 } 80 return MakeTrusted(typ, val), nil 81 case IsFloat(typ) || typ == Decimal: 82 if _, err := strconv.ParseFloat(string(val), 64); err != nil { 83 return NULL, err 84 } 85 return MakeTrusted(typ, val), nil 86 case IsQuoted(typ) || typ == Bit || typ == HexNum || typ == HexVal || typ == Null: 87 return MakeTrusted(typ, val), nil 88 } 89 // All other types are unsafe or invalid. 90 return NULL, fmt.Errorf("invalid type specified for MakeValue: %v", typ) 91 } 92 93 // MakeTrusted makes a new Value based on the type. 94 // This function should only be used if you know the value 95 // and type conform to the rules. Every place this function is 96 // called, a comment is needed that explains why it's justified. 97 // Exceptions: The current package and mysql package do not need 98 // comments. Other packages can also use the function to create 99 // VarBinary or VarChar values. 100 func MakeTrusted(typ querypb.Type, val []byte) Value { 101 102 if typ == Null { 103 return NULL 104 } 105 106 return Value{typ: typ, val: val} 107 } 108 109 // NewHexNum builds an Hex Value. 110 func NewHexNum(v []byte) Value { 111 return MakeTrusted(HexNum, v) 112 } 113 114 // NewHexVal builds a HexVal Value. 115 func NewHexVal(v []byte) Value { 116 return MakeTrusted(HexVal, v) 117 } 118 119 // NewInt64 builds an Int64 Value. 120 func NewInt64(v int64) Value { 121 return MakeTrusted(Int64, strconv.AppendInt(nil, v, 10)) 122 } 123 124 // NewInt8 builds an Int8 Value. 125 func NewInt8(v int8) Value { 126 return MakeTrusted(Int8, strconv.AppendInt(nil, int64(v), 10)) 127 } 128 129 // NewInt32 builds an Int64 Value. 130 func NewInt32(v int32) Value { 131 return MakeTrusted(Int32, strconv.AppendInt(nil, int64(v), 10)) 132 } 133 134 // NewUint64 builds an Uint64 Value. 135 func NewUint64(v uint64) Value { 136 return MakeTrusted(Uint64, strconv.AppendUint(nil, v, 10)) 137 } 138 139 // NewUint32 builds an Uint32 Value. 140 func NewUint32(v uint32) Value { 141 return MakeTrusted(Uint32, strconv.AppendUint(nil, uint64(v), 10)) 142 } 143 144 // NewFloat64 builds an Float64 Value. 145 func NewFloat64(v float64) Value { 146 return MakeTrusted(Float64, strconv.AppendFloat(nil, v, 'g', -1, 64)) 147 } 148 149 // NewVarChar builds a VarChar Value. 150 func NewVarChar(v string) Value { 151 return MakeTrusted(VarChar, []byte(v)) 152 } 153 154 // NewVarBinary builds a VarBinary Value. 155 // The input is a string because it's the most common use case. 156 func NewVarBinary(v string) Value { 157 return MakeTrusted(VarBinary, []byte(v)) 158 } 159 160 // NewDate builds a Date value. 161 func NewDate(v string) Value { 162 return MakeTrusted(Date, []byte(v)) 163 } 164 165 // NewTime builds a Time value. 166 func NewTime(v string) Value { 167 return MakeTrusted(Time, []byte(v)) 168 } 169 170 // NewTimestamp builds a Timestamp value. 171 func NewTimestamp(v string) Value { 172 return MakeTrusted(Timestamp, []byte(v)) 173 } 174 175 // NewDatetime builds a Datetime value. 176 func NewDatetime(v string) Value { 177 return MakeTrusted(Datetime, []byte(v)) 178 } 179 180 // NewDecimal builds a Decimal value. 181 func NewDecimal(v string) Value { 182 return MakeTrusted(Decimal, []byte(v)) 183 } 184 185 // NewIntegral builds an integral type from a string representation. 186 // The type will be Int64 or Uint64. Int64 will be preferred where possible. 187 func NewIntegral(val string) (n Value, err error) { 188 signed, err := strconv.ParseInt(val, 0, 64) 189 if err == nil { 190 return MakeTrusted(Int64, strconv.AppendInt(nil, signed, 10)), nil 191 } 192 unsigned, err := strconv.ParseUint(val, 0, 64) 193 if err != nil { 194 return Value{}, err 195 } 196 return MakeTrusted(Uint64, strconv.AppendUint(nil, unsigned, 10)), nil 197 } 198 199 // InterfaceToValue builds a value from a go type. 200 // Supported types are nil, int64, uint64, float64, 201 // string and []byte. 202 // This function is deprecated. Use the type-specific 203 // functions instead. 204 func InterfaceToValue(goval interface{}) (Value, error) { 205 switch goval := goval.(type) { 206 case nil: 207 return NULL, nil 208 case []byte: 209 return MakeTrusted(VarBinary, goval), nil 210 case int64: 211 return NewInt64(goval), nil 212 case uint64: 213 return NewUint64(goval), nil 214 case float64: 215 return NewFloat64(goval), nil 216 case string: 217 return NewVarChar(goval), nil 218 default: 219 return NULL, fmt.Errorf("unexpected type %T: %v", goval, goval) 220 } 221 } 222 223 // Type returns the type of Value. 224 func (v Value) Type() querypb.Type { 225 return v.typ 226 } 227 228 // Raw returns the internal representation of the value. For newer types, 229 // this may not match MySQL's representation. 230 func (v Value) Raw() []byte { 231 return v.val 232 } 233 234 // RawStr returns the internal representation of the value as a string instead 235 // of a byte slice. This is equivalent to calling `string(v.Raw())` but does 236 // not allocate. 237 func (v Value) RawStr() string { 238 return hack.String(v.val) 239 } 240 241 // ToBytes returns the value as MySQL would return it as []byte. 242 // In contrast, Raw returns the internal representation of the Value, which may not 243 // match MySQL's representation for hex encoded binary data or newer types. 244 // If the value is not convertible like in the case of Expression, it returns an error. 245 func (v Value) ToBytes() ([]byte, error) { 246 if v.typ == Expression { 247 return nil, vterrors.New(vtrpcpb.Code_INVALID_ARGUMENT, "expression cannot be converted to bytes") 248 } 249 if v.typ == HexVal { 250 dv, err := v.decodeHexVal() 251 return dv, err 252 } 253 if v.typ == HexNum { 254 dv, err := v.decodeHexNum() 255 return dv, err 256 } 257 return v.val, nil 258 } 259 260 // Len returns the length. 261 func (v Value) Len() int { 262 return len(v.val) 263 } 264 265 // ToInt64 returns the value as MySQL would return it as a int64. 266 func (v Value) ToInt64() (int64, error) { 267 if !v.IsIntegral() { 268 return 0, ErrIncompatibleTypeCast 269 } 270 271 return strconv.ParseInt(v.ToString(), 10, 64) 272 } 273 274 // ToFloat64 returns the value as MySQL would return it as a float64. 275 func (v Value) ToFloat64() (float64, error) { 276 if !IsNumber(v.typ) { 277 return 0, ErrIncompatibleTypeCast 278 } 279 280 return strconv.ParseFloat(v.ToString(), 64) 281 } 282 283 // ToUint64 returns the value as MySQL would return it as a uint64. 284 func (v Value) ToUint64() (uint64, error) { 285 if !v.IsIntegral() { 286 return 0, ErrIncompatibleTypeCast 287 } 288 289 return strconv.ParseUint(v.ToString(), 10, 64) 290 } 291 292 // ToBool returns the value as a bool value 293 func (v Value) ToBool() (bool, error) { 294 i, err := v.ToInt64() 295 if err != nil { 296 return false, err 297 } 298 switch i { 299 case 0: 300 return false, nil 301 case 1: 302 return true, nil 303 } 304 return false, ErrIncompatibleTypeCast 305 } 306 307 // ToString returns the value as MySQL would return it as string. 308 // If the value is not convertible like in the case of Expression, it returns nil. 309 func (v Value) ToString() string { 310 if v.typ == Expression { 311 return "" 312 } 313 return hack.String(v.val) 314 } 315 316 // String returns a printable version of the value. 317 func (v Value) String() string { 318 if v.typ == Null { 319 return "NULL" 320 } 321 if v.IsQuoted() || v.typ == Bit { 322 return fmt.Sprintf("%v(%q)", v.typ, v.val) 323 } 324 return fmt.Sprintf("%v(%s)", v.typ, v.val) 325 } 326 327 // EncodeSQL encodes the value into an SQL statement. Can be binary. 328 func (v Value) EncodeSQL(b BinWriter) { 329 switch { 330 case v.typ == Null: 331 b.Write(nullstr) 332 case v.IsQuoted(): 333 encodeBytesSQL(v.val, b) 334 case v.typ == Bit: 335 encodeBytesSQLBits(v.val, b) 336 default: 337 b.Write(v.val) 338 } 339 } 340 341 // EncodeSQLStringBuilder is identical to EncodeSQL but it takes a strings.Builder 342 // as its writer, so it can be inlined for performance. 343 func (v Value) EncodeSQLStringBuilder(b *strings.Builder) { 344 switch { 345 case v.typ == Null: 346 b.Write(nullstr) 347 case v.IsQuoted(): 348 encodeBytesSQLStringBuilder(v.val, b) 349 case v.typ == Bit: 350 encodeBytesSQLBits(v.val, b) 351 default: 352 b.Write(v.val) 353 } 354 } 355 356 // EncodeSQLBytes2 is identical to EncodeSQL but it takes a bytes2.Buffer 357 // as its writer, so it can be inlined for performance. 358 func (v Value) EncodeSQLBytes2(b *bytes2.Buffer) { 359 switch { 360 case v.typ == Null: 361 b.Write(nullstr) 362 case v.IsQuoted(): 363 encodeBytesSQLBytes2(v.val, b) 364 case v.typ == Bit: 365 encodeBytesSQLBits(v.val, b) 366 default: 367 b.Write(v.val) 368 } 369 } 370 371 // EncodeASCII encodes the value using 7-bit clean ascii bytes. 372 func (v Value) EncodeASCII(b BinWriter) { 373 switch { 374 case v.typ == Null: 375 b.Write(nullstr) 376 case v.IsQuoted() || v.typ == Bit: 377 encodeBytesASCII(v.val, b) 378 default: 379 b.Write(v.val) 380 } 381 } 382 383 // IsNull returns true if Value is null. 384 func (v Value) IsNull() bool { 385 return v.typ == Null 386 } 387 388 // IsIntegral returns true if Value is an integral. 389 func (v Value) IsIntegral() bool { 390 return IsIntegral(v.typ) 391 } 392 393 // IsSigned returns true if Value is a signed integral. 394 func (v Value) IsSigned() bool { 395 return IsSigned(v.typ) 396 } 397 398 // IsUnsigned returns true if Value is an unsigned integral. 399 func (v Value) IsUnsigned() bool { 400 return IsUnsigned(v.typ) 401 } 402 403 // IsFloat returns true if Value is a float. 404 func (v Value) IsFloat() bool { 405 return IsFloat(v.typ) 406 } 407 408 // IsQuoted returns true if Value must be SQL-quoted. 409 func (v Value) IsQuoted() bool { 410 return IsQuoted(v.typ) 411 } 412 413 // IsText returns true if Value is a collatable text. 414 func (v Value) IsText() bool { 415 return IsText(v.typ) 416 } 417 418 // IsBinary returns true if Value is binary. 419 func (v Value) IsBinary() bool { 420 return IsBinary(v.typ) 421 } 422 423 // IsDateTime returns true if Value is datetime. 424 func (v Value) IsDateTime() bool { 425 dt := int(querypb.Type_DATETIME) 426 return int(v.typ)&dt == dt 427 } 428 429 // IsComparable returns true if the Value is null safe comparable without collation information. 430 func (v *Value) IsComparable() bool { 431 if v.typ == Null || IsNumber(v.typ) || IsBinary(v.typ) { 432 return true 433 } 434 switch v.typ { 435 case Timestamp, Date, Time, Datetime, Enum, Set, TypeJSON, Bit: 436 return true 437 } 438 return false 439 } 440 441 // MarshalJSON should only be used for testing. 442 // It's not a complete implementation. 443 func (v Value) MarshalJSON() ([]byte, error) { 444 switch { 445 case v.IsQuoted() || v.typ == Bit: 446 return json.Marshal(v.ToString()) 447 case v.typ == Null: 448 return nullstr, nil 449 } 450 return v.val, nil 451 } 452 453 // UnmarshalJSON should only be used for testing. 454 // It's not a complete implementation. 455 func (v *Value) UnmarshalJSON(b []byte) error { 456 if len(b) == 0 { 457 return fmt.Errorf("error unmarshaling empty bytes") 458 } 459 var val interface{} 460 var err error 461 switch b[0] { 462 case '-': 463 var ival int64 464 err = json.Unmarshal(b, &ival) 465 val = ival 466 case '"': 467 var bval []byte 468 err = json.Unmarshal(b, &bval) 469 val = bval 470 case 'n': // null 471 err = json.Unmarshal(b, &val) 472 default: 473 var uval uint64 474 err = json.Unmarshal(b, &uval) 475 val = uval 476 } 477 if err != nil { 478 return err 479 } 480 *v, err = InterfaceToValue(val) 481 return err 482 } 483 484 // decodeHexVal decodes the SQL hex value of the form x'A1' into a byte 485 // array matching what MySQL would return when querying the column where 486 // an INSERT was performed with x'A1' having been specified as a value 487 func (v *Value) decodeHexVal() ([]byte, error) { 488 if len(v.val) < 3 || (v.val[0] != 'x' && v.val[0] != 'X') || v.val[1] != '\'' || v.val[len(v.val)-1] != '\'' { 489 return nil, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "invalid hex value: %v", v.val) 490 } 491 hexBytes := v.val[2 : len(v.val)-1] 492 decodedHexBytes, err := hex.DecodeString(string(hexBytes)) 493 if err != nil { 494 return nil, err 495 } 496 return decodedHexBytes, nil 497 } 498 499 // decodeHexNum decodes the SQL hex value of the form 0xA1 into a byte 500 // array matching what MySQL would return when querying the column where 501 // an INSERT was performed with 0xA1 having been specified as a value 502 func (v *Value) decodeHexNum() ([]byte, error) { 503 if len(v.val) < 3 || v.val[0] != '0' || v.val[1] != 'x' { 504 return nil, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "invalid hex number: %v", v.val) 505 } 506 hexBytes := v.val[2:] 507 decodedHexBytes, err := hex.DecodeString(string(hexBytes)) 508 if err != nil { 509 return nil, err 510 } 511 return decodedHexBytes, nil 512 } 513 514 func encodeBytesSQL(val []byte, b BinWriter) { 515 buf := &bytes2.Buffer{} 516 encodeBytesSQLBytes2(val, buf) 517 b.Write(buf.Bytes()) 518 } 519 520 func encodeBytesSQLBytes2(val []byte, buf *bytes2.Buffer) { 521 buf.WriteByte('\'') 522 for _, ch := range val { 523 if encodedChar := SQLEncodeMap[ch]; encodedChar == DontEscape { 524 buf.WriteByte(ch) 525 } else { 526 buf.WriteByte('\\') 527 buf.WriteByte(encodedChar) 528 } 529 } 530 buf.WriteByte('\'') 531 } 532 533 func encodeBytesSQLStringBuilder(val []byte, buf *strings.Builder) { 534 buf.WriteByte('\'') 535 for _, ch := range val { 536 if encodedChar := SQLEncodeMap[ch]; encodedChar == DontEscape { 537 buf.WriteByte(ch) 538 } else { 539 buf.WriteByte('\\') 540 buf.WriteByte(encodedChar) 541 } 542 } 543 buf.WriteByte('\'') 544 } 545 546 // BufEncodeStringSQL encodes the string into a strings.Builder 547 func BufEncodeStringSQL(buf *strings.Builder, val string) { 548 buf.WriteByte('\'') 549 for _, ch := range val { 550 if ch > 255 { 551 buf.WriteRune(ch) 552 continue 553 } 554 if encodedChar := SQLEncodeMap[ch]; encodedChar == DontEscape { 555 buf.WriteRune(ch) 556 } else { 557 buf.WriteByte('\\') 558 buf.WriteByte(encodedChar) 559 } 560 } 561 buf.WriteByte('\'') 562 } 563 564 // EncodeStringSQL encodes the string as a SQL string. 565 func EncodeStringSQL(val string) string { 566 var buf strings.Builder 567 BufEncodeStringSQL(&buf, val) 568 return buf.String() 569 } 570 571 func encodeBytesSQLBits(val []byte, b BinWriter) { 572 fmt.Fprint(b, "b'") 573 for _, ch := range val { 574 fmt.Fprintf(b, "%08b", ch) 575 } 576 fmt.Fprint(b, "'") 577 } 578 579 func encodeBytesASCII(val []byte, b BinWriter) { 580 buf := &bytes2.Buffer{} 581 buf.WriteByte('\'') 582 encoder := base64.NewEncoder(base64.StdEncoding, buf) 583 encoder.Write(val) 584 encoder.Close() 585 buf.WriteByte('\'') 586 b.Write(buf.Bytes()) 587 } 588 589 // SQLEncodeMap specifies how to escape binary data with '\'. 590 // Complies to http://dev.mysql.com/doc/refman/5.1/en/string-syntax.html 591 var SQLEncodeMap [256]byte 592 593 // SQLDecodeMap is the reverse of SQLEncodeMap 594 var SQLDecodeMap [256]byte 595 596 var encodeRef = map[byte]byte{ 597 '\x00': '0', 598 '\'': '\'', 599 '"': '"', 600 '\b': 'b', 601 '\n': 'n', 602 '\r': 'r', 603 '\t': 't', 604 26: 'Z', // ctl-Z 605 '\\': '\\', 606 } 607 608 func init() { 609 for i := range SQLEncodeMap { 610 SQLEncodeMap[i] = DontEscape 611 SQLDecodeMap[i] = DontEscape 612 } 613 for i := range SQLEncodeMap { 614 if to, ok := encodeRef[byte(i)]; ok { 615 SQLEncodeMap[byte(i)] = to 616 SQLDecodeMap[to] = byte(i) 617 } 618 } 619 }