github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/sqlparse/sqltypes/value.go (about) 1 /* 2 Copyright 2017 Google Inc. 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 "errors" 23 "strconv" 24 25 "github.com/bingoohuang/gg/pkg/sqlparse/bytes2" 26 "github.com/bingoohuang/gg/pkg/sqlparse/hack" 27 ) 28 29 var ( 30 // NULL represents the NULL value. 31 NULL = Value{} 32 // DontEscape tells you if a character should not be escaped. 33 DontEscape = byte(255) 34 nullstr = []byte("null") 35 ) 36 37 // BinWriter interface is used for encoding values. 38 // Types like bytes.Buffer conform to this interface. 39 // We expect the writer objects to be in-memory buffers. 40 // So, we don't expect the write operations to fail. 41 type BinWriter interface { 42 Write([]byte) (int, error) 43 } 44 45 // Value can store any SQL value. If the value represents 46 // an integral type, the bytes are always stored as a cannonical 47 // representation that matches how MySQL returns such values. 48 type Value struct { 49 typ Type 50 val []byte 51 } 52 53 // MakeTrusted makes a new Value based on the type. 54 // If the value is an integral, then val must be in its cannonical 55 // form. This function should only be used if you know the value 56 // and type conform to the rules. Every place this function is 57 // called, a comment is needed that explains why it's justified. 58 // Functions within this package are exempt. 59 func MakeTrusted(typ Type, val []byte) Value { 60 if typ == Null { 61 return NULL 62 } 63 return Value{typ: typ, val: val} 64 } 65 66 // MakeString makes a VarBinary Value. 67 func MakeString(val []byte) Value { 68 return MakeTrusted(VarBinary, val) 69 } 70 71 // Type returns the type of Value. 72 func (v Value) Type() Type { 73 return v.typ 74 } 75 76 // Raw returns the raw bytes. All types are currently implemented as []byte. 77 // You should avoid using this function. If you do, you should treat the 78 // bytes as read-only. 79 func (v Value) Raw() []byte { 80 return v.val 81 } 82 83 // Bytes returns a copy of the raw data. All types are currently implemented as []byte. 84 // Use this function instead of Raw if you can't be sure about maintaining the read-only 85 // requirements of the bytes. 86 func (v Value) Bytes() []byte { 87 out := make([]byte, len(v.val)) 88 copy(out, v.val) 89 return out 90 } 91 92 // Len returns the length. 93 func (v Value) Len() int { 94 return len(v.val) 95 } 96 97 // String returns the raw value as a string. 98 func (v Value) String() string { 99 return hack.String(v.val) 100 } 101 102 // ToNative converts Value to a native go type. 103 // This does not work for sqltypes.Tuple. The function 104 // panics if there are inconsistencies. 105 func (v Value) ToNative() interface{} { 106 var out interface{} 107 var err error 108 switch { 109 case v.typ == Null: 110 // no-op 111 case IsSigned(v.typ): 112 out, err = v.ParseInt64() 113 case IsUnsigned(v.typ): 114 out, err = v.ParseUint64() 115 case IsFloat(v.typ): 116 out, err = v.ParseFloat64() 117 case v.typ == Tuple: 118 err = errors.New("unexpected tuple") 119 default: 120 out = v.val 121 } 122 if err != nil { 123 panic(err) 124 } 125 return out 126 } 127 128 // ParseInt64 will parse a Value into an int64. It does 129 // not check the type. 130 // TODO(sougou): deprecate this function in favor of a 131 // more type-aware implemention in arithmetic. 132 func (v Value) ParseInt64() (val int64, err error) { 133 return strconv.ParseInt(v.String(), 10, 64) 134 } 135 136 // ParseUint64 will parse a Value into a uint64. It does 137 // not check the type. 138 // TODO(sougou): deprecate this function in favor of a 139 // more type-aware implemention in arithmetic. 140 func (v Value) ParseUint64() (val uint64, err error) { 141 return strconv.ParseUint(v.String(), 10, 64) 142 } 143 144 // ParseFloat64 will parse a Value into an float64. It does 145 // not check the type. 146 // TODO(sougou): deprecate this function in favor of a 147 // more type-aware implemention in arithmetic. 148 func (v Value) ParseFloat64() (val float64, err error) { 149 return strconv.ParseFloat(v.String(), 64) 150 } 151 152 // EncodeSQL encodes the value into an SQL statement. Can be binary. 153 func (v Value) EncodeSQL(b BinWriter) { 154 // ToNative panics if v is invalid. 155 _ = v.ToNative() 156 switch { 157 case v.typ == Null: 158 b.Write(nullstr) 159 case IsQuoted(v.typ): 160 encodeBytesSQL(v.val, b) 161 default: 162 b.Write(v.val) 163 } 164 } 165 166 // EncodeASCII encodes the value using 7-bit clean ascii bytes. 167 func (v Value) EncodeASCII(b BinWriter) { 168 // ToNative panics if v is invalid. 169 _ = v.ToNative() 170 switch { 171 case v.typ == Null: 172 b.Write(nullstr) 173 case IsQuoted(v.typ): 174 encodeBytesASCII(v.val, b) 175 default: 176 b.Write(v.val) 177 } 178 } 179 180 func encodeBytesSQL(val []byte, b BinWriter) { 181 buf := &bytes2.Buffer{} 182 buf.WriteByte('\'') 183 for _, ch := range val { 184 if encodedChar := SQLEncodeMap[ch]; encodedChar == DontEscape { 185 buf.WriteByte(ch) 186 } else { 187 buf.WriteByte('\\') 188 buf.WriteByte(encodedChar) 189 } 190 } 191 buf.WriteByte('\'') 192 b.Write(buf.Bytes()) 193 } 194 195 func encodeBytesASCII(val []byte, b BinWriter) { 196 buf := &bytes2.Buffer{} 197 buf.WriteByte('\'') 198 encoder := base64.NewEncoder(base64.StdEncoding, buf) 199 encoder.Write(val) 200 encoder.Close() 201 buf.WriteByte('\'') 202 b.Write(buf.Bytes()) 203 } 204 205 // SQLEncodeMap specifies how to escape binary data with '\'. 206 // Complies to http://dev.mysql.com/doc/refman/5.1/en/string-syntax.html 207 var SQLEncodeMap [256]byte 208 209 // SQLDecodeMap is the reverse of SQLEncodeMap 210 var SQLDecodeMap [256]byte 211 212 var encodeRef = map[byte]byte{ 213 '\x00': '0', 214 '\'': '\'', 215 //'"': '"', 216 //'\b': 'b', 217 //'\n': 'n', 218 //'\r': 'r', 219 //'\t': 't', 220 26: 'Z', // ctl-Z 221 '\\': '\\', 222 } 223 224 func init() { 225 for i := range SQLEncodeMap { 226 SQLEncodeMap[i] = DontEscape 227 SQLDecodeMap[i] = DontEscape 228 } 229 for i := range SQLEncodeMap { 230 if to, ok := encodeRef[byte(i)]; ok { 231 SQLEncodeMap[byte(i)] = to 232 SQLDecodeMap[to] = byte(i) 233 } 234 } 235 }