package pq import ( "bytes" "database/sql" "database/sql/driver" "math/rand" "reflect" "strings" "testing" ) func TestParseArray(t *testing.T) { for _, tt := range []struct { input string delim string dims []int elems [][]byte }{ {`{}`, `,`, nil, [][]byte{}}, {`{NULL}`, `,`, []int{1}, [][]byte{nil}}, {`{a}`, `,`, []int{1}, [][]byte{{'a'}}}, {`{a,b}`, `,`, []int{2}, [][]byte{{'a'}, {'b'}}}, {`{{a,b}}`, `,`, []int{1, 2}, [][]byte{{'a'}, {'b'}}}, {`{{a},{b}}`, `,`, []int{2, 1}, [][]byte{{'a'}, {'b'}}}, {`{{{a,b},{c,d},{e,f}}}`, `,`, []int{1, 3, 2}, [][]byte{ {'a'}, {'b'}, {'c'}, {'d'}, {'e'}, {'f'}, }}, {`{""}`, `,`, []int{1}, [][]byte{{}}}, {`{","}`, `,`, []int{1}, [][]byte{{','}}}, {`{",",","}`, `,`, []int{2}, [][]byte{{','}, {','}}}, {`{{",",","}}`, `,`, []int{1, 2}, [][]byte{{','}, {','}}}, {`{{","},{","}}`, `,`, []int{2, 1}, [][]byte{{','}, {','}}}, {`{{{",",","},{",",","},{",",","}}}`, `,`, []int{1, 3, 2}, [][]byte{ {','}, {','}, {','}, {','}, {','}, {','}, }}, {`{"\"}"}`, `,`, []int{1}, [][]byte{{'"', '}'}}}, {`{"\"","\""}`, `,`, []int{2}, [][]byte{{'"'}, {'"'}}}, {`{{"\"","\""}}`, `,`, []int{1, 2}, [][]byte{{'"'}, {'"'}}}, {`{{"\""},{"\""}}`, `,`, []int{2, 1}, [][]byte{{'"'}, {'"'}}}, {`{{{"\"","\""},{"\"","\""},{"\"","\""}}}`, `,`, []int{1, 3, 2}, [][]byte{ {'"'}, {'"'}, {'"'}, {'"'}, {'"'}, {'"'}, }}, {`{axyzb}`, `xyz`, []int{2}, [][]byte{{'a'}, {'b'}}}, } { dims, elems, err := parseArray([]byte(tt.input), []byte(tt.delim)) if err != nil { t.Fatalf("Expected no error for %q, got %q", tt.input, err) } if !reflect.DeepEqual(dims, tt.dims) { t.Errorf("Expected %v dimensions for %q, got %v", tt.dims, tt.input, dims) } if !reflect.DeepEqual(elems, tt.elems) { t.Errorf("Expected %v elements for %q, got %v", tt.elems, tt.input, elems) } } } func TestParseArrayError(t *testing.T) { for _, tt := range []struct { input, err string }{ {``, "expected '{' at offset 0"}, {`x`, "expected '{' at offset 0"}, {`}`, "expected '{' at offset 0"}, {`{`, "expected '}' at offset 1"}, {`{{}`, "expected '}' at offset 3"}, {`{}}`, "unexpected '}' at offset 2"}, {`{,}`, "unexpected ',' at offset 1"}, {`{,x}`, "unexpected ',' at offset 1"}, {`{x,}`, "unexpected '}' at offset 3"}, {`{x,{`, "unexpected '{' at offset 3"}, {`{x},`, "unexpected ',' at offset 3"}, {`{x}}`, "unexpected '}' at offset 3"}, {`{{x}`, "expected '}' at offset 4"}, {`{""x}`, "unexpected 'x' at offset 3"}, {`{{a},{b,c}}`, "multidimensional arrays must have elements with matching dimensions"}, } { _, _, err := parseArray([]byte(tt.input), []byte{','}) if err == nil { t.Fatalf("Expected error for %q, got none", tt.input) } if !strings.Contains(err.Error(), tt.err) { t.Errorf("Expected error to contain %q for %q, got %q", tt.err, tt.input, err) } } } func TestArrayScanner(t *testing.T) { var s sql.Scanner = Array(&[]bool{}) if _, ok := s.(*BoolArray); !ok { t.Errorf("Expected *BoolArray, got %T", s) } s = Array(&[]float64{}) if _, ok := s.(*Float64Array); !ok { t.Errorf("Expected *Float64Array, got %T", s) } s = Array(&[]int64{}) if _, ok := s.(*Int64Array); !ok { t.Errorf("Expected *Int64Array, got %T", s) } s = Array(&[]string{}) 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) { var v driver.Valuer = Array([]bool{}) if _, ok := v.(*BoolArray); !ok { t.Errorf("Expected *BoolArray, got %T", v) } v = Array([]float64{}) if _, ok := v.(*Float64Array); !ok { t.Errorf("Expected *Float64Array, got %T", v) } v = Array([]int64{}) if _, ok := v.(*Int64Array); !ok { t.Errorf("Expected *Int64Array, got %T", v) } v = Array([]string{}) 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) { var arr BoolArray err := arr.Scan(1) if err == nil { t.Fatal("Expected error when scanning from int") } if !strings.Contains(err.Error(), "int to BoolArray") { t.Errorf("Expected type to be mentioned when scanning, got %q", err) } } func TestBoolArrayScanEmpty(t *testing.T) { var arr BoolArray err := arr.Scan(`{}`) if err != nil { t.Fatalf("Expected no error, got %v", err) } if arr == nil || len(arr) != 0 { t.Errorf("Expected empty, got %#v", arr) } } func TestBoolArrayScanNil(t *testing.T) { arr := BoolArray{true, true, true} err := arr.Scan(nil) if err != nil { t.Fatalf("Expected no error, got %v", err) } if arr != nil { t.Errorf("Expected nil, got %+v", arr) } } var BoolArrayStringTests = []struct { str string arr BoolArray }{ {`{}`, BoolArray{}}, {`{t}`, BoolArray{true}}, {`{f,t}`, BoolArray{false, true}}, } func TestBoolArrayScanBytes(t *testing.T) { for _, tt := range BoolArrayStringTests { bytes := []byte(tt.str) arr := BoolArray{true, true, true} err := arr.Scan(bytes) if err != nil { t.Fatalf("Expected no error for %q, got %v", bytes, err) } if !reflect.DeepEqual(arr, tt.arr) { t.Errorf("Expected %+v for %q, got %+v", tt.arr, bytes, arr) } } } func BenchmarkBoolArrayScanBytes(b *testing.B) { var a BoolArray var x interface{} = []byte(`{t,f,t,f,t,f,t,f,t,f}`) for i := 0; i < b.N; i++ { a = BoolArray{} a.Scan(x) } } func TestBoolArrayScanString(t *testing.T) { for _, tt := range BoolArrayStringTests { arr := BoolArray{true, true, true} err := arr.Scan(tt.str) if err != nil { t.Fatalf("Expected no error for %q, got %v", tt.str, err) } if !reflect.DeepEqual(arr, tt.arr) { t.Errorf("Expected %+v for %q, got %+v", tt.arr, tt.str, arr) } } } func TestBoolArrayScanError(t *testing.T) { for _, tt := range []struct { input, err string }{ {``, "unable to parse array"}, {`{`, "unable to parse array"}, {`{{t},{f}}`, "cannot convert ARRAY[2][1] to BoolArray"}, {`{NULL}`, `could not parse boolean array index 0: invalid boolean ""`}, {`{a}`, `could not parse boolean array index 0: invalid boolean "a"`}, {`{t,b}`, `could not parse boolean array index 1: invalid boolean "b"`}, {`{t,f,cd}`, `could not parse boolean array index 2: invalid boolean "cd"`}, } { arr := BoolArray{true, true, true} err := arr.Scan(tt.input) if err == nil { t.Fatalf("Expected error for %q, got none", tt.input) } if !strings.Contains(err.Error(), tt.err) { t.Errorf("Expected error to contain %q for %q, got %q", tt.err, tt.input, err) } if !reflect.DeepEqual(arr, BoolArray{true, true, true}) { t.Errorf("Expected destination not to change for %q, got %+v", tt.input, arr) } } } func TestBoolArrayValue(t *testing.T) { result, err := BoolArray(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) } result, err = BoolArray([]bool{}).Value() if err != nil { t.Fatalf("Expected no error for empty, got %v", err) } if expected := `{}`; !reflect.DeepEqual(result, expected) { t.Errorf("Expected empty, got %q", result) } result, err = BoolArray([]bool{false, true, false}).Value() if err != nil { t.Fatalf("Expected no error, got %v", err) } if expected := `{f,t,f}`; !reflect.DeepEqual(result, expected) { t.Errorf("Expected %q, got %q", expected, result) } } func BenchmarkBoolArrayValue(b *testing.B) { rand.Seed(1) x := make([]bool, 10) for i := 0; i < len(x); i++ { x[i] = rand.Intn(2) == 0 } a := BoolArray(x) for i := 0; i < b.N; i++ { a.Value() } } func TestByteaArrayScanUnsupported(t *testing.T) { var arr ByteaArray err := arr.Scan(1) if err == nil { t.Fatal("Expected error when scanning from int") } if !strings.Contains(err.Error(), "int to ByteaArray") { t.Errorf("Expected type to be mentioned when scanning, got %q", err) } } func TestByteaArrayScanEmpty(t *testing.T) { var arr ByteaArray err := arr.Scan(`{}`) if err != nil { t.Fatalf("Expected no error, got %v", err) } if arr == nil || len(arr) != 0 { t.Errorf("Expected empty, got %#v", arr) } } func TestByteaArrayScanNil(t *testing.T) { arr := ByteaArray{{2}, {6}, {0, 0}} err := arr.Scan(nil) if err != nil { t.Fatalf("Expected no error, got %v", err) } if arr != nil { t.Errorf("Expected nil, got %+v", arr) } } var ByteaArrayStringTests = []struct { str string arr ByteaArray }{ {`{}`, ByteaArray{}}, {`{NULL}`, ByteaArray{nil}}, {`{"\\xfeff"}`, ByteaArray{{'\xFE', '\xFF'}}}, {`{"\\xdead","\\xbeef"}`, ByteaArray{{'\xDE', '\xAD'}, {'\xBE', '\xEF'}}}, } func TestByteaArrayScanBytes(t *testing.T) { for _, tt := range ByteaArrayStringTests { bytes := []byte(tt.str) arr := ByteaArray{{2}, {6}, {0, 0}} err := arr.Scan(bytes) if err != nil { t.Fatalf("Expected no error for %q, got %v", bytes, err) } if !reflect.DeepEqual(arr, tt.arr) { t.Errorf("Expected %+v for %q, got %+v", tt.arr, bytes, arr) } } } func BenchmarkByteaArrayScanBytes(b *testing.B) { var a ByteaArray var x interface{} = []byte(`{"\\xfe","\\xff","\\xdead","\\xbeef","\\xfe","\\xff","\\xdead","\\xbeef","\\xfe","\\xff"}`) for i := 0; i < b.N; i++ { a = ByteaArray{} a.Scan(x) } } func TestByteaArrayScanString(t *testing.T) { for _, tt := range ByteaArrayStringTests { arr := ByteaArray{{2}, {6}, {0, 0}} err := arr.Scan(tt.str) if err != nil { t.Fatalf("Expected no error for %q, got %v", tt.str, err) } if !reflect.DeepEqual(arr, tt.arr) { t.Errorf("Expected %+v for %q, got %+v", tt.arr, tt.str, arr) } } } func TestByteaArrayScanError(t *testing.T) { for _, tt := range []struct { input, err string }{ {``, "unable to parse array"}, {`{`, "unable to parse array"}, {`{{"\\xfeff"},{"\\xbeef"}}`, "cannot convert ARRAY[2][1] to ByteaArray"}, {`{"\\abc"}`, "could not parse bytea array index 0: could not parse bytea value"}, } { arr := ByteaArray{{2}, {6}, {0, 0}} err := arr.Scan(tt.input) if err == nil { t.Fatalf("Expected error for %q, got none", tt.input) } if !strings.Contains(err.Error(), tt.err) { t.Errorf("Expected error to contain %q for %q, got %q", tt.err, tt.input, err) } if !reflect.DeepEqual(arr, ByteaArray{{2}, {6}, {0, 0}}) { t.Errorf("Expected destination not to change for %q, got %+v", tt.input, arr) } } } func TestByteaArrayValue(t *testing.T) { result, err := ByteaArray(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) } result, err = ByteaArray([][]byte{}).Value() if err != nil { t.Fatalf("Expected no error for empty, got %v", err) } if expected := `{}`; !reflect.DeepEqual(result, expected) { t.Errorf("Expected empty, got %q", result) } result, err = ByteaArray([][]byte{{'\xDE', '\xAD', '\xBE', '\xEF'}, {'\xFE', '\xFF'}, {}}).Value() if err != nil { t.Fatalf("Expected no error, got %v", err) } if expected := `{"\\xdeadbeef","\\xfeff","\\x"}`; !reflect.DeepEqual(result, expected) { t.Errorf("Expected %q, got %q", expected, result) } } func BenchmarkByteaArrayValue(b *testing.B) { rand.Seed(1) x := make([][]byte, 10) for i := 0; i < len(x); i++ { x[i] = make([]byte, len(x)) for j := 0; j < len(x); j++ { x[i][j] = byte(rand.Int()) } } a := ByteaArray(x) for i := 0; i < b.N; i++ { a.Value() } } func TestFloat64ArrayScanUnsupported(t *testing.T) { var arr Float64Array err := arr.Scan(true) if err == nil { t.Fatal("Expected error when scanning from bool") } if !strings.Contains(err.Error(), "bool to Float64Array") { t.Errorf("Expected type to be mentioned when scanning, got %q", err) } } func TestFloat64ArrayScanEmpty(t *testing.T) { var arr Float64Array err := arr.Scan(`{}`) if err != nil { t.Fatalf("Expected no error, got %v", err) } if arr == nil || len(arr) != 0 { t.Errorf("Expected empty, got %#v", arr) } } func TestFloat64ArrayScanNil(t *testing.T) { arr := Float64Array{5, 5, 5} err := arr.Scan(nil) if err != nil { t.Fatalf("Expected no error, got %v", err) } if arr != nil { t.Errorf("Expected nil, got %+v", arr) } } var Float64ArrayStringTests = []struct { str string arr Float64Array }{ {`{}`, Float64Array{}}, {`{1.2}`, Float64Array{1.2}}, {`{3.456,7.89}`, Float64Array{3.456, 7.89}}, {`{3,1,2}`, Float64Array{3, 1, 2}}, } func TestFloat64ArrayScanBytes(t *testing.T) { for _, tt := range Float64ArrayStringTests { bytes := []byte(tt.str) arr := Float64Array{5, 5, 5} err := arr.Scan(bytes) if err != nil { t.Fatalf("Expected no error for %q, got %v", bytes, err) } if !reflect.DeepEqual(arr, tt.arr) { t.Errorf("Expected %+v for %q, got %+v", tt.arr, bytes, arr) } } } func BenchmarkFloat64ArrayScanBytes(b *testing.B) { var a Float64Array var x interface{} = []byte(`{1.2,3.4,5.6,7.8,9.01,2.34,5.67,8.90,1.234,5.678}`) for i := 0; i < b.N; i++ { a = Float64Array{} a.Scan(x) } } func TestFloat64ArrayScanString(t *testing.T) { for _, tt := range Float64ArrayStringTests { arr := Float64Array{5, 5, 5} err := arr.Scan(tt.str) if err != nil { t.Fatalf("Expected no error for %q, got %v", tt.str, err) } if !reflect.DeepEqual(arr, tt.arr) { t.Errorf("Expected %+v for %q, got %+v", tt.arr, tt.str, arr) } } } func TestFloat64ArrayScanError(t *testing.T) { for _, tt := range []struct { input, err string }{ {``, "unable to parse array"}, {`{`, "unable to parse array"}, {`{{5.6},{7.8}}`, "cannot convert ARRAY[2][1] to Float64Array"}, {`{NULL}`, "parsing array element index 0:"}, {`{a}`, "parsing array element index 0:"}, {`{5.6,a}`, "parsing array element index 1:"}, {`{5.6,7.8,a}`, "parsing array element index 2:"}, } { arr := Float64Array{5, 5, 5} err := arr.Scan(tt.input) if err == nil { t.Fatalf("Expected error for %q, got none", tt.input) } if !strings.Contains(err.Error(), tt.err) { t.Errorf("Expected error to contain %q for %q, got %q", tt.err, tt.input, err) } if !reflect.DeepEqual(arr, Float64Array{5, 5, 5}) { t.Errorf("Expected destination not to change for %q, got %+v", tt.input, arr) } } } func TestFloat64ArrayValue(t *testing.T) { result, err := Float64Array(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) } result, err = Float64Array([]float64{}).Value() if err != nil { t.Fatalf("Expected no error for empty, got %v", err) } if expected := `{}`; !reflect.DeepEqual(result, expected) { t.Errorf("Expected empty, got %q", result) } result, err = Float64Array([]float64{1.2, 3.4, 5.6}).Value() if err != nil { t.Fatalf("Expected no error, got %v", err) } if expected := `{1.2,3.4,5.6}`; !reflect.DeepEqual(result, expected) { t.Errorf("Expected %q, got %q", expected, result) } } func BenchmarkFloat64ArrayValue(b *testing.B) { rand.Seed(1) x := make([]float64, 10) for i := 0; i < len(x); i++ { x[i] = rand.NormFloat64() } a := Float64Array(x) for i := 0; i < b.N; i++ { a.Value() } } func TestInt64ArrayScanUnsupported(t *testing.T) { var arr Int64Array err := arr.Scan(true) if err == nil { t.Fatal("Expected error when scanning from bool") } if !strings.Contains(err.Error(), "bool to Int64Array") { t.Errorf("Expected type to be mentioned when scanning, got %q", err) } } func TestInt64ArrayScanEmpty(t *testing.T) { var arr Int64Array err := arr.Scan(`{}`) if err != nil { t.Fatalf("Expected no error, got %v", err) } if arr == nil || len(arr) != 0 { t.Errorf("Expected empty, got %#v", arr) } } func TestInt64ArrayScanNil(t *testing.T) { arr := Int64Array{5, 5, 5} err := arr.Scan(nil) if err != nil { t.Fatalf("Expected no error, got %v", err) } if arr != nil { t.Errorf("Expected nil, got %+v", arr) } } var Int64ArrayStringTests = []struct { str string arr Int64Array }{ {`{}`, Int64Array{}}, {`{12}`, Int64Array{12}}, {`{345,678}`, Int64Array{345, 678}}, } func TestInt64ArrayScanBytes(t *testing.T) { for _, tt := range Int64ArrayStringTests { bytes := []byte(tt.str) arr := Int64Array{5, 5, 5} err := arr.Scan(bytes) if err != nil { t.Fatalf("Expected no error for %q, got %v", bytes, err) } if !reflect.DeepEqual(arr, tt.arr) { t.Errorf("Expected %+v for %q, got %+v", tt.arr, bytes, arr) } } } func BenchmarkInt64ArrayScanBytes(b *testing.B) { var a Int64Array var x interface{} = []byte(`{1,2,3,4,5,6,7,8,9,0}`) for i := 0; i < b.N; i++ { a = Int64Array{} a.Scan(x) } } func TestInt64ArrayScanString(t *testing.T) { for _, tt := range Int64ArrayStringTests { arr := Int64Array{5, 5, 5} err := arr.Scan(tt.str) if err != nil { t.Fatalf("Expected no error for %q, got %v", tt.str, err) } if !reflect.DeepEqual(arr, tt.arr) { t.Errorf("Expected %+v for %q, got %+v", tt.arr, tt.str, arr) } } } func TestInt64ArrayScanError(t *testing.T) { for _, tt := range []struct { input, err string }{ {``, "unable to parse array"}, {`{`, "unable to parse array"}, {`{{5},{6}}`, "cannot convert ARRAY[2][1] to Int64Array"}, {`{NULL}`, "parsing array element index 0:"}, {`{a}`, "parsing array element index 0:"}, {`{5,a}`, "parsing array element index 1:"}, {`{5,6,a}`, "parsing array element index 2:"}, } { arr := Int64Array{5, 5, 5} err := arr.Scan(tt.input) if err == nil { t.Fatalf("Expected error for %q, got none", tt.input) } if !strings.Contains(err.Error(), tt.err) { t.Errorf("Expected error to contain %q for %q, got %q", tt.err, tt.input, err) } if !reflect.DeepEqual(arr, Int64Array{5, 5, 5}) { t.Errorf("Expected destination not to change for %q, got %+v", tt.input, arr) } } } func TestInt64ArrayValue(t *testing.T) { result, err := Int64Array(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) } result, err = Int64Array([]int64{}).Value() if err != nil { t.Fatalf("Expected no error for empty, got %v", err) } if expected := `{}`; !reflect.DeepEqual(result, expected) { t.Errorf("Expected empty, got %q", result) } result, err = Int64Array([]int64{1, 2, 3}).Value() if err != nil { t.Fatalf("Expected no error, got %v", err) } if expected := `{1,2,3}`; !reflect.DeepEqual(result, expected) { t.Errorf("Expected %q, got %q", expected, result) } } func BenchmarkInt64ArrayValue(b *testing.B) { rand.Seed(1) x := make([]int64, 10) for i := 0; i < len(x); i++ { x[i] = rand.Int63() } a := Int64Array(x) for i := 0; i < b.N; i++ { a.Value() } } func TestStringArrayScanUnsupported(t *testing.T) { var arr StringArray err := arr.Scan(true) if err == nil { t.Fatal("Expected error when scanning from bool") } if !strings.Contains(err.Error(), "bool to StringArray") { t.Errorf("Expected type to be mentioned when scanning, got %q", err) } } func TestStringArrayScanEmpty(t *testing.T) { var arr StringArray err := arr.Scan(`{}`) if err != nil { t.Fatalf("Expected no error, got %v", err) } if arr == nil || len(arr) != 0 { t.Errorf("Expected empty, got %#v", arr) } } func TestStringArrayScanNil(t *testing.T) { arr := StringArray{"x", "x", "x"} err := arr.Scan(nil) if err != nil { t.Fatalf("Expected no error, got %v", err) } if arr != nil { t.Errorf("Expected nil, got %+v", arr) } } var StringArrayStringTests = []struct { str string arr StringArray }{ {`{}`, StringArray{}}, {`{t}`, StringArray{"t"}}, {`{f,1}`, StringArray{"f", "1"}}, {`{"a\\b","c d",","}`, StringArray{"a\\b", "c d", ","}}, } func TestStringArrayScanBytes(t *testing.T) { for _, tt := range StringArrayStringTests { bytes := []byte(tt.str) arr := StringArray{"x", "x", "x"} err := arr.Scan(bytes) if err != nil { t.Fatalf("Expected no error for %q, got %v", bytes, err) } if !reflect.DeepEqual(arr, tt.arr) { t.Errorf("Expected %+v for %q, got %+v", tt.arr, bytes, arr) } } } func BenchmarkStringArrayScanBytes(b *testing.B) { var a StringArray 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 = StringArray{} a.Scan(x) a = StringArray{} a.Scan(y) } } func TestStringArrayScanString(t *testing.T) { for _, tt := range StringArrayStringTests { arr := StringArray{"x", "x", "x"} err := arr.Scan(tt.str) if err != nil { t.Fatalf("Expected no error for %q, got %v", tt.str, err) } if !reflect.DeepEqual(arr, tt.arr) { t.Errorf("Expected %+v for %q, got %+v", tt.arr, tt.str, arr) } } } func TestStringArrayScanError(t *testing.T) { for _, tt := range []struct { input, err string }{ {``, "unable to parse array"}, {`{`, "unable to parse array"}, {`{{a},{b}}`, "cannot convert ARRAY[2][1] to StringArray"}, {`{NULL}`, "parsing array element index 0: cannot convert nil to string"}, {`{a,NULL}`, "parsing array element index 1: cannot convert nil to string"}, {`{a,b,NULL}`, "parsing array element index 2: cannot convert nil to string"}, } { arr := StringArray{"x", "x", "x"} err := arr.Scan(tt.input) if err == nil { t.Fatalf("Expected error for %q, got none", tt.input) } if !strings.Contains(err.Error(), tt.err) { t.Errorf("Expected error to contain %q for %q, got %q", tt.err, tt.input, err) } if !reflect.DeepEqual(arr, StringArray{"x", "x", "x"}) { t.Errorf("Expected destination not to change for %q, got %+v", tt.input, arr) } } } func TestStringArrayValue(t *testing.T) { result, err := StringArray(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) } result, err = StringArray([]string{}).Value() if err != nil { t.Fatalf("Expected no error for empty, got %v", err) } if expected := `{}`; !reflect.DeepEqual(result, expected) { t.Errorf("Expected empty, got %q", result) } result, err = StringArray([]string{`a`, `\b`, `c"`, `d,e`}).Value() if err != nil { t.Fatalf("Expected no error, got %v", err) } if expected := `{"a","\\b","c\"","d,e"}`; !reflect.DeepEqual(result, expected) { t.Errorf("Expected %q, got %q", expected, result) } } func BenchmarkStringArrayValue(b *testing.B) { x := make([]string, 10) for i := 0; i < len(x); i++ { x[i] = strings.Repeat(`abc"def\ghi`, 5) } a := StringArray(x) for i := 0; i < b.N; i++ { a.Value() } } func TestGenericArrayScanUnsupported(t *testing.T) { var s string var ss []string var nsa [1]sql.NullString for _, tt := range []struct { src, dest interface{} err string }{ {nil, nil, "destination 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"}, {nil, &nsa, " to [1]sql.NullString"}, {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 TestGenericArrayScanScannerSliceEmpty(t *testing.T) { var nss []sql.NullString if err := (GenericArray{&nss}).Scan(`{}`); err != nil { t.Fatalf("Expected no error, got %v", err) } if nss == nil || len(nss) != 0 { t.Errorf("Expected empty, got %#v", nss) } } func TestGenericArrayScanScannerSliceNil(t *testing.T) { nss := []sql.NullString{{String: ``, Valid: true}, {}} if err := (GenericArray{&nss}).Scan(nil); err != nil { t.Fatalf("Expected no error, got %v", err) } if nss != nil { t.Errorf("Expected nil, got %+v", nss) } } 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) } for _, tt := range []interface{}{ []bool(nil), [][]int(nil), []*int(nil), []sql.NullString(nil), } { result, err := GenericArray{tt}.Value() if err != nil { t.Fatalf("Expected no error for %#v, got %v", tt, err) } if result != nil { t.Errorf("Expected nil for %#v, got %q", tt, 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) { 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() } } func TestArrayScanBackend(t *testing.T) { db := openTestConn(t) defer db.Close() for _, tt := range []struct { s string d sql.Scanner e interface{} }{ {`ARRAY[true, false]`, new(BoolArray), &BoolArray{true, false}}, {`ARRAY[E'\\xdead', E'\\xbeef']`, new(ByteaArray), &ByteaArray{{'\xDE', '\xAD'}, {'\xBE', '\xEF'}}}, {`ARRAY[1.2, 3.4]`, new(Float64Array), &Float64Array{1.2, 3.4}}, {`ARRAY[1, 2, 3]`, new(Int64Array), &Int64Array{1, 2, 3}}, {`ARRAY['a', E'\\b', 'c"', 'd,e']`, new(StringArray), &StringArray{`a`, `\b`, `c"`, `d,e`}}, } { err := db.QueryRow(`SELECT ` + tt.s).Scan(tt.d) if err != nil { t.Errorf("Expected no error when scanning %s into %T, got %v", tt.s, tt.d, err) } if !reflect.DeepEqual(tt.d, tt.e) { t.Errorf("Expected %v when scanning %s into %T, got %v", tt.e, tt.s, tt.d, tt.d) } } } func TestArrayValueBackend(t *testing.T) { db := openTestConn(t) defer db.Close() for _, tt := range []struct { s string v driver.Valuer }{ {`ARRAY[true, false]`, BoolArray{true, false}}, {`ARRAY[E'\\xdead', E'\\xbeef']`, ByteaArray{{'\xDE', '\xAD'}, {'\xBE', '\xEF'}}}, {`ARRAY[1.2, 3.4]`, Float64Array{1.2, 3.4}}, {`ARRAY[1, 2, 3]`, Int64Array{1, 2, 3}}, {`ARRAY['a', E'\\b', 'c"', 'd,e']`, StringArray{`a`, `\b`, `c"`, `d,e`}}, } { var x int err := db.QueryRow(`SELECT 1 WHERE `+tt.s+` <> $1`, tt.v).Scan(&x) if err != sql.ErrNoRows { t.Errorf("Expected %v to equal %s, got %v", tt.v, tt.s, err) } } }