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	"reflect"
21
22	proto3 "github.com/golang/protobuf/ptypes/struct"
23
24	sppb "google.golang.org/genproto/googleapis/spanner/v1"
25	"google.golang.org/grpc/codes"
26)
27
28// op is the mutation operation.
29type op int
30
31const (
32	// opDelete removes a row from a table.  Succeeds whether or not the
33	// key was present.
34	opDelete op = iota
35	// opInsert inserts a row into a table.  If the row already exists, the
36	// write or transaction fails.
37	opInsert
38	// opInsertOrUpdate inserts a row into a table. If the row already
39	// exists, it updates it instead.  Any column values not explicitly
40	// written are preserved.
41	opInsertOrUpdate
42	// opReplace inserts a row into a table, deleting any existing row.
43	// Unlike InsertOrUpdate, this means any values not explicitly written
44	// become NULL.
45	opReplace
46	// opUpdate updates a row in a table.  If the row does not already
47	// exist, the write or transaction fails.
48	opUpdate
49)
50
51// A Mutation describes a modification to one or more Cloud Spanner rows.  The
52// mutation represents an insert, update, delete, etc on a table.
53//
54// Many mutations can be applied in a single atomic commit. For purposes of
55// constraint checking (such as foreign key constraints), the operations can be
56// viewed as applying in the same order as the mutations are provided (so that, e.g.,
57// a row and its logical "child" can be inserted in the same commit).
58//
59// The Apply function applies series of mutations. For example,
60//
61//	 m := spanner.Insert("User",
62//		 []string{"user_id", "profile"},
63//		 []interface{}{UserID, profile})
64//	 _, err := client.Apply(ctx, []*spanner.Mutation{m})
65//
66// inserts a new row into the User table. The primary key
67// for the new row is UserID (presuming that "user_id" has been declared as the
68// primary key of the "User" table).
69//
70// To apply a series of mutations as part of an atomic read-modify-write operation,
71// use ReadWriteTransaction.
72//
73// Updating a row
74//
75// Changing the values of columns in an existing row is very similar to
76// inserting a new row:
77//
78//	m := spanner.Update("User",
79//		[]string{"user_id", "profile"},
80//		[]interface{}{UserID, profile})
81//	_, err := client.Apply(ctx, []*spanner.Mutation{m})
82//
83// Deleting a row
84//
85// To delete a row, use spanner.Delete:
86//
87//	m := spanner.Delete("User", spanner.Key{UserId})
88//	_, err := client.Apply(ctx, []*spanner.Mutation{m})
89//
90// spanner.Delete accepts a KeySet, so you can also pass in a KeyRange, or use the
91// spanner.KeySets function to build any combination of Keys and KeyRanges.
92//
93// Note that deleting a row in a table may also delete rows from other tables
94// if cascading deletes are specified in those tables' schemas. Delete does
95// nothing if the named row does not exist (does not yield an error).
96//
97// Deleting a field
98//
99// To delete/clear a field within a row, use spanner.Update with the value nil:
100//
101//	m := spanner.Update("User",
102//		[]string{"user_id", "profile"},
103//		[]interface{}{UserID, nil})
104//	_, err := client.Apply(ctx, []*spanner.Mutation{m})
105//
106// The valid Go types and their corresponding Cloud Spanner types that can be
107// used in the Insert/Update/InsertOrUpdate functions are:
108//
109//     string, NullString - STRING
110//     []string, []NullString - STRING ARRAY
111//     []byte - BYTES
112//     [][]byte - BYTES ARRAY
113//     int, int64, NullInt64 - INT64
114//     []int, []int64, []NullInt64 - INT64 ARRAY
115//     bool, NullBool - BOOL
116//     []bool, []NullBool - BOOL ARRAY
117//     float64, NullFloat64 - FLOAT64
118//     []float64, []NullFloat64 - FLOAT64 ARRAY
119//     time.Time, NullTime - TIMESTAMP
120//     []time.Time, []NullTime - TIMESTAMP ARRAY
121//     Date, NullDate - DATE
122//     []Date, []NullDate - DATE ARRAY
123//
124// To compare two Mutations for testing purposes, use reflect.DeepEqual.
125type Mutation struct {
126	// op is the operation type of the mutation.
127	// See documentation for spanner.op for more details.
128	op op
129	// Table is the name of the target table to be modified.
130	table string
131	// keySet is a set of primary keys that names the rows
132	// in a delete operation.
133	keySet KeySet
134	// columns names the set of columns that are going to be
135	// modified by Insert, InsertOrUpdate, Replace or Update
136	// operations.
137	columns []string
138	// values specifies the new values for the target columns
139	// named by Columns.
140	values []interface{}
141}
142
143// mapToMutationParams converts Go map into mutation parameters.
144func mapToMutationParams(in map[string]interface{}) ([]string, []interface{}) {
145	cols := []string{}
146	vals := []interface{}{}
147	for k, v := range in {
148		cols = append(cols, k)
149		vals = append(vals, v)
150	}
151	return cols, vals
152}
153
154// errNotStruct returns error for not getting a go struct type.
155func errNotStruct(in interface{}) error {
156	return spannerErrorf(codes.InvalidArgument, "%T is not a go struct type", in)
157}
158
159// structToMutationParams converts Go struct into mutation parameters.
160// If the input is not a valid Go struct type, structToMutationParams
161// returns error.
162func structToMutationParams(in interface{}) ([]string, []interface{}, error) {
163	if in == nil {
164		return nil, nil, errNotStruct(in)
165	}
166	v := reflect.ValueOf(in)
167	t := v.Type()
168	if t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct {
169		// t is a pointer to a struct.
170		if v.IsNil() {
171			// Return empty results.
172			return nil, nil, nil
173		}
174		// Get the struct value that in points to.
175		v = v.Elem()
176		t = t.Elem()
177	}
178	if t.Kind() != reflect.Struct {
179		return nil, nil, errNotStruct(in)
180	}
181	fields, err := fieldCache.Fields(t)
182	if err != nil {
183		return nil, nil, toSpannerError(err)
184	}
185	var cols []string
186	var vals []interface{}
187	for _, f := range fields {
188		cols = append(cols, f.Name)
189		vals = append(vals, v.FieldByIndex(f.Index).Interface())
190	}
191	return cols, vals, nil
192}
193
194// Insert returns a Mutation to insert a row into a table. If the row already
195// exists, the write or transaction fails.
196func Insert(table string, cols []string, vals []interface{}) *Mutation {
197	return &Mutation{
198		op:      opInsert,
199		table:   table,
200		columns: cols,
201		values:  vals,
202	}
203}
204
205// InsertMap returns a Mutation to insert a row into a table, specified by
206// a map of column name to value. If the row already exists, the write or
207// transaction fails.
208func InsertMap(table string, in map[string]interface{}) *Mutation {
209	cols, vals := mapToMutationParams(in)
210	return Insert(table, cols, vals)
211}
212
213// InsertStruct returns a Mutation to insert a row into a table, specified by
214// a Go struct.  If the row already exists, the write or transaction fails.
215//
216// The in argument must be a struct or a pointer to a struct. Its exported
217// fields specify the column names and values. Use a field tag like "spanner:name"
218// to provide an alternative column name, or use "spanner:-" to ignore the field.
219func InsertStruct(table string, in interface{}) (*Mutation, error) {
220	cols, vals, err := structToMutationParams(in)
221	if err != nil {
222		return nil, err
223	}
224	return Insert(table, cols, vals), nil
225}
226
227// Update returns a Mutation to update a row in a table. If the row does not
228// already exist, the write or transaction fails.
229func Update(table string, cols []string, vals []interface{}) *Mutation {
230	return &Mutation{
231		op:      opUpdate,
232		table:   table,
233		columns: cols,
234		values:  vals,
235	}
236}
237
238// UpdateMap returns a Mutation to update a row in a table, specified by
239// a map of column to value. If the row does not already exist, the write or
240// transaction fails.
241func UpdateMap(table string, in map[string]interface{}) *Mutation {
242	cols, vals := mapToMutationParams(in)
243	return Update(table, cols, vals)
244}
245
246// UpdateStruct returns a Mutation to update a row in a table, specified by a Go
247// struct. If the row does not already exist, the write or transaction fails.
248func UpdateStruct(table string, in interface{}) (*Mutation, error) {
249	cols, vals, err := structToMutationParams(in)
250	if err != nil {
251		return nil, err
252	}
253	return Update(table, cols, vals), nil
254}
255
256// InsertOrUpdate returns a Mutation to insert a row into a table. If the row
257// already exists, it updates it instead. Any column values not explicitly
258// written are preserved.
259//
260// For a similar example, See Update.
261func InsertOrUpdate(table string, cols []string, vals []interface{}) *Mutation {
262	return &Mutation{
263		op:      opInsertOrUpdate,
264		table:   table,
265		columns: cols,
266		values:  vals,
267	}
268}
269
270// InsertOrUpdateMap returns a Mutation to insert a row into a table,
271// specified by a map of column to value. If the row already exists, it
272// updates it instead. Any column values not explicitly written are preserved.
273//
274// For a similar example, See UpdateMap.
275func InsertOrUpdateMap(table string, in map[string]interface{}) *Mutation {
276	cols, vals := mapToMutationParams(in)
277	return InsertOrUpdate(table, cols, vals)
278}
279
280// InsertOrUpdateStruct returns a Mutation to insert a row into a table,
281// specified by a Go struct. If the row already exists, it updates it instead.
282// Any column values not explicitly written are preserved.
283//
284// The in argument must be a struct or a pointer to a struct. Its exported
285// fields specify the column names and values. Use a field tag like "spanner:name"
286// to provide an alternative column name, or use "spanner:-" to ignore the field.
287//
288// For a similar example, See UpdateStruct.
289func InsertOrUpdateStruct(table string, in interface{}) (*Mutation, error) {
290	cols, vals, err := structToMutationParams(in)
291	if err != nil {
292		return nil, err
293	}
294	return InsertOrUpdate(table, cols, vals), nil
295}
296
297// Replace returns a Mutation to insert a row into a table, deleting any
298// existing row. Unlike InsertOrUpdate, this means any values not explicitly
299// written become NULL.
300//
301// For a similar example, See Update.
302func Replace(table string, cols []string, vals []interface{}) *Mutation {
303	return &Mutation{
304		op:      opReplace,
305		table:   table,
306		columns: cols,
307		values:  vals,
308	}
309}
310
311// ReplaceMap returns a Mutation to insert a row into a table, deleting any
312// existing row. Unlike InsertOrUpdateMap, this means any values not explicitly
313// written become NULL.  The row is specified by a map of column to value.
314//
315// For a similar example, See UpdateMap.
316func ReplaceMap(table string, in map[string]interface{}) *Mutation {
317	cols, vals := mapToMutationParams(in)
318	return Replace(table, cols, vals)
319}
320
321// ReplaceStruct returns a Mutation to insert a row into a table, deleting any
322// existing row. Unlike InsertOrUpdateMap, this means any values not explicitly
323// written become NULL.  The row is specified by a Go struct.
324//
325// The in argument must be a struct or a pointer to a struct. Its exported
326// fields specify the column names and values. Use a field tag like "spanner:name"
327// to provide an alternative column name, or use "spanner:-" to ignore the field.
328//
329// For a similar example, See UpdateStruct.
330func ReplaceStruct(table string, in interface{}) (*Mutation, error) {
331	cols, vals, err := structToMutationParams(in)
332	if err != nil {
333		return nil, err
334	}
335	return Replace(table, cols, vals), nil
336}
337
338// Delete removes the rows described by the KeySet from the table. It succeeds
339// whether or not the keys were present.
340func Delete(table string, ks KeySet) *Mutation {
341	return &Mutation{
342		op:     opDelete,
343		table:  table,
344		keySet: ks,
345	}
346}
347
348// prepareWrite generates sppb.Mutation_Write from table name, column names
349// and new column values.
350func prepareWrite(table string, columns []string, vals []interface{}) (*sppb.Mutation_Write, error) {
351	v, err := encodeValueArray(vals)
352	if err != nil {
353		return nil, err
354	}
355	return &sppb.Mutation_Write{
356		Table:   table,
357		Columns: columns,
358		Values:  []*proto3.ListValue{v},
359	}, nil
360}
361
362// errInvdMutationOp returns error for unrecognized mutation operation.
363func errInvdMutationOp(m Mutation) error {
364	return spannerErrorf(codes.InvalidArgument, "Unknown op type: %d", m.op)
365}
366
367// proto converts spanner.Mutation to sppb.Mutation, in preparation to send
368// RPCs.
369func (m Mutation) proto() (*sppb.Mutation, error) {
370	var pb *sppb.Mutation
371	switch m.op {
372	case opDelete:
373		var kp *sppb.KeySet
374		if m.keySet != nil {
375			var err error
376			kp, err = m.keySet.keySetProto()
377			if err != nil {
378				return nil, err
379			}
380		}
381		pb = &sppb.Mutation{
382			Operation: &sppb.Mutation_Delete_{
383				Delete: &sppb.Mutation_Delete{
384					Table:  m.table,
385					KeySet: kp,
386				},
387			},
388		}
389	case opInsert:
390		w, err := prepareWrite(m.table, m.columns, m.values)
391		if err != nil {
392			return nil, err
393		}
394		pb = &sppb.Mutation{Operation: &sppb.Mutation_Insert{Insert: w}}
395	case opInsertOrUpdate:
396		w, err := prepareWrite(m.table, m.columns, m.values)
397		if err != nil {
398			return nil, err
399		}
400		pb = &sppb.Mutation{Operation: &sppb.Mutation_InsertOrUpdate{InsertOrUpdate: w}}
401	case opReplace:
402		w, err := prepareWrite(m.table, m.columns, m.values)
403		if err != nil {
404			return nil, err
405		}
406		pb = &sppb.Mutation{Operation: &sppb.Mutation_Replace{Replace: w}}
407	case opUpdate:
408		w, err := prepareWrite(m.table, m.columns, m.values)
409		if err != nil {
410			return nil, err
411		}
412		pb = &sppb.Mutation{Operation: &sppb.Mutation_Update{Update: w}}
413	default:
414		return nil, errInvdMutationOp(m)
415	}
416	return pb, nil
417}
418
419// mutationsProto turns a spanner.Mutation array into a sppb.Mutation array,
420// it is convenient for sending batch mutations to Cloud Spanner.
421func mutationsProto(ms []*Mutation) ([]*sppb.Mutation, error) {
422	l := make([]*sppb.Mutation, 0, len(ms))
423	for _, m := range ms {
424		pb, err := m.proto()
425		if err != nil {
426			return nil, err
427		}
428		l = append(l, pb)
429	}
430	return l, nil
431}
432