Fix randomize for all array types, remove generic

* GenericArray can't work with generated code.
* Multi-dimensional arrays can't work because
  PSQL does not have a method to discover array
  depth.
This commit is contained in:
Patrick O'brien 2016-09-12 07:22:17 +10:00
parent e62dfe369f
commit 9bcaf51493
6 changed files with 89 additions and 534 deletions

View file

@ -367,12 +367,12 @@ func getArrayType(c bdb.Column) string {
return "types.BytesArray"
case "bit", "interval", "uuint", "bit varying", "character", "money", "character varying", "cidr", "inet", "macaddr", "text", "uuid", "xml":
return "types.StringArray"
case "bool":
case "boolean":
return "types.BoolArray"
case "decimal", "numeric", "double precision", "real":
return "types.Float64Array"
default:
return "types.GenericArray"
return "types.StringArray"
}
}

View file

@ -47,7 +47,6 @@ var (
typeBoolArray = reflect.TypeOf(types.BoolArray{})
typeFloat64Array = reflect.TypeOf(types.Float64Array{})
typeStringArray = reflect.TypeOf(types.StringArray{})
typeGenericArray = reflect.TypeOf(types.GenericArray{})
typeHstore = reflect.TypeOf(types.Hstore{})
rgxValidTime = regexp.MustCompile(`[2-9]+`)
@ -318,11 +317,7 @@ func randomizeField(s *Seed, field reflect.Value, fieldType string, canBeNull bo
// If it's a Postgres array, treat it like one
if strings.HasPrefix(fieldType, "ARRAY") {
if isNull {
value = getArrayNullValue(typ)
} else {
value = getArrayRandValue(s, typ)
}
value = getArrayRandValue(s, typ, fieldType)
// Retrieve the value to be returned
} else if kind == reflect.Struct {
if isNull {
@ -347,27 +342,8 @@ func randomizeField(s *Seed, field reflect.Value, fieldType string, canBeNull bo
return nil
}
func getArrayNullValue(typ reflect.Type) interface{} {
fmt.Println(typ)
switch typ {
case typeInt64Array:
return types.Int64Array{}
case typeFloat64Array:
return types.Float64Array{}
case typeBoolArray:
return types.BoolArray{}
case typeStringArray:
return types.StringArray{}
case typeBytesArray:
return types.BytesArray{}
case typeGenericArray:
return types.GenericArray{}
}
return nil
}
func getArrayRandValue(s *Seed, typ reflect.Type) interface{} {
func getArrayRandValue(s *Seed, typ reflect.Type, fieldType string) interface{} {
fieldType = strings.TrimLeft(fieldType, "ARRAY")
switch typ {
case typeInt64Array:
return types.Int64Array{int64(s.nextInt()), int64(s.nextInt())}
@ -376,11 +352,54 @@ func getArrayRandValue(s *Seed, typ reflect.Type) interface{} {
case typeBoolArray:
return types.BoolArray{s.nextInt()%2 == 0, s.nextInt()%2 == 0, s.nextInt()%2 == 0}
case typeStringArray:
if fieldType == "interval" {
value := strconv.Itoa((s.nextInt()%26)+2) + " days"
return types.StringArray{value, value}
}
if fieldType == "uuid" {
value := uuid.NewV4().String()
return types.StringArray{value, value}
}
if fieldType == "box" || fieldType == "line" || fieldType == "lseg" ||
fieldType == "path" || fieldType == "polygon" {
value := randBox()
return types.StringArray{value, value}
}
if fieldType == "cidr" || fieldType == "inet" {
value := randNetAddr()
return types.StringArray{value, value}
}
if fieldType == "macaddr" {
value := randMacAddr()
return types.StringArray{value, value}
}
if fieldType == "circle" {
value := randCircle()
return types.StringArray{value, value}
}
if fieldType == "pg_lsn" {
value := randLsn()
return types.StringArray{value, value}
}
if fieldType == "point" {
value := randPoint()
return types.StringArray{value, value}
}
if fieldType == "txid_snapshot" {
value := randTxID()
return types.StringArray{value, value}
}
if fieldType == "money" {
value := randMoney(s)
return types.StringArray{value, value}
}
if fieldType == "json" || fieldType == "jsonb" {
value := []byte(fmt.Sprintf(`"%s"`, randStr(s, 1)))
return types.StringArray{string(value)}
}
return types.StringArray{randStr(s, 4), randStr(s, 4), randStr(s, 4)}
case typeBytesArray:
return types.BytesArray{randByteSlice(s, 4), randByteSlice(s, 4), randByteSlice(s, 4)}
case typeGenericArray:
return types.GenericArray{A: []types.JSON{randJSON(s, 4), randJSON(s, 4), randJSON(s, 4)}}
}
return nil
@ -574,17 +593,6 @@ func randByteSlice(s *Seed, ln int) []byte {
return str
}
func randJSON(s *Seed, ln int) types.JSON {
str := make(types.JSON, ln)
str[0] = '"'
for i := 1; i < ln-1; i++ {
str[i] = byte(s.nextInt() % 256)
}
str[ln-1] = '"'
return str
}
func randPoint() string {
a := rand.Intn(100)
b := a + 1

View file

@ -184,9 +184,10 @@ func Array(a interface{}) interface {
return (*Int64Array)(a)
case *[]string:
return (*StringArray)(a)
}
return GenericArray{a}
default:
panic(fmt.Sprintf("boil: invalid type received %T", a))
}
}
// ArrayDelimiter may be optionally implemented by driver.Valuer or sql.Scanner
@ -208,7 +209,7 @@ func (a *BoolArray) Scan(src interface{}) error {
return a.scanBytes([]byte(src))
}
return fmt.Errorf("pq: cannot convert %T to BoolArray", src)
return fmt.Errorf("boil: cannot convert %T to BoolArray", src)
}
func (a *BoolArray) scanBytes(src []byte) error {
@ -222,7 +223,7 @@ func (a *BoolArray) scanBytes(src []byte) error {
b := make(BoolArray, len(elems))
for i, v := range elems {
if len(v) != 1 {
return fmt.Errorf("pq: could not parse boolean array index %d: invalid boolean %q", i, v)
return fmt.Errorf("boil: could not parse boolean array index %d: invalid boolean %q", i, v)
}
switch v[0] {
case 't':
@ -230,7 +231,7 @@ func (a *BoolArray) scanBytes(src []byte) error {
case 'f':
b[i] = false
default:
return fmt.Errorf("pq: could not parse boolean array index %d: invalid boolean %q", i, v)
return fmt.Errorf("boil: could not parse boolean array index %d: invalid boolean %q", i, v)
}
}
*a = b
@ -279,7 +280,7 @@ func (a *BytesArray) Scan(src interface{}) error {
return a.scanBytes([]byte(src))
}
return fmt.Errorf("pq: cannot convert %T to BytesArray", src)
return fmt.Errorf("boil: cannot convert %T to BytesArray", src)
}
func (a *BytesArray) scanBytes(src []byte) error {
@ -348,7 +349,7 @@ func (a *Float64Array) Scan(src interface{}) error {
return a.scanBytes([]byte(src))
}
return fmt.Errorf("pq: cannot convert %T to Float64Array", src)
return fmt.Errorf("boil: cannot convert %T to Float64Array", src)
}
func (a *Float64Array) scanBytes(src []byte) error {
@ -362,7 +363,7 @@ func (a *Float64Array) scanBytes(src []byte) error {
b := make(Float64Array, len(elems))
for i, v := range elems {
if b[i], err = strconv.ParseFloat(string(v), 64); err != nil {
return fmt.Errorf("pq: parsing array element index %d: %v", i, err)
return fmt.Errorf("boil: parsing array element index %d: %v", i, err)
}
}
*a = b
@ -394,151 +395,6 @@ func (a Float64Array) Value() (driver.Value, error) {
return "{}", nil
}
// GenericArray implements the driver.Valuer and sql.Scanner interfaces for
// an array or slice of any dimension.
type GenericArray struct{ A interface{} }
func (GenericArray) evaluateDestination(rt reflect.Type) (reflect.Type, func([]byte, reflect.Value) error, string) {
var assign func([]byte, reflect.Value) error
var del = ","
// TODO calculate the assign function for other types
// TODO repeat this section on the element type of arrays or slices (multidimensional)
{
if reflect.PtrTo(rt).Implements(typeSQLScanner) {
// dest is always addressable because it is an element of a slice.
assign = func(src []byte, dest reflect.Value) (err error) {
ss := dest.Addr().Interface().(sql.Scanner)
if src == nil {
err = ss.Scan(nil)
} else {
err = ss.Scan(src)
}
return
}
goto FoundType
}
assign = func([]byte, reflect.Value) error {
return fmt.Errorf("pq: scanning to %s is not implemented; only sql.Scanner", rt)
}
}
FoundType:
if ad, ok := reflect.Zero(rt).Interface().(ArrayDelimiter); ok {
del = ad.ArrayDelimiter()
}
return rt, assign, del
}
// Scan implements the sql.Scanner interface.
func (a GenericArray) Scan(src interface{}) error {
dpv := reflect.ValueOf(a.A)
switch {
case dpv.Kind() != reflect.Ptr:
return fmt.Errorf("pq: destination %T is not a pointer to array or slice", a.A)
case dpv.IsNil():
return fmt.Errorf("pq: destination %T is nil", a.A)
}
dv := dpv.Elem()
switch dv.Kind() {
case reflect.Slice:
case reflect.Array:
default:
return fmt.Errorf("pq: destination %T is not a pointer to array or slice", a.A)
}
switch src := src.(type) {
case []byte:
return a.scanBytes(src, dv)
case string:
return a.scanBytes([]byte(src), dv)
}
return fmt.Errorf("pq: cannot convert %T to %s", src, dv.Type())
}
func (a GenericArray) scanBytes(src []byte, dv reflect.Value) error {
dtype, assign, del := a.evaluateDestination(dv.Type().Elem())
dims, elems, err := parseArray(src, []byte(del))
if err != nil {
return err
}
// TODO allow multidimensional
if len(dims) > 1 {
return fmt.Errorf("pq: scanning from multidimensional ARRAY%s is not implemented",
strings.Replace(fmt.Sprint(dims), " ", "][", -1))
}
// Treat a zero-dimensional array like an array with a single dimension of zero.
if len(dims) == 0 {
dims = append(dims, 0)
}
for i, rt := 0, dv.Type(); i < len(dims); i, rt = i+1, rt.Elem() {
switch rt.Kind() {
case reflect.Slice:
case reflect.Array:
if rt.Len() != dims[i] {
return fmt.Errorf("pq: cannot convert ARRAY%s to %s",
strings.Replace(fmt.Sprint(dims), " ", "][", -1), dv.Type())
}
default:
// TODO handle multidimensional
}
}
values := reflect.MakeSlice(reflect.SliceOf(dtype), len(elems), len(elems))
for i, e := range elems {
if err := assign(e, values.Index(i)); err != nil {
return fmt.Errorf("pq: parsing array element index %d: %v", i, err)
}
}
// TODO handle multidimensional
switch dv.Kind() {
case reflect.Slice:
dv.Set(values.Slice(0, dims[0]))
case reflect.Array:
for i := 0; i < dims[0]; i++ {
dv.Index(i).Set(values.Index(i))
}
}
return nil
}
// Value implements the driver.Valuer interface.
func (a GenericArray) Value() (driver.Value, error) {
if a.A == nil {
return nil, nil
}
rv := reflect.ValueOf(a.A)
if k := rv.Kind(); k != reflect.Array && k != reflect.Slice {
return nil, fmt.Errorf("pq: Unable to convert %T to array", a.A)
}
if n := rv.Len(); n > 0 {
// There will be at least two curly brackets, N bytes of values,
// and N-1 bytes of delimiters.
b := make([]byte, 0, 1+2*n)
b, _, err := appendArray(b, rv, n)
return string(b), err
}
return "{}", nil
}
// Int64Array represents a one-dimensional array of the PostgreSQL integer types.
type Int64Array []int64
// Scan implements the sql.Scanner interface.
@ -550,7 +406,7 @@ func (a *Int64Array) Scan(src interface{}) error {
return a.scanBytes([]byte(src))
}
return fmt.Errorf("pq: cannot convert %T to Int64Array", src)
return fmt.Errorf("boil: cannot convert %T to Int64Array", src)
}
func (a *Int64Array) scanBytes(src []byte) error {
@ -564,7 +420,7 @@ func (a *Int64Array) scanBytes(src []byte) error {
b := make(Int64Array, len(elems))
for i, v := range elems {
if b[i], err = strconv.ParseInt(string(v), 10, 64); err != nil {
return fmt.Errorf("pq: parsing array element index %d: %v", i, err)
return fmt.Errorf("boil: parsing array element index %d: %v", i, err)
}
}
*a = b
@ -608,7 +464,7 @@ func (a *StringArray) Scan(src interface{}) error {
return a.scanBytes([]byte(src))
}
return fmt.Errorf("pq: cannot convert %T to StringArray", src)
return fmt.Errorf("boil: cannot convert %T to StringArray", src)
}
func (a *StringArray) scanBytes(src []byte) error {
@ -622,7 +478,7 @@ func (a *StringArray) scanBytes(src []byte) error {
b := make(StringArray, len(elems))
for i, v := range elems {
if b[i] = string(v); v == nil {
return fmt.Errorf("pq: parsing array element index %d: cannot convert nil to string", i)
return fmt.Errorf("boil: parsing array element index %d: cannot convert nil to string", i)
}
}
*a = b
@ -753,7 +609,7 @@ func parseArray(src, del []byte) (dims []int, elems [][]byte, err error) {
var depth, i int
if len(src) < 1 || src[0] != '{' {
return nil, nil, fmt.Errorf("pq: unable to parse array; expected %q at offset %d", '{', 0)
return nil, nil, fmt.Errorf("boil: unable to parse array; expected %q at offset %d", '{', 0)
}
Open:
@ -803,7 +659,7 @@ Element:
if bytes.HasPrefix(src[i:], del) || src[i] == '}' {
elem := src[start:i]
if len(elem) == 0 {
return nil, nil, fmt.Errorf("pq: unable to parse array; unexpected %q at offset %d", src[i], i)
return nil, nil, fmt.Errorf("boil: unable to parse array; unexpected %q at offset %d", src[i], i)
}
if bytes.Equal(elem, []byte("NULL")) {
elem = nil
@ -825,7 +681,7 @@ Element:
depth--
i++
} else {
return nil, nil, fmt.Errorf("pq: unable to parse array; unexpected %q at offset %d", src[i], i)
return nil, nil, fmt.Errorf("boil: unable to parse array; unexpected %q at offset %d", src[i], i)
}
}
@ -835,16 +691,16 @@ Close:
depth--
i++
} else {
return nil, nil, fmt.Errorf("pq: unable to parse array; unexpected %q at offset %d", src[i], i)
return nil, nil, fmt.Errorf("boil: unable to parse array; unexpected %q at offset %d", src[i], i)
}
}
if depth > 0 {
err = fmt.Errorf("pq: unable to parse array; expected %q at offset %d", '}', i)
err = fmt.Errorf("boil: unable to parse array; expected %q at offset %d", '}', i)
}
if err == nil {
for _, d := range dims {
if (len(elems) % d) != 0 {
err = fmt.Errorf("pq: multidimensional arrays must have elements with matching dimensions")
err = fmt.Errorf("boil: multidimensional arrays must have elements with matching dimensions")
}
}
}
@ -857,7 +713,7 @@ func scanLinearArray(src, del []byte, typ string) (elems [][]byte, err error) {
return nil, err
}
if len(dims) > 1 {
return nil, fmt.Errorf("pq: cannot convert ARRAY%s to %s", strings.Replace(fmt.Sprint(dims), " ", "][", -1), typ)
return nil, fmt.Errorf("boil: cannot convert ARRAY%s to %s", strings.Replace(fmt.Sprint(dims), " ", "][", -1), typ)
}
return elems, err
}

View file

@ -20,7 +20,6 @@
package types
import (
"bytes"
"database/sql"
"database/sql/driver"
"math/rand"
@ -125,19 +124,6 @@ func TestArrayScanner(t *testing.T) {
if _, ok := s.(*StringArray); !ok {
t.Errorf("Expected *StringArray, got %T", s)
}
for _, tt := range []interface{}{
&[]sql.Scanner{},
&[][]bool{},
&[][]float64{},
&[][]int64{},
&[][]string{},
} {
s = Array(tt)
if _, ok := s.(GenericArray); !ok {
t.Errorf("Expected GenericArray for %T, got %T", tt, s)
}
}
}
func TestArrayValuer(t *testing.T) {
@ -162,20 +148,6 @@ func TestArrayValuer(t *testing.T) {
if _, ok := v.(*StringArray); !ok {
t.Errorf("Expected *StringArray, got %T", v)
}
for _, tt := range []interface{}{
nil,
[]driver.Value{},
[][]bool{},
[][]float64{},
[][]int64{},
[][]string{},
} {
v = Array(tt)
if _, ok := v.(GenericArray); !ok {
t.Errorf("Expected GenericArray for %T, got %T", tt, v)
}
}
}
func TestBoolArrayScanUnsupported(t *testing.T) {
@ -826,300 +798,3 @@ func BenchmarkStringArrayValue(b *testing.B) {
a.Value()
}
}
func TestGenericArrayScanUnsupported(t *testing.T) {
var s string
var ss []string
for _, tt := range []struct {
src, dest interface{}
err string
}{
{nil, nil, "destination <nil> is not a pointer to array or slice"},
{nil, true, "destination bool is not a pointer to array or slice"},
{nil, &s, "destination *string is not a pointer to array or slice"},
{nil, ss, "destination []string is not a pointer to array or slice"},
{true, &ss, "bool to []string"},
{`{{x}}`, &ss, "multidimensional ARRAY[1][1] is not implemented"},
{`{{x},{x}}`, &ss, "multidimensional ARRAY[2][1] is not implemented"},
{`{x}`, &ss, "scanning to string is not implemented"},
} {
err := GenericArray{tt.dest}.Scan(tt.src)
if err == nil {
t.Fatalf("Expected error for [%#v %#v]", tt.src, tt.dest)
}
if !strings.Contains(err.Error(), tt.err) {
t.Errorf("Expected error to contain %q for [%#v %#v], got %q", tt.err, tt.src, tt.dest, err)
}
}
}
func TestGenericArrayScanScannerArrayBytes(t *testing.T) {
src, expected, nsa := []byte(`{NULL,abc,"\""}`),
[3]sql.NullString{{}, {String: `abc`, Valid: true}, {String: `"`, Valid: true}},
[3]sql.NullString{{String: ``, Valid: true}, {}, {}}
if err := (GenericArray{&nsa}).Scan(src); err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if !reflect.DeepEqual(nsa, expected) {
t.Errorf("Expected %v, got %v", expected, nsa)
}
}
func TestGenericArrayScanScannerArrayString(t *testing.T) {
src, expected, nsa := `{NULL,"\"",xyz}`,
[3]sql.NullString{{}, {String: `"`, Valid: true}, {String: `xyz`, Valid: true}},
[3]sql.NullString{{String: ``, Valid: true}, {}, {}}
if err := (GenericArray{&nsa}).Scan(src); err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if !reflect.DeepEqual(nsa, expected) {
t.Errorf("Expected %v, got %v", expected, nsa)
}
}
func TestGenericArrayScanScannerSliceBytes(t *testing.T) {
src, expected, nss := []byte(`{NULL,abc,"\""}`),
[]sql.NullString{{}, {String: `abc`, Valid: true}, {String: `"`, Valid: true}},
[]sql.NullString{{String: ``, Valid: true}, {}, {}, {}, {}}
if err := (GenericArray{&nss}).Scan(src); err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if !reflect.DeepEqual(nss, expected) {
t.Errorf("Expected %v, got %v", expected, nss)
}
}
func BenchmarkGenericArrayScanScannerSliceBytes(b *testing.B) {
var a GenericArray
var x interface{} = []byte(`{a,b,c,d,e,f,g,h,i,j}`)
var y interface{} = []byte(`{"\a","\b","\c","\d","\e","\f","\g","\h","\i","\j"}`)
for i := 0; i < b.N; i++ {
a = GenericArray{new([]sql.NullString)}
a.Scan(x)
a = GenericArray{new([]sql.NullString)}
a.Scan(y)
}
}
func TestGenericArrayScanScannerSliceString(t *testing.T) {
src, expected, nss := `{NULL,"\"",xyz}`,
[]sql.NullString{{}, {String: `"`, Valid: true}, {String: `xyz`, Valid: true}},
[]sql.NullString{{String: ``, Valid: true}, {}, {}}
if err := (GenericArray{&nss}).Scan(src); err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if !reflect.DeepEqual(nss, expected) {
t.Errorf("Expected %v, got %v", expected, nss)
}
}
type TildeNullInt64 struct{ sql.NullInt64 }
func (TildeNullInt64) ArrayDelimiter() string { return "~" }
func TestGenericArrayScanDelimiter(t *testing.T) {
src, expected, tnis := `{12~NULL~76}`,
[]TildeNullInt64{{sql.NullInt64{Int64: 12, Valid: true}}, {}, {sql.NullInt64{Int64: 76, Valid: true}}},
[]TildeNullInt64{{sql.NullInt64{Int64: 0, Valid: true}}, {}}
if err := (GenericArray{&tnis}).Scan(src); err != nil {
t.Fatalf("Expected no error for %#v, got %v", src, err)
}
if !reflect.DeepEqual(tnis, expected) {
t.Errorf("Expected %v for %#v, got %v", expected, src, tnis)
}
}
func TestGenericArrayScanErrors(t *testing.T) {
var sa [1]string
var nis []sql.NullInt64
var pss *[]string
for _, tt := range []struct {
src, dest interface{}
err string
}{
{nil, pss, "destination *[]string is nil"},
{`{`, &sa, "unable to parse"},
{`{}`, &sa, "cannot convert ARRAY[0] to [1]string"},
{`{x,x}`, &sa, "cannot convert ARRAY[2] to [1]string"},
{`{x}`, &nis, `parsing array element index 0: converting`},
} {
err := GenericArray{tt.dest}.Scan(tt.src)
if err == nil {
t.Fatalf("Expected error for [%#v %#v]", tt.src, tt.dest)
}
if !strings.Contains(err.Error(), tt.err) {
t.Errorf("Expected error to contain %q for [%#v %#v], got %q", tt.err, tt.src, tt.dest, err)
}
}
}
func TestGenericArrayValueUnsupported(t *testing.T) {
_, err := GenericArray{true}.Value()
if err == nil {
t.Fatal("Expected error for bool")
}
if !strings.Contains(err.Error(), "bool to array") {
t.Errorf("Expected type to be mentioned, got %q", err)
}
}
type ByteArrayValuer [1]byte
type ByteSliceValuer []byte
type FuncArrayValuer struct {
delimiter func() string
value func() (driver.Value, error)
}
func (a ByteArrayValuer) Value() (driver.Value, error) { return a[:], nil }
func (b ByteSliceValuer) Value() (driver.Value, error) { return []byte(b), nil }
func (f FuncArrayValuer) ArrayDelimiter() string { return f.delimiter() }
func (f FuncArrayValuer) Value() (driver.Value, error) { return f.value() }
func TestGenericArrayValue(t *testing.T) {
result, err := GenericArray{nil}.Value()
if err != nil {
t.Fatalf("Expected no error for nil, got %v", err)
}
if result != nil {
t.Errorf("Expected nil, got %q", result)
}
Tilde := func(v driver.Value) FuncArrayValuer {
return FuncArrayValuer{
func() string { return "~" },
func() (driver.Value, error) { return v, nil }}
}
for _, tt := range []struct {
result string
input interface{}
}{
{`{}`, []bool{}},
{`{true}`, []bool{true}},
{`{true,false}`, []bool{true, false}},
{`{true,false}`, [2]bool{true, false}},
{`{}`, [][]int{{}}},
{`{}`, [][]int{{}, {}}},
{`{{1}}`, [][]int{{1}}},
{`{{1},{2}}`, [][]int{{1}, {2}}},
{`{{1,2},{3,4}}`, [][]int{{1, 2}, {3, 4}}},
{`{{1,2},{3,4}}`, [2][2]int{{1, 2}, {3, 4}}},
{`{"a","\\b","c\"","d,e"}`, []string{`a`, `\b`, `c"`, `d,e`}},
{`{"a","\\b","c\"","d,e"}`, [][]byte{{'a'}, {'\\', 'b'}, {'c', '"'}, {'d', ',', 'e'}}},
{`{NULL}`, []*int{nil}},
{`{0,NULL}`, []*int{new(int), nil}},
{`{NULL}`, []sql.NullString{{}}},
{`{"\"",NULL}`, []sql.NullString{{String: `"`, Valid: true}, {}}},
{`{"a","b"}`, []ByteArrayValuer{{'a'}, {'b'}}},
{`{{"a","b"},{"c","d"}}`, [][]ByteArrayValuer{{{'a'}, {'b'}}, {{'c'}, {'d'}}}},
{`{"e","f"}`, []ByteSliceValuer{{'e'}, {'f'}}},
{`{{"e","f"},{"g","h"}}`, [][]ByteSliceValuer{{{'e'}, {'f'}}, {{'g'}, {'h'}}}},
{`{1~2}`, []FuncArrayValuer{Tilde(int64(1)), Tilde(int64(2))}},
{`{{1~2}~{3~4}}`, [][]FuncArrayValuer{{Tilde(int64(1)), Tilde(int64(2))}, {Tilde(int64(3)), Tilde(int64(4))}}},
} {
result, err := GenericArray{tt.input}.Value()
if err != nil {
t.Fatalf("Expected no error for %q, got %v", tt.input, err)
}
if !reflect.DeepEqual(result, tt.result) {
t.Errorf("Expected %q for %q, got %q", tt.result, tt.input, result)
}
}
}
func TestGenericArrayValueErrors(t *testing.T) {
var v []interface{}
v = []interface{}{func() {}}
if _, err := (GenericArray{v}).Value(); err == nil {
t.Errorf("Expected error for %q, got nil", v)
}
v = []interface{}{nil, func() {}}
if _, err := (GenericArray{v}).Value(); err == nil {
t.Errorf("Expected error for %q, got nil", v)
}
}
func BenchmarkGenericArrayValueBools(b *testing.B) {
rand.Seed(1)
x := make([]bool, 10)
for i := 0; i < len(x); i++ {
x[i] = rand.Intn(2) == 0
}
a := GenericArray{x}
for i := 0; i < b.N; i++ {
a.Value()
}
}
func BenchmarkGenericArrayValueFloat64s(b *testing.B) {
rand.Seed(1)
x := make([]float64, 10)
for i := 0; i < len(x); i++ {
x[i] = rand.NormFloat64()
}
a := GenericArray{x}
for i := 0; i < b.N; i++ {
a.Value()
}
}
func BenchmarkGenericArrayValueInt64s(b *testing.B) {
rand.Seed(1)
x := make([]int64, 10)
for i := 0; i < len(x); i++ {
x[i] = rand.Int63()
}
a := GenericArray{x}
for i := 0; i < b.N; i++ {
a.Value()
}
}
func BenchmarkGenericArrayValueByteSlices(b *testing.B) {
x := make([][]byte, 10)
for i := 0; i < len(x); i++ {
x[i] = bytes.Repeat([]byte(`abc"def\ghi`), 5)
}
a := GenericArray{x}
for i := 0; i < b.N; i++ {
a.Value()
}
}
func BenchmarkGenericArrayValueStrings(b *testing.B) {
x := make([]string, 10)
for i := 0; i < len(x); i++ {
x[i] = strings.Repeat(`abc"def\ghi`, 5)
}
a := GenericArray{x}
for i := 0; i < b.N; i++ {
a.Value()
}
}

View file

@ -305,9 +305,6 @@ var importsBasedOnType = map[string]imports{
"types.BytesArray": {
thirdParty: importList{`"github.com/vattle/sqlboiler/boil/types"`},
},
"types.GenericArray": {
thirdParty: importList{`"github.com/vattle/sqlboiler/boil/types"`},
},
"types.Int64Array": {
thirdParty: importList{`"github.com/vattle/sqlboiler/boil/types"`},
},

View file

@ -194,3 +194,22 @@ create table enemies (
enemies character varying,
primary key (enemies)
);
create table fun_arrays (
id serial,
fun_one integer[] null,
fun_two integer[] not null,
fun_three boolean[] null,
fun_four boolean[] not null,
fun_five varchar[] null,
fun_six varchar[] not null,
fun_seven decimal[] null,
fun_eight decimal[] not null,
fun_nine bytea[] null,
fun_ten bytea[] not null,
fun_eleven jsonb[] null,
fun_twelve jsonb[] not null,
fun_thirteen json[] null,
fun_fourteen json[] not null,
primary key (id)
)