append.go (8569B)
1 package pgdialect 2 3 import ( 4 "database/sql/driver" 5 "encoding/hex" 6 "fmt" 7 "reflect" 8 "strconv" 9 "time" 10 "unicode/utf8" 11 12 "github.com/uptrace/bun/dialect" 13 "github.com/uptrace/bun/schema" 14 ) 15 16 var ( 17 driverValuerType = reflect.TypeOf((*driver.Valuer)(nil)).Elem() 18 19 stringType = reflect.TypeOf((*string)(nil)).Elem() 20 sliceStringType = reflect.TypeOf([]string(nil)) 21 22 intType = reflect.TypeOf((*int)(nil)).Elem() 23 sliceIntType = reflect.TypeOf([]int(nil)) 24 25 int64Type = reflect.TypeOf((*int64)(nil)).Elem() 26 sliceInt64Type = reflect.TypeOf([]int64(nil)) 27 28 float64Type = reflect.TypeOf((*float64)(nil)).Elem() 29 sliceFloat64Type = reflect.TypeOf([]float64(nil)) 30 31 timeType = reflect.TypeOf((*time.Time)(nil)).Elem() 32 sliceTimeType = reflect.TypeOf([]time.Time(nil)) 33 ) 34 35 func arrayAppend(fmter schema.Formatter, b []byte, v interface{}) []byte { 36 switch v := v.(type) { 37 case int64: 38 return strconv.AppendInt(b, v, 10) 39 case float64: 40 return dialect.AppendFloat64(b, v) 41 case bool: 42 return dialect.AppendBool(b, v) 43 case []byte: 44 return arrayAppendBytes(b, v) 45 case string: 46 return arrayAppendString(b, v) 47 case time.Time: 48 return fmter.Dialect().AppendTime(b, v) 49 default: 50 err := fmt.Errorf("pgdialect: can't append %T", v) 51 return dialect.AppendError(b, err) 52 } 53 } 54 55 func arrayAppendStringValue(fmter schema.Formatter, b []byte, v reflect.Value) []byte { 56 return arrayAppendString(b, v.String()) 57 } 58 59 func arrayAppendBytesValue(fmter schema.Formatter, b []byte, v reflect.Value) []byte { 60 return arrayAppendBytes(b, v.Bytes()) 61 } 62 63 func arrayAppendDriverValue(fmter schema.Formatter, b []byte, v reflect.Value) []byte { 64 iface, err := v.Interface().(driver.Valuer).Value() 65 if err != nil { 66 return dialect.AppendError(b, err) 67 } 68 return arrayAppend(fmter, b, iface) 69 } 70 71 //------------------------------------------------------------------------------ 72 73 func (d *Dialect) arrayAppender(typ reflect.Type) schema.AppenderFunc { 74 kind := typ.Kind() 75 76 switch kind { 77 case reflect.Ptr: 78 if fn := d.arrayAppender(typ.Elem()); fn != nil { 79 return schema.PtrAppender(fn) 80 } 81 case reflect.Slice, reflect.Array: 82 // ok: 83 default: 84 return nil 85 } 86 87 elemType := typ.Elem() 88 89 if kind == reflect.Slice { 90 switch elemType { 91 case stringType: 92 return appendStringSliceValue 93 case intType: 94 return appendIntSliceValue 95 case int64Type: 96 return appendInt64SliceValue 97 case float64Type: 98 return appendFloat64SliceValue 99 case timeType: 100 return appendTimeSliceValue 101 } 102 } 103 104 appendElem := d.arrayElemAppender(elemType) 105 if appendElem == nil { 106 panic(fmt.Errorf("pgdialect: %s is not supported", typ)) 107 } 108 109 return func(fmter schema.Formatter, b []byte, v reflect.Value) []byte { 110 kind := v.Kind() 111 switch kind { 112 case reflect.Ptr, reflect.Slice: 113 if v.IsNil() { 114 return dialect.AppendNull(b) 115 } 116 } 117 118 if kind == reflect.Ptr { 119 v = v.Elem() 120 } 121 122 b = append(b, '\'') 123 124 b = append(b, '{') 125 ln := v.Len() 126 for i := 0; i < ln; i++ { 127 elem := v.Index(i) 128 b = appendElem(fmter, b, elem) 129 b = append(b, ',') 130 } 131 if v.Len() > 0 { 132 b[len(b)-1] = '}' // Replace trailing comma. 133 } else { 134 b = append(b, '}') 135 } 136 137 b = append(b, '\'') 138 139 return b 140 } 141 } 142 143 func (d *Dialect) arrayElemAppender(typ reflect.Type) schema.AppenderFunc { 144 if typ.Implements(driverValuerType) { 145 return arrayAppendDriverValue 146 } 147 switch typ.Kind() { 148 case reflect.String: 149 return arrayAppendStringValue 150 case reflect.Slice: 151 if typ.Elem().Kind() == reflect.Uint8 { 152 return arrayAppendBytesValue 153 } 154 } 155 return schema.Appender(d, typ) 156 } 157 158 func appendStringSliceValue(fmter schema.Formatter, b []byte, v reflect.Value) []byte { 159 ss := v.Convert(sliceStringType).Interface().([]string) 160 return appendStringSlice(b, ss) 161 } 162 163 func appendStringSlice(b []byte, ss []string) []byte { 164 if ss == nil { 165 return dialect.AppendNull(b) 166 } 167 168 b = append(b, '\'') 169 170 b = append(b, '{') 171 for _, s := range ss { 172 b = arrayAppendString(b, s) 173 b = append(b, ',') 174 } 175 if len(ss) > 0 { 176 b[len(b)-1] = '}' // Replace trailing comma. 177 } else { 178 b = append(b, '}') 179 } 180 181 b = append(b, '\'') 182 183 return b 184 } 185 186 func appendIntSliceValue(fmter schema.Formatter, b []byte, v reflect.Value) []byte { 187 ints := v.Convert(sliceIntType).Interface().([]int) 188 return appendIntSlice(b, ints) 189 } 190 191 func appendIntSlice(b []byte, ints []int) []byte { 192 if ints == nil { 193 return dialect.AppendNull(b) 194 } 195 196 b = append(b, '\'') 197 198 b = append(b, '{') 199 for _, n := range ints { 200 b = strconv.AppendInt(b, int64(n), 10) 201 b = append(b, ',') 202 } 203 if len(ints) > 0 { 204 b[len(b)-1] = '}' // Replace trailing comma. 205 } else { 206 b = append(b, '}') 207 } 208 209 b = append(b, '\'') 210 211 return b 212 } 213 214 func appendInt64SliceValue(fmter schema.Formatter, b []byte, v reflect.Value) []byte { 215 ints := v.Convert(sliceInt64Type).Interface().([]int64) 216 return appendInt64Slice(b, ints) 217 } 218 219 func appendInt64Slice(b []byte, ints []int64) []byte { 220 if ints == nil { 221 return dialect.AppendNull(b) 222 } 223 224 b = append(b, '\'') 225 226 b = append(b, '{') 227 for _, n := range ints { 228 b = strconv.AppendInt(b, n, 10) 229 b = append(b, ',') 230 } 231 if len(ints) > 0 { 232 b[len(b)-1] = '}' // Replace trailing comma. 233 } else { 234 b = append(b, '}') 235 } 236 237 b = append(b, '\'') 238 239 return b 240 } 241 242 func appendFloat64SliceValue(fmter schema.Formatter, b []byte, v reflect.Value) []byte { 243 floats := v.Convert(sliceFloat64Type).Interface().([]float64) 244 return appendFloat64Slice(b, floats) 245 } 246 247 func appendFloat64Slice(b []byte, floats []float64) []byte { 248 if floats == nil { 249 return dialect.AppendNull(b) 250 } 251 252 b = append(b, '\'') 253 254 b = append(b, '{') 255 for _, n := range floats { 256 b = dialect.AppendFloat64(b, n) 257 b = append(b, ',') 258 } 259 if len(floats) > 0 { 260 b[len(b)-1] = '}' // Replace trailing comma. 261 } else { 262 b = append(b, '}') 263 } 264 265 b = append(b, '\'') 266 267 return b 268 } 269 270 //------------------------------------------------------------------------------ 271 272 func arrayAppendBytes(b []byte, bs []byte) []byte { 273 if bs == nil { 274 return dialect.AppendNull(b) 275 } 276 277 b = append(b, `"\\x`...) 278 279 s := len(b) 280 b = append(b, make([]byte, hex.EncodedLen(len(bs)))...) 281 hex.Encode(b[s:], bs) 282 283 b = append(b, '"') 284 285 return b 286 } 287 288 func arrayAppendString(b []byte, s string) []byte { 289 b = append(b, '"') 290 for _, r := range s { 291 switch r { 292 case 0: 293 // ignore 294 case '\'': 295 b = append(b, "''"...) 296 case '"': 297 b = append(b, '\\', '"') 298 case '\\': 299 b = append(b, '\\', '\\') 300 default: 301 if r < utf8.RuneSelf { 302 b = append(b, byte(r)) 303 break 304 } 305 l := len(b) 306 if cap(b)-l < utf8.UTFMax { 307 b = append(b, make([]byte, utf8.UTFMax)...) 308 } 309 n := utf8.EncodeRune(b[l:l+utf8.UTFMax], r) 310 b = b[:l+n] 311 } 312 } 313 b = append(b, '"') 314 return b 315 } 316 317 func appendTimeSliceValue(fmter schema.Formatter, b []byte, v reflect.Value) []byte { 318 ts := v.Convert(sliceTimeType).Interface().([]time.Time) 319 return appendTimeSlice(fmter, b, ts) 320 } 321 322 func appendTimeSlice(fmter schema.Formatter, b []byte, ts []time.Time) []byte { 323 if ts == nil { 324 return dialect.AppendNull(b) 325 } 326 b = append(b, '\'') 327 b = append(b, '{') 328 for _, t := range ts { 329 b = append(b, '"') 330 b = t.UTC().AppendFormat(b, "2006-01-02 15:04:05.999999-07:00") 331 b = append(b, '"') 332 b = append(b, ',') 333 } 334 if len(ts) > 0 { 335 b[len(b)-1] = '}' // Replace trailing comma. 336 } else { 337 b = append(b, '}') 338 } 339 b = append(b, '\'') 340 return b 341 } 342 343 //------------------------------------------------------------------------------ 344 345 var mapStringStringType = reflect.TypeOf(map[string]string(nil)) 346 347 func (d *Dialect) hstoreAppender(typ reflect.Type) schema.AppenderFunc { 348 kind := typ.Kind() 349 350 switch kind { 351 case reflect.Ptr: 352 if fn := d.hstoreAppender(typ.Elem()); fn != nil { 353 return schema.PtrAppender(fn) 354 } 355 case reflect.Map: 356 // ok: 357 default: 358 return nil 359 } 360 361 if typ.Key() == stringType && typ.Elem() == stringType { 362 return appendMapStringStringValue 363 } 364 365 return func(fmter schema.Formatter, b []byte, v reflect.Value) []byte { 366 err := fmt.Errorf("bun: Hstore(unsupported %s)", v.Type()) 367 return dialect.AppendError(b, err) 368 } 369 } 370 371 func appendMapStringString(b []byte, m map[string]string) []byte { 372 if m == nil { 373 return dialect.AppendNull(b) 374 } 375 376 b = append(b, '\'') 377 378 for key, value := range m { 379 b = arrayAppendString(b, key) 380 b = append(b, '=', '>') 381 b = arrayAppendString(b, value) 382 b = append(b, ',') 383 } 384 if len(m) > 0 { 385 b = b[:len(b)-1] // Strip trailing comma. 386 } 387 388 b = append(b, '\'') 389 390 return b 391 } 392 393 func appendMapStringStringValue(fmter schema.Formatter, b []byte, v reflect.Value) []byte { 394 m := v.Convert(mapStringStringType).Interface().(map[string]string) 395 return appendMapStringString(b, m) 396 }