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