1// Code generated by erb. DO NOT EDIT. 2 3package pgtype 4 5import ( 6 "database/sql/driver" 7 "encoding/binary" 8 "fmt" 9 "reflect" 10 11 "github.com/jackc/pgio" 12) 13 14type VarcharArray struct { 15 Elements []Varchar 16 Dimensions []ArrayDimension 17 Status Status 18} 19 20func (dst *VarcharArray) Set(src interface{}) error { 21 // untyped nil and typed nil interfaces are different 22 if src == nil { 23 *dst = VarcharArray{Status: Null} 24 return nil 25 } 26 27 if value, ok := src.(interface{ Get() interface{} }); ok { 28 value2 := value.Get() 29 if value2 != value { 30 return dst.Set(value2) 31 } 32 } 33 34 // Attempt to match to select common types: 35 switch value := src.(type) { 36 37 case []string: 38 if value == nil { 39 *dst = VarcharArray{Status: Null} 40 } else if len(value) == 0 { 41 *dst = VarcharArray{Status: Present} 42 } else { 43 elements := make([]Varchar, len(value)) 44 for i := range value { 45 if err := elements[i].Set(value[i]); err != nil { 46 return err 47 } 48 } 49 *dst = VarcharArray{ 50 Elements: elements, 51 Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, 52 Status: Present, 53 } 54 } 55 56 case []*string: 57 if value == nil { 58 *dst = VarcharArray{Status: Null} 59 } else if len(value) == 0 { 60 *dst = VarcharArray{Status: Present} 61 } else { 62 elements := make([]Varchar, len(value)) 63 for i := range value { 64 if err := elements[i].Set(value[i]); err != nil { 65 return err 66 } 67 } 68 *dst = VarcharArray{ 69 Elements: elements, 70 Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, 71 Status: Present, 72 } 73 } 74 75 case []Varchar: 76 if value == nil { 77 *dst = VarcharArray{Status: Null} 78 } else if len(value) == 0 { 79 *dst = VarcharArray{Status: Present} 80 } else { 81 *dst = VarcharArray{ 82 Elements: value, 83 Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, 84 Status: Present, 85 } 86 } 87 default: 88 // Fallback to reflection if an optimised match was not found. 89 // The reflection is necessary for arrays and multidimensional slices, 90 // but it comes with a 20-50% performance penalty for large arrays/slices 91 reflectedValue := reflect.ValueOf(src) 92 if !reflectedValue.IsValid() || reflectedValue.IsZero() { 93 *dst = VarcharArray{Status: Null} 94 return nil 95 } 96 97 dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0) 98 if !ok { 99 return fmt.Errorf("cannot find dimensions of %v for VarcharArray", src) 100 } 101 if elementsLength == 0 { 102 *dst = VarcharArray{Status: Present} 103 return nil 104 } 105 if len(dimensions) == 0 { 106 if originalSrc, ok := underlyingSliceType(src); ok { 107 return dst.Set(originalSrc) 108 } 109 return fmt.Errorf("cannot convert %v to VarcharArray", src) 110 } 111 112 *dst = VarcharArray{ 113 Elements: make([]Varchar, elementsLength), 114 Dimensions: dimensions, 115 Status: Present, 116 } 117 elementCount, err := dst.setRecursive(reflectedValue, 0, 0) 118 if err != nil { 119 // Maybe the target was one dimension too far, try again: 120 if len(dst.Dimensions) > 1 { 121 dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] 122 elementsLength = 0 123 for _, dim := range dst.Dimensions { 124 if elementsLength == 0 { 125 elementsLength = int(dim.Length) 126 } else { 127 elementsLength *= int(dim.Length) 128 } 129 } 130 dst.Elements = make([]Varchar, elementsLength) 131 elementCount, err = dst.setRecursive(reflectedValue, 0, 0) 132 if err != nil { 133 return err 134 } 135 } else { 136 return err 137 } 138 } 139 if elementCount != len(dst.Elements) { 140 return fmt.Errorf("cannot convert %v to VarcharArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) 141 } 142 } 143 144 return nil 145} 146 147func (dst *VarcharArray) setRecursive(value reflect.Value, index, dimension int) (int, error) { 148 switch value.Kind() { 149 case reflect.Array: 150 fallthrough 151 case reflect.Slice: 152 if len(dst.Dimensions) == dimension { 153 break 154 } 155 156 valueLen := value.Len() 157 if int32(valueLen) != dst.Dimensions[dimension].Length { 158 return 0, fmt.Errorf("multidimensional arrays must have array expressions with matching dimensions") 159 } 160 for i := 0; i < valueLen; i++ { 161 var err error 162 index, err = dst.setRecursive(value.Index(i), index, dimension+1) 163 if err != nil { 164 return 0, err 165 } 166 } 167 168 return index, nil 169 } 170 if !value.CanInterface() { 171 return 0, fmt.Errorf("cannot convert all values to VarcharArray") 172 } 173 if err := dst.Elements[index].Set(value.Interface()); err != nil { 174 return 0, fmt.Errorf("%v in VarcharArray", err) 175 } 176 index++ 177 178 return index, nil 179} 180 181func (dst VarcharArray) Get() interface{} { 182 switch dst.Status { 183 case Present: 184 return dst 185 case Null: 186 return nil 187 default: 188 return dst.Status 189 } 190} 191 192func (src *VarcharArray) AssignTo(dst interface{}) error { 193 switch src.Status { 194 case Present: 195 if len(src.Dimensions) <= 1 { 196 // Attempt to match to select common types: 197 switch v := dst.(type) { 198 199 case *[]string: 200 *v = make([]string, len(src.Elements)) 201 for i := range src.Elements { 202 if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { 203 return err 204 } 205 } 206 return nil 207 208 case *[]*string: 209 *v = make([]*string, len(src.Elements)) 210 for i := range src.Elements { 211 if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { 212 return err 213 } 214 } 215 return nil 216 217 } 218 } 219 220 // Try to convert to something AssignTo can use directly. 221 if nextDst, retry := GetAssignToDstType(dst); retry { 222 return src.AssignTo(nextDst) 223 } 224 225 // Fallback to reflection if an optimised match was not found. 226 // The reflection is necessary for arrays and multidimensional slices, 227 // but it comes with a 20-50% performance penalty for large arrays/slices 228 value := reflect.ValueOf(dst) 229 if value.Kind() == reflect.Ptr { 230 value = value.Elem() 231 } 232 233 switch value.Kind() { 234 case reflect.Array, reflect.Slice: 235 default: 236 return fmt.Errorf("cannot assign %T to %T", src, dst) 237 } 238 239 if len(src.Elements) == 0 { 240 if value.Kind() == reflect.Slice { 241 value.Set(reflect.MakeSlice(value.Type(), 0, 0)) 242 return nil 243 } 244 } 245 246 elementCount, err := src.assignToRecursive(value, 0, 0) 247 if err != nil { 248 return err 249 } 250 if elementCount != len(src.Elements) { 251 return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) 252 } 253 254 return nil 255 case Null: 256 return NullAssignTo(dst) 257 } 258 259 return fmt.Errorf("cannot decode %#v into %T", src, dst) 260} 261 262func (src *VarcharArray) assignToRecursive(value reflect.Value, index, dimension int) (int, error) { 263 switch kind := value.Kind(); kind { 264 case reflect.Array: 265 fallthrough 266 case reflect.Slice: 267 if len(src.Dimensions) == dimension { 268 break 269 } 270 271 length := int(src.Dimensions[dimension].Length) 272 if reflect.Array == kind { 273 typ := value.Type() 274 if typ.Len() != length { 275 return 0, fmt.Errorf("expected size %d array, but %s has size %d array", length, typ, typ.Len()) 276 } 277 value.Set(reflect.New(typ).Elem()) 278 } else { 279 value.Set(reflect.MakeSlice(value.Type(), length, length)) 280 } 281 282 var err error 283 for i := 0; i < length; i++ { 284 index, err = src.assignToRecursive(value.Index(i), index, dimension+1) 285 if err != nil { 286 return 0, err 287 } 288 } 289 290 return index, nil 291 } 292 if len(src.Dimensions) != dimension { 293 return 0, fmt.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension) 294 } 295 if !value.CanAddr() { 296 return 0, fmt.Errorf("cannot assign all values from VarcharArray") 297 } 298 addr := value.Addr() 299 if !addr.CanInterface() { 300 return 0, fmt.Errorf("cannot assign all values from VarcharArray") 301 } 302 if err := src.Elements[index].AssignTo(addr.Interface()); err != nil { 303 return 0, err 304 } 305 index++ 306 return index, nil 307} 308 309func (dst *VarcharArray) DecodeText(ci *ConnInfo, src []byte) error { 310 if src == nil { 311 *dst = VarcharArray{Status: Null} 312 return nil 313 } 314 315 uta, err := ParseUntypedTextArray(string(src)) 316 if err != nil { 317 return err 318 } 319 320 var elements []Varchar 321 322 if len(uta.Elements) > 0 { 323 elements = make([]Varchar, len(uta.Elements)) 324 325 for i, s := range uta.Elements { 326 var elem Varchar 327 var elemSrc []byte 328 if s != "NULL" || uta.Quoted[i] { 329 elemSrc = []byte(s) 330 } 331 err = elem.DecodeText(ci, elemSrc) 332 if err != nil { 333 return err 334 } 335 336 elements[i] = elem 337 } 338 } 339 340 *dst = VarcharArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} 341 342 return nil 343} 344 345func (dst *VarcharArray) DecodeBinary(ci *ConnInfo, src []byte) error { 346 if src == nil { 347 *dst = VarcharArray{Status: Null} 348 return nil 349 } 350 351 var arrayHeader ArrayHeader 352 rp, err := arrayHeader.DecodeBinary(ci, src) 353 if err != nil { 354 return err 355 } 356 357 if len(arrayHeader.Dimensions) == 0 { 358 *dst = VarcharArray{Dimensions: arrayHeader.Dimensions, Status: Present} 359 return nil 360 } 361 362 elementCount := arrayHeader.Dimensions[0].Length 363 for _, d := range arrayHeader.Dimensions[1:] { 364 elementCount *= d.Length 365 } 366 367 elements := make([]Varchar, elementCount) 368 369 for i := range elements { 370 elemLen := int(int32(binary.BigEndian.Uint32(src[rp:]))) 371 rp += 4 372 var elemSrc []byte 373 if elemLen >= 0 { 374 elemSrc = src[rp : rp+elemLen] 375 rp += elemLen 376 } 377 err = elements[i].DecodeBinary(ci, elemSrc) 378 if err != nil { 379 return err 380 } 381 } 382 383 *dst = VarcharArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} 384 return nil 385} 386 387func (src VarcharArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { 388 switch src.Status { 389 case Null: 390 return nil, nil 391 case Undefined: 392 return nil, errUndefined 393 } 394 395 if len(src.Dimensions) == 0 { 396 return append(buf, '{', '}'), nil 397 } 398 399 buf = EncodeTextArrayDimensions(buf, src.Dimensions) 400 401 // dimElemCounts is the multiples of elements that each array lies on. For 402 // example, a single dimension array of length 4 would have a dimElemCounts of 403 // [4]. A multi-dimensional array of lengths [3,5,2] would have a 404 // dimElemCounts of [30,10,2]. This is used to simplify when to render a '{' 405 // or '}'. 406 dimElemCounts := make([]int, len(src.Dimensions)) 407 dimElemCounts[len(src.Dimensions)-1] = int(src.Dimensions[len(src.Dimensions)-1].Length) 408 for i := len(src.Dimensions) - 2; i > -1; i-- { 409 dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1] 410 } 411 412 inElemBuf := make([]byte, 0, 32) 413 for i, elem := range src.Elements { 414 if i > 0 { 415 buf = append(buf, ',') 416 } 417 418 for _, dec := range dimElemCounts { 419 if i%dec == 0 { 420 buf = append(buf, '{') 421 } 422 } 423 424 elemBuf, err := elem.EncodeText(ci, inElemBuf) 425 if err != nil { 426 return nil, err 427 } 428 if elemBuf == nil { 429 buf = append(buf, `NULL`...) 430 } else { 431 buf = append(buf, QuoteArrayElementIfNeeded(string(elemBuf))...) 432 } 433 434 for _, dec := range dimElemCounts { 435 if (i+1)%dec == 0 { 436 buf = append(buf, '}') 437 } 438 } 439 } 440 441 return buf, nil 442} 443 444func (src VarcharArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { 445 switch src.Status { 446 case Null: 447 return nil, nil 448 case Undefined: 449 return nil, errUndefined 450 } 451 452 arrayHeader := ArrayHeader{ 453 Dimensions: src.Dimensions, 454 } 455 456 if dt, ok := ci.DataTypeForName("varchar"); ok { 457 arrayHeader.ElementOID = int32(dt.OID) 458 } else { 459 return nil, fmt.Errorf("unable to find oid for type name %v", "varchar") 460 } 461 462 for i := range src.Elements { 463 if src.Elements[i].Status == Null { 464 arrayHeader.ContainsNull = true 465 break 466 } 467 } 468 469 buf = arrayHeader.EncodeBinary(ci, buf) 470 471 for i := range src.Elements { 472 sp := len(buf) 473 buf = pgio.AppendInt32(buf, -1) 474 475 elemBuf, err := src.Elements[i].EncodeBinary(ci, buf) 476 if err != nil { 477 return nil, err 478 } 479 if elemBuf != nil { 480 buf = elemBuf 481 pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4)) 482 } 483 } 484 485 return buf, nil 486} 487 488// Scan implements the database/sql Scanner interface. 489func (dst *VarcharArray) Scan(src interface{}) error { 490 if src == nil { 491 return dst.DecodeText(nil, nil) 492 } 493 494 switch src := src.(type) { 495 case string: 496 return dst.DecodeText(nil, []byte(src)) 497 case []byte: 498 srcCopy := make([]byte, len(src)) 499 copy(srcCopy, src) 500 return dst.DecodeText(nil, srcCopy) 501 } 502 503 return fmt.Errorf("cannot scan %T", src) 504} 505 506// Value implements the database/sql/driver Valuer interface. 507func (src VarcharArray) Value() (driver.Value, error) { 508 buf, err := src.EncodeText(nil, nil) 509 if err != nil { 510 return nil, err 511 } 512 if buf == nil { 513 return nil, nil 514 } 515 516 return string(buf), nil 517} 518