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/big" 21 "sort" 22 "strings" 23 "testing" 24 "time" 25 26 "cloud.google.com/go/civil" 27 proto3 "github.com/golang/protobuf/ptypes/struct" 28 sppb "google.golang.org/genproto/googleapis/spanner/v1" 29) 30 31// keysetProto returns protobuf encoding of valid spanner.KeySet. 32func keysetProto(t *testing.T, ks KeySet) *sppb.KeySet { 33 k, err := ks.keySetProto() 34 if err != nil { 35 t.Fatalf("cannot convert keyset %v to protobuf: %v", ks, err) 36 } 37 return k 38} 39 40// Test encoding from spanner.Mutation to protobuf. 41func TestMutationToProto(t *testing.T) { 42 utc, err := time.LoadLocation("UTC") 43 if err != nil { 44 t.Fatalf("Could not load UTC: %v", err) 45 } 46 r, _ := (&big.Rat{}).SetString("3.14") 47 48 for i, test := range []struct { 49 m *Mutation 50 want *sppb.Mutation 51 }{ 52 // Delete Mutation 53 { 54 &Mutation{opDelete, "t_foo", Key{"foo"}, nil, nil}, 55 &sppb.Mutation{ 56 Operation: &sppb.Mutation_Delete_{ 57 Delete: &sppb.Mutation_Delete{ 58 Table: "t_foo", 59 KeySet: keysetProto(t, Key{"foo"}), 60 }, 61 }, 62 }, 63 }, 64 // Insert Mutation 65 { 66 &Mutation{opInsert, "t_foo", KeySets(), []string{"col1", "col2"}, []interface{}{int64(1), int64(2)}}, 67 &sppb.Mutation{ 68 Operation: &sppb.Mutation_Insert{ 69 Insert: &sppb.Mutation_Write{ 70 Table: "t_foo", 71 Columns: []string{"col1", "col2"}, 72 Values: []*proto3.ListValue{ 73 { 74 Values: []*proto3.Value{intProto(1), intProto(2)}, 75 }, 76 }, 77 }, 78 }, 79 }, 80 }, 81 // InsertOrUpdate Mutation 82 { 83 &Mutation{opInsertOrUpdate, "t_foo", KeySets(), []string{"col1", "col2"}, []interface{}{1.0, 2.0}}, 84 &sppb.Mutation{ 85 Operation: &sppb.Mutation_InsertOrUpdate{ 86 InsertOrUpdate: &sppb.Mutation_Write{ 87 Table: "t_foo", 88 Columns: []string{"col1", "col2"}, 89 Values: []*proto3.ListValue{ 90 { 91 Values: []*proto3.Value{floatProto(1.0), floatProto(2.0)}, 92 }, 93 }, 94 }, 95 }, 96 }, 97 }, 98 // Replace Mutation 99 { 100 &Mutation{opReplace, "t_foo", KeySets(), []string{"col1", "col2"}, []interface{}{"one", 2.0}}, 101 &sppb.Mutation{ 102 Operation: &sppb.Mutation_Replace{ 103 Replace: &sppb.Mutation_Write{ 104 Table: "t_foo", 105 Columns: []string{"col1", "col2"}, 106 Values: []*proto3.ListValue{ 107 { 108 Values: []*proto3.Value{stringProto("one"), floatProto(2.0)}, 109 }, 110 }, 111 }, 112 }, 113 }, 114 }, 115 // Update Mutation 116 { 117 &Mutation{opUpdate, "t_foo", KeySets(), []string{"col1", "col2"}, []interface{}{"one", []byte(nil)}}, 118 &sppb.Mutation{ 119 Operation: &sppb.Mutation_Update{ 120 Update: &sppb.Mutation_Write{ 121 Table: "t_foo", 122 Columns: []string{"col1", "col2"}, 123 Values: []*proto3.ListValue{ 124 { 125 Values: []*proto3.Value{stringProto("one"), nullProto()}, 126 }, 127 }, 128 }, 129 }, 130 }, 131 }, 132 // Mutation with all supported data types 133 { 134 &Mutation{ 135 opInsert, 136 "t_foo", 137 KeySets(), 138 []string{"colBool", "colInt64", "colFloat64", "colNumeric", "colString", "colBytes", "colDate", "colTimestamp"}, 139 []interface{}{ 140 true, 141 int64(100), 142 float64(3.14), 143 *r, // 3.14 144 "one", 145 []byte{1, 2, 3}, 146 civil.Date{Year: 2020, Month: 12, Day: 2}, 147 time.Date(2020, time.December, 3, 8, 46, 58, 109, utc), 148 }, 149 }, 150 &sppb.Mutation{ 151 Operation: &sppb.Mutation_Insert{ 152 Insert: &sppb.Mutation_Write{ 153 Table: "t_foo", 154 Columns: []string{"colBool", "colInt64", "colFloat64", "colNumeric", "colString", "colBytes", "colDate", "colTimestamp"}, 155 Values: []*proto3.ListValue{ 156 { 157 Values: []*proto3.Value{ 158 boolProto(true), 159 stringProto("100"), 160 floatProto(3.14), 161 stringProto("3.140000000"), 162 stringProto("one"), 163 bytesProto([]byte{1, 2, 3}), 164 stringProto("2020-12-02"), 165 stringProto("2020-12-03T08:46:58.000000109Z"), 166 }, 167 }, 168 }, 169 }, 170 }, 171 }, 172 }, 173 } { 174 if got, err := test.m.proto(); err != nil || !testEqual(got, test.want) { 175 t.Errorf("%d:\n(%#v).proto() =\n (%v, %v)\nwant (%v, nil)", i, test.m, got, err, test.want) 176 } 177 } 178} 179 180// mutationColumnSorter implements sort.Interface for sorting column-value pairs in a Mutation by column names. 181type mutationColumnSorter struct { 182 Mutation 183} 184 185// newMutationColumnSorter creates new instance of mutationColumnSorter by duplicating the input Mutation so that 186// sorting won't change the input Mutation. 187func newMutationColumnSorter(m *Mutation) *mutationColumnSorter { 188 return &mutationColumnSorter{ 189 Mutation{ 190 m.op, 191 m.table, 192 m.keySet, 193 append([]string(nil), m.columns...), 194 append([]interface{}(nil), m.values...), 195 }, 196 } 197} 198 199// Len implements sort.Interface.Len. 200func (ms *mutationColumnSorter) Len() int { 201 return len(ms.columns) 202} 203 204// Swap implements sort.Interface.Swap. 205func (ms *mutationColumnSorter) Swap(i, j int) { 206 ms.columns[i], ms.columns[j] = ms.columns[j], ms.columns[i] 207 ms.values[i], ms.values[j] = ms.values[j], ms.values[i] 208} 209 210// Less implements sort.Interface.Less. 211func (ms *mutationColumnSorter) Less(i, j int) bool { 212 return strings.Compare(ms.columns[i], ms.columns[j]) < 0 213} 214 215// mutationEqual returns true if two mutations in question are equal 216// to each other. 217func mutationEqual(t *testing.T, m1, m2 Mutation) bool { 218 // Two mutations are considered to be equal even if their column values have different 219 // orders. 220 ms1 := newMutationColumnSorter(&m1) 221 ms2 := newMutationColumnSorter(&m2) 222 sort.Sort(ms1) 223 sort.Sort(ms2) 224 return testEqual(ms1, ms2) 225} 226 227// Test helper functions which help to generate spanner.Mutation. 228func TestMutationHelpers(t *testing.T) { 229 for _, test := range []struct { 230 m string 231 got *Mutation 232 want *Mutation 233 }{ 234 { 235 "Insert", 236 Insert("t_foo", []string{"col1", "col2"}, []interface{}{int64(1), int64(2)}), 237 &Mutation{opInsert, "t_foo", nil, []string{"col1", "col2"}, []interface{}{int64(1), int64(2)}}, 238 }, 239 { 240 "InsertMap", 241 InsertMap("t_foo", map[string]interface{}{"col1": int64(1), "col2": int64(2)}), 242 &Mutation{opInsert, "t_foo", nil, []string{"col1", "col2"}, []interface{}{int64(1), int64(2)}}, 243 }, 244 { 245 "InsertStruct", 246 func() *Mutation { 247 m, err := InsertStruct( 248 "t_foo", 249 struct { 250 notCol bool 251 Col1 int64 `spanner:"col1"` 252 Col2 int64 `spanner:"col2"` 253 }{false, int64(1), int64(2)}, 254 ) 255 if err != nil { 256 t.Errorf("cannot convert struct into mutation: %v", err) 257 } 258 return m 259 }(), 260 &Mutation{opInsert, "t_foo", nil, []string{"col1", "col2"}, []interface{}{int64(1), int64(2)}}, 261 }, 262 { 263 "Update", 264 Update("t_foo", []string{"col1", "col2"}, []interface{}{"one", []byte(nil)}), 265 &Mutation{opUpdate, "t_foo", nil, []string{"col1", "col2"}, []interface{}{"one", []byte(nil)}}, 266 }, 267 { 268 "UpdateMap", 269 UpdateMap("t_foo", map[string]interface{}{"col1": "one", "col2": []byte(nil)}), 270 &Mutation{opUpdate, "t_foo", nil, []string{"col1", "col2"}, []interface{}{"one", []byte(nil)}}, 271 }, 272 { 273 "UpdateStruct", 274 func() *Mutation { 275 m, err := UpdateStruct( 276 "t_foo", 277 struct { 278 Col1 string `spanner:"col1"` 279 notCol int 280 Col2 []byte `spanner:"col2"` 281 }{"one", 1, nil}, 282 ) 283 if err != nil { 284 t.Errorf("cannot convert struct into mutation: %v", err) 285 } 286 return m 287 }(), 288 &Mutation{opUpdate, "t_foo", nil, []string{"col1", "col2"}, []interface{}{"one", []byte(nil)}}, 289 }, 290 { 291 "InsertOrUpdate", 292 InsertOrUpdate("t_foo", []string{"col1", "col2"}, []interface{}{1.0, 2.0}), 293 &Mutation{opInsertOrUpdate, "t_foo", nil, []string{"col1", "col2"}, []interface{}{1.0, 2.0}}, 294 }, 295 { 296 "InsertOrUpdateMap", 297 InsertOrUpdateMap("t_foo", map[string]interface{}{"col1": 1.0, "col2": 2.0}), 298 &Mutation{opInsertOrUpdate, "t_foo", nil, []string{"col1", "col2"}, []interface{}{1.0, 2.0}}, 299 }, 300 { 301 "InsertOrUpdateStruct", 302 func() *Mutation { 303 m, err := InsertOrUpdateStruct( 304 "t_foo", 305 struct { 306 Col1 float64 `spanner:"col1"` 307 Col2 float64 `spanner:"col2"` 308 notCol float64 309 }{1.0, 2.0, 3.0}, 310 ) 311 if err != nil { 312 t.Errorf("cannot convert struct into mutation: %v", err) 313 } 314 return m 315 }(), 316 &Mutation{opInsertOrUpdate, "t_foo", nil, []string{"col1", "col2"}, []interface{}{1.0, 2.0}}, 317 }, 318 { 319 "Replace", 320 Replace("t_foo", []string{"col1", "col2"}, []interface{}{"one", 2.0}), 321 &Mutation{opReplace, "t_foo", nil, []string{"col1", "col2"}, []interface{}{"one", 2.0}}, 322 }, 323 { 324 "ReplaceMap", 325 ReplaceMap("t_foo", map[string]interface{}{"col1": "one", "col2": 2.0}), 326 &Mutation{opReplace, "t_foo", nil, []string{"col1", "col2"}, []interface{}{"one", 2.0}}, 327 }, 328 { 329 "ReplaceStruct", 330 func() *Mutation { 331 m, err := ReplaceStruct( 332 "t_foo", 333 struct { 334 Col1 string `spanner:"col1"` 335 Col2 float64 `spanner:"col2"` 336 notCol string 337 }{"one", 2.0, "foo"}, 338 ) 339 if err != nil { 340 t.Errorf("cannot convert struct into mutation: %v", err) 341 } 342 return m 343 }(), 344 &Mutation{opReplace, "t_foo", nil, []string{"col1", "col2"}, []interface{}{"one", 2.0}}, 345 }, 346 { 347 "Delete", 348 Delete("t_foo", Key{"foo"}), 349 &Mutation{opDelete, "t_foo", Key{"foo"}, nil, nil}, 350 }, 351 { 352 "DeleteRange", 353 Delete("t_foo", KeyRange{Key{"bar"}, Key{"foo"}, ClosedClosed}), 354 &Mutation{opDelete, "t_foo", KeyRange{Key{"bar"}, Key{"foo"}, ClosedClosed}, nil, nil}, 355 }, 356 } { 357 if !mutationEqual(t, *test.got, *test.want) { 358 t.Errorf("%v: got Mutation %v, want %v", test.m, test.got, test.want) 359 } 360 } 361} 362 363// Test encoding non-struct types by using *Struct helpers. 364func TestBadStructs(t *testing.T) { 365 val := "i_am_not_a_struct" 366 wantErr := errNotStruct(val) 367 if _, gotErr := InsertStruct("t_test", val); !testEqual(gotErr, wantErr) { 368 t.Errorf("InsertStruct(%q) returns error %v, want %v", val, gotErr, wantErr) 369 } 370 if _, gotErr := InsertOrUpdateStruct("t_test", val); !testEqual(gotErr, wantErr) { 371 t.Errorf("InsertOrUpdateStruct(%q) returns error %v, want %v", val, gotErr, wantErr) 372 } 373 if _, gotErr := UpdateStruct("t_test", val); !testEqual(gotErr, wantErr) { 374 t.Errorf("UpdateStruct(%q) returns error %v, want %v", val, gotErr, wantErr) 375 } 376 if _, gotErr := ReplaceStruct("t_test", val); !testEqual(gotErr, wantErr) { 377 t.Errorf("ReplaceStruct(%q) returns error %v, want %v", val, gotErr, wantErr) 378 } 379} 380 381func TestStructToMutationParams(t *testing.T) { 382 // Tests cases not covered elsewhere. 383 type S struct{ F interface{} } 384 385 for _, test := range []struct { 386 in interface{} 387 wantCols []string 388 wantVals []interface{} 389 wantErr error 390 }{ 391 {nil, nil, nil, errNotStruct(nil)}, 392 {3, nil, nil, errNotStruct(3)}, 393 {(*S)(nil), nil, nil, nil}, 394 {&S{F: 1}, []string{"F"}, []interface{}{1}, nil}, 395 {&S{F: CommitTimestamp}, []string{"F"}, []interface{}{CommitTimestamp}, nil}, 396 } { 397 gotCols, gotVals, gotErr := structToMutationParams(test.in) 398 if !testEqual(gotCols, test.wantCols) { 399 t.Errorf("%#v: got cols %v, want %v", test.in, gotCols, test.wantCols) 400 } 401 if !testEqual(gotVals, test.wantVals) { 402 t.Errorf("%#v: got vals %v, want %v", test.in, gotVals, test.wantVals) 403 } 404 if !testEqual(gotErr, test.wantErr) { 405 t.Errorf("%#v: got err %v, want %v", test.in, gotErr, test.wantErr) 406 } 407 } 408} 409 410// Test encoding Mutation into proto. 411func TestEncodeMutation(t *testing.T) { 412 for _, test := range []struct { 413 name string 414 mutation Mutation 415 wantProto *sppb.Mutation 416 wantErr error 417 }{ 418 { 419 "OpDelete", 420 Mutation{opDelete, "t_test", Key{1}, nil, nil}, 421 &sppb.Mutation{ 422 Operation: &sppb.Mutation_Delete_{ 423 Delete: &sppb.Mutation_Delete{ 424 Table: "t_test", 425 KeySet: &sppb.KeySet{ 426 Keys: []*proto3.ListValue{listValueProto(intProto(1))}, 427 }, 428 }, 429 }, 430 }, 431 nil, 432 }, 433 { 434 "OpDelete - Key error", 435 Mutation{opDelete, "t_test", Key{struct{}{}}, nil, nil}, 436 &sppb.Mutation{ 437 Operation: &sppb.Mutation_Delete_{ 438 Delete: &sppb.Mutation_Delete{ 439 Table: "t_test", 440 KeySet: &sppb.KeySet{}, 441 }, 442 }, 443 }, 444 errInvdKeyPartType(struct{}{}), 445 }, 446 { 447 "OpInsert", 448 Mutation{opInsert, "t_test", nil, []string{"key", "val"}, []interface{}{"foo", 1}}, 449 &sppb.Mutation{ 450 Operation: &sppb.Mutation_Insert{ 451 Insert: &sppb.Mutation_Write{ 452 Table: "t_test", 453 Columns: []string{"key", "val"}, 454 Values: []*proto3.ListValue{listValueProto(stringProto("foo"), intProto(1))}, 455 }, 456 }, 457 }, 458 nil, 459 }, 460 { 461 "OpInsert - Value Type Error", 462 Mutation{opInsert, "t_test", nil, []string{"key", "val"}, []interface{}{struct{}{}, 1}}, 463 &sppb.Mutation{ 464 Operation: &sppb.Mutation_Insert{ 465 Insert: &sppb.Mutation_Write{}, 466 }, 467 }, 468 errEncoderUnsupportedType(struct{}{}), 469 }, 470 { 471 "OpInsertOrUpdate", 472 Mutation{opInsertOrUpdate, "t_test", nil, []string{"key", "val"}, []interface{}{"foo", 1}}, 473 &sppb.Mutation{ 474 Operation: &sppb.Mutation_InsertOrUpdate{ 475 InsertOrUpdate: &sppb.Mutation_Write{ 476 Table: "t_test", 477 Columns: []string{"key", "val"}, 478 Values: []*proto3.ListValue{listValueProto(stringProto("foo"), intProto(1))}, 479 }, 480 }, 481 }, 482 nil, 483 }, 484 { 485 "OpInsertOrUpdate - Value Type Error", 486 Mutation{opInsertOrUpdate, "t_test", nil, []string{"key", "val"}, []interface{}{struct{}{}, 1}}, 487 &sppb.Mutation{ 488 Operation: &sppb.Mutation_InsertOrUpdate{ 489 InsertOrUpdate: &sppb.Mutation_Write{}, 490 }, 491 }, 492 errEncoderUnsupportedType(struct{}{}), 493 }, 494 { 495 "OpReplace", 496 Mutation{opReplace, "t_test", nil, []string{"key", "val"}, []interface{}{"foo", 1}}, 497 &sppb.Mutation{ 498 Operation: &sppb.Mutation_Replace{ 499 Replace: &sppb.Mutation_Write{ 500 Table: "t_test", 501 Columns: []string{"key", "val"}, 502 Values: []*proto3.ListValue{listValueProto(stringProto("foo"), intProto(1))}, 503 }, 504 }, 505 }, 506 nil, 507 }, 508 { 509 "OpReplace - Value Type Error", 510 Mutation{opReplace, "t_test", nil, []string{"key", "val"}, []interface{}{struct{}{}, 1}}, 511 &sppb.Mutation{ 512 Operation: &sppb.Mutation_Replace{ 513 Replace: &sppb.Mutation_Write{}, 514 }, 515 }, 516 errEncoderUnsupportedType(struct{}{}), 517 }, 518 { 519 "OpUpdate", 520 Mutation{opUpdate, "t_test", nil, []string{"key", "val"}, []interface{}{"foo", 1}}, 521 &sppb.Mutation{ 522 Operation: &sppb.Mutation_Update{ 523 Update: &sppb.Mutation_Write{ 524 Table: "t_test", 525 Columns: []string{"key", "val"}, 526 Values: []*proto3.ListValue{listValueProto(stringProto("foo"), intProto(1))}, 527 }, 528 }, 529 }, 530 nil, 531 }, 532 { 533 "OpUpdate - Value Type Error", 534 Mutation{opUpdate, "t_test", nil, []string{"key", "val"}, []interface{}{struct{}{}, 1}}, 535 &sppb.Mutation{ 536 Operation: &sppb.Mutation_Update{ 537 Update: &sppb.Mutation_Write{}, 538 }, 539 }, 540 errEncoderUnsupportedType(struct{}{}), 541 }, 542 { 543 "OpKnown - Unknown Mutation Operation Code", 544 Mutation{op(100), "t_test", nil, nil, nil}, 545 &sppb.Mutation{}, 546 errInvdMutationOp(Mutation{op(100), "t_test", nil, nil, nil}), 547 }, 548 } { 549 gotProto, gotErr := test.mutation.proto() 550 if gotErr != nil { 551 if !testEqual(gotErr, test.wantErr) { 552 t.Errorf("%s: %v.proto() returns error %v, want %v", test.name, test.mutation, gotErr, test.wantErr) 553 } 554 continue 555 } 556 if !testEqual(gotProto, test.wantProto) { 557 t.Errorf("%s: %v.proto() = (%v, nil), want (%v, nil)", test.name, test.mutation, gotProto, test.wantProto) 558 } 559 } 560} 561 562// Test Encoding an array of mutations. 563func TestEncodeMutationArray(t *testing.T) { 564 for _, test := range []struct { 565 name string 566 ms []*Mutation 567 want []*sppb.Mutation 568 wantErr error 569 }{ 570 { 571 "Multiple Mutations", 572 []*Mutation{ 573 {opDelete, "t_test", Key{"bar"}, nil, nil}, 574 {opInsertOrUpdate, "t_test", nil, []string{"key", "val"}, []interface{}{"foo", 1}}, 575 }, 576 []*sppb.Mutation{ 577 { 578 Operation: &sppb.Mutation_Delete_{ 579 Delete: &sppb.Mutation_Delete{ 580 Table: "t_test", 581 KeySet: &sppb.KeySet{ 582 Keys: []*proto3.ListValue{listValueProto(stringProto("bar"))}, 583 }, 584 }, 585 }, 586 }, 587 { 588 Operation: &sppb.Mutation_InsertOrUpdate{ 589 InsertOrUpdate: &sppb.Mutation_Write{ 590 Table: "t_test", 591 Columns: []string{"key", "val"}, 592 Values: []*proto3.ListValue{listValueProto(stringProto("foo"), intProto(1))}, 593 }, 594 }, 595 }, 596 }, 597 nil, 598 }, 599 { 600 "Multiple Mutations - Bad Mutation", 601 []*Mutation{ 602 {opDelete, "t_test", Key{"bar"}, nil, nil}, 603 {opInsertOrUpdate, "t_test", nil, []string{"key", "val"}, []interface{}{"foo", struct{}{}}}, 604 }, 605 []*sppb.Mutation{}, 606 errEncoderUnsupportedType(struct{}{}), 607 }, 608 } { 609 gotProto, gotErr := mutationsProto(test.ms) 610 if gotErr != nil { 611 if !testEqual(gotErr, test.wantErr) { 612 t.Errorf("%v: mutationsProto(%v) returns error %v, want %v", test.name, test.ms, gotErr, test.wantErr) 613 } 614 continue 615 } 616 if !testEqual(gotProto, test.want) { 617 t.Errorf("%v: mutationsProto(%v) = (%v, nil), want (%v, nil)", test.name, test.ms, gotProto, test.want) 618 } 619 } 620} 621