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 // Untyped null 163 { 164 nil, 165 nullProto(), 166 nil, 167 }, 168 } { 169 st.Params["var"] = test.val 170 gotParams, gotParamTypes, gotErr := st.convertParams() 171 if gotErr != nil { 172 t.Error(gotErr) 173 continue 174 } 175 gotParamField := gotParams.Fields["var"] 176 if !proto.Equal(gotParamField, test.wantField) { 177 // handle NaN 178 if test.wantType.Code == floatType().Code && proto.MarshalTextString(gotParamField) == proto.MarshalTextString(test.wantField) { 179 continue 180 } 181 t.Errorf("%#v: got %v, want %v\n", test.val, gotParamField, test.wantField) 182 } 183 gotParamType := gotParamTypes["var"] 184 if !proto.Equal(gotParamType, test.wantType) { 185 t.Errorf("%#v: got %v, want %v\n", test.val, gotParamType, test.wantField) 186 } 187 } 188} 189 190func TestNewStatement(t *testing.T) { 191 s := NewStatement("query") 192 if got, want := s.SQL, "query"; got != want { 193 t.Errorf("got %q, want %q", got, want) 194 } 195} 196