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	sppb "google.golang.org/genproto/googleapis/spanner/v1"
24	"google.golang.org/grpc/codes"
25)
26
27// op is the mutation operation.
28type op int
29
30const (
31	// opDelete removes a row from a table.  Succeeds whether or not the
32	// key was present.
33	opDelete op = iota
34	// opInsert inserts a row into a table.  If the row already exists, the
35	// write or transaction fails.
36	opInsert
37	// opInsertOrUpdate inserts a row into a table. If the row already
38	// exists, it updates it instead.  Any column values not explicitly
39	// written are preserved.
40	opInsertOrUpdate
41	// opReplace inserts a row into a table, deleting any existing row.
42	// Unlike InsertOrUpdate, this means any values not explicitly written
43	// become NULL.
44	opReplace
45	// opUpdate updates a row in a table.  If the row does not already
46	// exist, the write or transaction fails.
47	opUpdate
48)
49
50// A Mutation describes a modification to one or more Cloud Spanner rows.  The
51// mutation represents an insert, update, delete, etc on a table.
52//
53// Many mutations can be applied in a single atomic commit. For purposes of
54// constraint checking (such as foreign key constraints), the operations can be
55// viewed as applying in the same order as the mutations are provided (so that,
56// e.g., a row and its logical "child" can be inserted in the same commit).
57//
58// The Apply function applies series of mutations. For example,
59//
60//	 m := spanner.Insert("User",
61//		 []string{"user_id", "profile"},
62//		 []interface{}{UserID, profile})
63//	 _, err := client.Apply(ctx, []*spanner.Mutation{m})
64//
65// inserts a new row into the User table. The primary key
66// for the new row is UserID (presuming that "user_id" has been declared as the
67// primary key of the "User" table).
68//
69// To apply a series of mutations as part of an atomic read-modify-write
70// operation, use ReadWriteTransaction.
71//
72// Updating a row
73//
74// Changing the values of columns in an existing row is very similar to
75// inserting a new row:
76//
77//	m := spanner.Update("User",
78//		[]string{"user_id", "profile"},
79//		[]interface{}{UserID, profile})
80//	_, err := client.Apply(ctx, []*spanner.Mutation{m})
81//
82// Deleting a row
83//
84// To delete a row, use spanner.Delete:
85//
86//	m := spanner.Delete("User", spanner.Key{UserId})
87//	_, err := client.Apply(ctx, []*spanner.Mutation{m})
88//
89// spanner.Delete accepts a KeySet, so you can also pass in a KeyRange, or use
90// the spanner.KeySets function to build any combination of Keys and KeyRanges.
91//
92// Note that deleting a row in a table may also delete rows from other tables
93// if cascading deletes are specified in those tables' schemas. Delete does
94// nothing if the named row does not exist (does not yield an error).
95//
96// Deleting a field
97//
98// To delete/clear a field within a row, use spanner.Update with the value nil:
99//
100//	m := spanner.Update("User",
101//		[]string{"user_id", "profile"},
102//		[]interface{}{UserID, nil})
103//	_, err := client.Apply(ctx, []*spanner.Mutation{m})
104//
105// The valid Go types and their corresponding Cloud Spanner types that can be
106// used in the Insert/Update/InsertOrUpdate functions are:
107//
108//     string, NullString - STRING
109//     []string, []NullString - STRING ARRAY
110//     []byte - BYTES
111//     [][]byte - BYTES ARRAY
112//     int, int64, NullInt64 - INT64
113//     []int, []int64, []NullInt64 - INT64 ARRAY
114//     bool, NullBool - BOOL
115//     []bool, []NullBool - BOOL ARRAY
116//     float64, NullFloat64 - FLOAT64
117//     []float64, []NullFloat64 - FLOAT64 ARRAY
118//     time.Time, NullTime - TIMESTAMP
119//     []time.Time, []NullTime - TIMESTAMP ARRAY
120//     Date, NullDate - DATE
121//     []Date, []NullDate - DATE ARRAY
122//
123// To compare two Mutations for testing purposes, use reflect.DeepEqual.
124type Mutation struct {
125	// op is the operation type of the mutation.
126	// See documentation for spanner.op for more details.
127	op op
128	// Table is the name of the target table to be modified.
129	table string
130	// keySet is a set of primary keys that names the rows
131	// in a delete operation.
132	keySet KeySet
133	// columns names the set of columns that are going to be
134	// modified by Insert, InsertOrUpdate, Replace or Update
135	// operations.
136	columns []string
137	// values specifies the new values for the target columns
138	// named by Columns.
139	values []interface{}
140}
141
142// mapToMutationParams converts Go map into mutation parameters.
143func mapToMutationParams(in map[string]interface{}) ([]string, []interface{}) {
144	cols := []string{}
145	vals := []interface{}{}
146	for k, v := range in {
147		cols = append(cols, k)
148		vals = append(vals, v)
149	}
150	return cols, vals
151}
152
153// errNotStruct returns error for not getting a go struct type.
154func errNotStruct(in interface{}) error {
155	return spannerErrorf(codes.InvalidArgument, "%T is not a go struct type", in)
156}
157
158// structToMutationParams converts Go struct into mutation parameters.
159// If the input is not a valid Go struct type, structToMutationParams
160// returns error.
161func structToMutationParams(in interface{}) ([]string, []interface{}, error) {
162	if in == nil {
163		return nil, nil, errNotStruct(in)
164	}
165	v := reflect.ValueOf(in)
166	t := v.Type()
167	if t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct {
168		// t is a pointer to a struct.
169		if v.IsNil() {
170			// Return empty results.
171			return nil, nil, nil
172		}
173		// Get the struct value that in points to.
174		v = v.Elem()
175		t = t.Elem()
176	}
177	if t.Kind() != reflect.Struct {
178		return nil, nil, errNotStruct(in)
179	}
180	fields, err := fieldCache.Fields(t)
181	if err != nil {
182		return nil, nil, toSpannerError(err)
183	}
184	var cols []string
185	var vals []interface{}
186	for _, f := range fields {
187		cols = append(cols, f.Name)
188		vals = append(vals, v.FieldByIndex(f.Index).Interface())
189	}
190	return cols, vals, nil
191}
192
193// Insert returns a Mutation to insert a row into a table. If the row already
194// exists, the write or transaction fails.
195func Insert(table string, cols []string, vals []interface{}) *Mutation {
196	return &Mutation{
197		op:      opInsert,
198		table:   table,
199		columns: cols,
200		values:  vals,
201	}
202}
203
204// InsertMap returns a Mutation to insert a row into a table, specified by
205// a map of column name to value. If the row already exists, the write or
206// transaction fails.
207func InsertMap(table string, in map[string]interface{}) *Mutation {
208	cols, vals := mapToMutationParams(in)
209	return Insert(table, cols, vals)
210}
211
212// InsertStruct returns a Mutation to insert a row into a table, specified by
213// a Go struct.  If the row already exists, the write or transaction fails.
214//
215// The in argument must be a struct or a pointer to a struct. Its exported
216// fields specify the column names and values. Use a field tag like "spanner:name"
217// to provide an alternative column name, or use "spanner:-" to ignore the field.
218func InsertStruct(table string, in interface{}) (*Mutation, error) {
219	cols, vals, err := structToMutationParams(in)
220	if err != nil {
221		return nil, err
222	}
223	return Insert(table, cols, vals), nil
224}
225
226// Update returns a Mutation to update a row in a table. If the row does not
227// already exist, the write or transaction fails.
228func Update(table string, cols []string, vals []interface{}) *Mutation {
229	return &Mutation{
230		op:      opUpdate,
231		table:   table,
232		columns: cols,
233		values:  vals,
234	}
235}
236
237// UpdateMap returns a Mutation to update a row in a table, specified by
238// a map of column to value. If the row does not already exist, the write or
239// transaction fails.
240func UpdateMap(table string, in map[string]interface{}) *Mutation {
241	cols, vals := mapToMutationParams(in)
242	return Update(table, cols, vals)
243}
244
245// UpdateStruct returns a Mutation to update a row in a table, specified by a Go
246// struct. If the row does not already exist, the write or transaction fails.
247func UpdateStruct(table string, in interface{}) (*Mutation, error) {
248	cols, vals, err := structToMutationParams(in)
249	if err != nil {
250		return nil, err
251	}
252	return Update(table, cols, vals), nil
253}
254
255// InsertOrUpdate returns a Mutation to insert a row into a table. If the row
256// already exists, it updates it instead. Any column values not explicitly
257// written are preserved.
258//
259// For a similar example, See Update.
260func InsertOrUpdate(table string, cols []string, vals []interface{}) *Mutation {
261	return &Mutation{
262		op:      opInsertOrUpdate,
263		table:   table,
264		columns: cols,
265		values:  vals,
266	}
267}
268
269// InsertOrUpdateMap returns a Mutation to insert a row into a table,
270// specified by a map of column to value. If the row already exists, it
271// updates it instead. Any column values not explicitly written are preserved.
272//
273// For a similar example, See UpdateMap.
274func InsertOrUpdateMap(table string, in map[string]interface{}) *Mutation {
275	cols, vals := mapToMutationParams(in)
276	return InsertOrUpdate(table, cols, vals)
277}
278
279// InsertOrUpdateStruct returns a Mutation to insert a row into a table,
280// specified by a Go struct. If the row already exists, it updates it instead.
281// Any column values not explicitly written are preserved.
282//
283// The in argument must be a struct or a pointer to a struct. Its exported
284// fields specify the column names and values. Use a field tag like
285// "spanner:name" to provide an alternative column name, or use "spanner:-" to
286// 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