1/* 2Copyright 2017 Google LLC 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15*/ 16 17package spanner 18 19import ( 20 "math" 21 "testing" 22 "time" 23 24 "cloud.google.com/go/civil" 25 "github.com/golang/protobuf/proto" 26 proto3 "github.com/golang/protobuf/ptypes/struct" 27 sppb "google.golang.org/genproto/googleapis/spanner/v1" 28) 29 30func TestConvertParams(t *testing.T) { 31 st := Statement{ 32 SQL: "SELECT id from t_foo WHERE col = @var", 33 Params: map[string]interface{}{"var": nil}, 34 } 35 var ( 36 t1, _ = time.Parse(time.RFC3339Nano, "2016-11-15T15:04:05.999999999Z") 37 // Boundaries 38 t2, _ = time.Parse(time.RFC3339Nano, "0001-01-01T00:00:00.000000000Z") 39 t3, _ = time.Parse(time.RFC3339Nano, "9999-12-31T23:59:59.999999999Z") 40 d1, _ = civil.ParseDate("2016-11-15") 41 // Boundaries 42 d2, _ = civil.ParseDate("0001-01-01") 43 d3, _ = civil.ParseDate("9999-12-31") 44 ) 45 46 type staticStruct struct { 47 Field int `spanner:"field"` 48 } 49 type CustomInt int64 50 type staticStructWithCustomType struct { 51 Field CustomInt `spanner:"field"` 52 } 53 54 var ( 55 s1 = staticStruct{10} 56 s2 = staticStruct{20} 57 s3 = staticStructWithCustomType{30} 58 ) 59 60 for _, test := range []struct { 61 val interface{} 62 wantField *proto3.Value 63 wantType *sppb.Type 64 }{ 65 // bool 66 {true, boolProto(true), boolType()}, 67 {NullBool{true, true}, boolProto(true), boolType()}, 68 {NullBool{true, false}, nullProto(), boolType()}, 69 {[]bool(nil), nullProto(), listType(boolType())}, 70 {[]bool{}, listProto(), listType(boolType())}, 71 {[]bool{true, false}, listProto(boolProto(true), boolProto(false)), listType(boolType())}, 72 {[]NullBool(nil), nullProto(), listType(boolType())}, 73 {[]NullBool{}, listProto(), listType(boolType())}, 74 {[]NullBool{{true, true}, {}}, listProto(boolProto(true), nullProto()), listType(boolType())}, 75 // int 76 {int(1), intProto(1), intType()}, 77 {[]int(nil), nullProto(), listType(intType())}, 78 {[]int{}, listProto(), listType(intType())}, 79 {[]int{1, 2}, listProto(intProto(1), intProto(2)), listType(intType())}, 80 // int64 81 {int64(1), intProto(1), intType()}, 82 {NullInt64{5, true}, intProto(5), intType()}, 83 {NullInt64{5, false}, nullProto(), intType()}, 84 {[]int64(nil), nullProto(), listType(intType())}, 85 {[]int64{}, listProto(), listType(intType())}, 86 {[]int64{1, 2}, listProto(intProto(1), intProto(2)), listType(intType())}, 87 {[]NullInt64(nil), nullProto(), listType(intType())}, 88 {[]NullInt64{}, listProto(), listType(intType())}, 89 {[]NullInt64{{1, true}, {}}, listProto(intProto(1), nullProto()), listType(intType())}, 90 // float64 91 {0.0, floatProto(0.0), floatType()}, 92 {math.Inf(1), floatProto(math.Inf(1)), floatType()}, 93 {math.Inf(-1), floatProto(math.Inf(-1)), floatType()}, 94 {math.NaN(), floatProto(math.NaN()), floatType()}, 95 {NullFloat64{2.71, true}, floatProto(2.71), floatType()}, 96 {NullFloat64{1.41, false}, nullProto(), floatType()}, 97 {[]float64(nil), nullProto(), listType(floatType())}, 98 {[]float64{}, listProto(), listType(floatType())}, 99 {[]float64{2.72, math.Inf(1)}, listProto(floatProto(2.72), floatProto(math.Inf(1))), listType(floatType())}, 100 {[]NullFloat64(nil), nullProto(), listType(floatType())}, 101 {[]NullFloat64{}, listProto(), listType(floatType())}, 102 {[]NullFloat64{{2.72, true}, {}}, listProto(floatProto(2.72), nullProto()), listType(floatType())}, 103 // string 104 {"", stringProto(""), stringType()}, 105 {"foo", stringProto("foo"), stringType()}, 106 {NullString{"bar", true}, stringProto("bar"), stringType()}, 107 {NullString{"bar", false}, nullProto(), stringType()}, 108 {[]string(nil), nullProto(), listType(stringType())}, 109 {[]string{}, listProto(), listType(stringType())}, 110 {[]string{"foo", "bar"}, listProto(stringProto("foo"), stringProto("bar")), listType(stringType())}, 111 {[]NullString(nil), nullProto(), listType(stringType())}, 112 {[]NullString{}, listProto(), listType(stringType())}, 113 {[]NullString{{"foo", true}, {}}, listProto(stringProto("foo"), nullProto()), listType(stringType())}, 114 // bytes 115 {[]byte{}, bytesProto([]byte{}), bytesType()}, 116 {[]byte{1, 2, 3}, bytesProto([]byte{1, 2, 3}), bytesType()}, 117 {[]byte(nil), nullProto(), bytesType()}, 118 {[][]byte(nil), nullProto(), listType(bytesType())}, 119 {[][]byte{}, listProto(), listType(bytesType())}, 120 {[][]byte{{1}, []byte(nil)}, listProto(bytesProto([]byte{1}), nullProto()), listType(bytesType())}, 121 // date 122 {d1, dateProto(d1), dateType()}, 123 {NullDate{civil.Date{}, false}, nullProto(), dateType()}, 124 {[]civil.Date(nil), nullProto(), listType(dateType())}, 125 {[]civil.Date{}, listProto(), listType(dateType())}, 126 {[]civil.Date{d1, d2, d3}, listProto(dateProto(d1), dateProto(d2), dateProto(d3)), listType(dateType())}, 127 {[]NullDate{{d2, true}, {}}, listProto(dateProto(d2), nullProto()), listType(dateType())}, 128 // timestamp 129 {t1, timeProto(t1), timeType()}, 130 {NullTime{}, nullProto(), timeType()}, 131 {[]time.Time(nil), nullProto(), listType(timeType())}, 132 {[]time.Time{}, listProto(), listType(timeType())}, 133 {[]time.Time{t1, t2, t3}, listProto(timeProto(t1), timeProto(t2), timeProto(t3)), listType(timeType())}, 134 {[]NullTime{{t2, true}, {}}, listProto(timeProto(t2), nullProto()), listType(timeType())}, 135 // Struct 136 { 137 s1, 138 listProto(intProto(10)), 139 structType(mkField("field", intType())), 140 }, 141 { 142 s3, 143 listProto(intProto(30)), 144 structType(mkField("field", intType())), 145 }, 146 { 147 (*struct { 148 F1 civil.Date `spanner:""` 149 F2 bool 150 })(nil), 151 nullProto(), 152 structType( 153 mkField("", dateType()), 154 mkField("F2", boolType())), 155 }, 156 // Array-of-struct 157 { 158 []staticStruct{s1, s2}, 159 listProto(listProto(intProto(10)), listProto(intProto(20))), 160 listType(structType(mkField("field", intType()))), 161 }, 162 } { 163 st.Params["var"] = test.val 164 gotParams, gotParamTypes, gotErr := st.convertParams() 165 if gotErr != nil { 166 t.Error(gotErr) 167 continue 168 } 169 gotParamField := gotParams.Fields["var"] 170 if !proto.Equal(gotParamField, test.wantField) { 171 // handle NaN 172 if test.wantType.Code == floatType().Code && proto.MarshalTextString(gotParamField) == proto.MarshalTextString(test.wantField) { 173 continue 174 } 175 t.Errorf("%#v: got %v, want %v\n", test.val, gotParamField, test.wantField) 176 } 177 gotParamType := gotParamTypes["var"] 178 if !proto.Equal(gotParamType, test.wantType) { 179 t.Errorf("%#v: got %v, want %v\n", test.val, gotParamType, test.wantField) 180 } 181 } 182 183 // Verify type error reporting. 184 for _, test := range []struct { 185 val interface{} 186 wantErr error 187 }{ 188 { 189 nil, 190 errBindParam("var", nil, errNilParam), 191 }, 192 } { 193 st.Params["var"] = test.val 194 _, _, gotErr := st.convertParams() 195 if !testEqual(gotErr, test.wantErr) { 196 t.Errorf("value %#v:\ngot: %v\nwant: %v", test.val, gotErr, test.wantErr) 197 } 198 } 199} 200 201func TestNewStatement(t *testing.T) { 202 s := NewStatement("query") 203 if got, want := s.SQL, "query"; got != want { 204 t.Errorf("got %q, want %q", got, want) 205 } 206} 207