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, e.g.,
56// 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 operation,
70// 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 the
90// 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 "spanner:name"
285// to provide an alternative column name, or use "spanner:-" to ignore the field.
286//
287// For a similar example, See UpdateStruct.
288func InsertOrUpdateStruct(table string, in interface{}) (*Mutation, error) {
289	cols, vals, err := structToMutationParams(in)
290	if err != nil {
291		return nil, err
292	}
293	return InsertOrUpdate(table, cols, vals), nil
294}
295
296// Replace returns a Mutation to insert a row into a table, deleting any
297// existing row. Unlike InsertOrUpdate, this means any values not explicitly
298// written become NULL.
299//
300// For a similar example, See Update.
301func Replace(table string, cols []string, vals []interface{}) *Mutation {
302	return &Mutation{
303		op:      opReplace,
304		table:   table,
305		columns: cols,
306		values:  vals,
307	}
308}
309
310// ReplaceMap returns a Mutation to insert a row into a table, deleting any
311// existing row. Unlike InsertOrUpdateMap, this means any values not explicitly
312// written become NULL.  The row is specified by a map of column to value.
313//
314// For a similar example, See UpdateMap.
315func ReplaceMap(table string, in map[string]interface{}) *Mutation {
316	cols, vals := mapToMutationParams(in)
317	return Replace(table, cols, vals)
318}
319
320// ReplaceStruct returns a Mutation to insert a row into a table, deleting any
321// existing row. Unlike InsertOrUpdateMap, this means any values not explicitly
322// written become NULL.  The row is specified by a Go struct.
323//
324// The in argument must be a struct or a pointer to a struct. Its exported
325// fields specify the column names and values. Use a field tag like "spanner:name"
326// to provide an alternative column name, or use "spanner:-" to ignore the field.
327//
328// For a similar example, See UpdateStruct.
329func ReplaceStruct(table string, in interface{}) (*Mutation, error) {
330	cols, vals, err := structToMutationParams(in)
331	if err != nil {
332		return nil, err
333	}
334	return Replace(table, cols, vals), nil
335}
336
337// Delete removes the rows described by the KeySet from the table. It succeeds
338// whether or not the keys were present.
339func Delete(table string, ks KeySet) *Mutation {
340	return &Mutation{
341		op:     opDelete,
342		table:  table,
343		keySet: ks,
344	}
345}
346
347// prepareWrite generates sppb.Mutation_Write from table name, column names
348// and new column values.
349func prepareWrite(table string, columns []string, vals []interface{}) (*sppb.Mutation_Write, error) {
350	v, err := encodeValueArray(vals)
351	if err != nil {
352		return nil, err
353	}
354	return &sppb.Mutation_Write{
355		Table:   table,
356		Columns: columns,
357		Values:  []*proto3.ListValue{v},
358	}, nil
359}
360
361// errInvdMutationOp returns error for unrecognized mutation operation.
362func errInvdMutationOp(m Mutation) error {
363	return spannerErrorf(codes.InvalidArgument, "Unknown op type: %d", m.op)
364}
365
366// proto converts spanner.Mutation to sppb.Mutation, in preparation to send
367// RPCs.
368func (m Mutation) proto() (*sppb.Mutation, error) {
369	var pb *sppb.Mutation
370	switch m.op {
371	case opDelete:
372		var kp *sppb.KeySet
373		if m.keySet != nil {
374			var err error
375			kp, err = m.keySet.keySetProto()
376			if err != nil {
377				return nil, err
378			}
379		}
380		pb = &sppb.Mutation{
381			Operation: &sppb.Mutation_Delete_{
382				Delete: &sppb.Mutation_Delete{
383					Table:  m.table,
384					KeySet: kp,
385				},
386			},
387		}
388	case opInsert:
389		w, err := prepareWrite(m.table, m.columns, m.values)
390		if err != nil {
391			return nil, err
392		}
393		pb = &sppb.Mutation{Operation: &sppb.Mutation_Insert{Insert: w}}
394	case opInsertOrUpdate:
395		w, err := prepareWrite(m.table, m.columns, m.values)
396		if err != nil {
397			return nil, err
398		}
399		pb = &sppb.Mutation{Operation: &sppb.Mutation_InsertOrUpdate{InsertOrUpdate: w}}
400	case opReplace:
401		w, err := prepareWrite(m.table, m.columns, m.values)
402		if err != nil {
403			return nil, err
404		}
405		pb = &sppb.Mutation{Operation: &sppb.Mutation_Replace{Replace: w}}
406	case opUpdate:
407		w, err := prepareWrite(m.table, m.columns, m.values)
408		if err != nil {
409			return nil, err
410		}
411		pb = &sppb.Mutation{Operation: &sppb.Mutation_Update{Update: w}}
412	default:
413		return nil, errInvdMutationOp(m)
414	}
415	return pb, nil
416}
417
418// mutationsProto turns a spanner.Mutation array into a sppb.Mutation array,
419// it is convenient for sending batch mutations to Cloud Spanner.
420func mutationsProto(ms []*Mutation) ([]*sppb.Mutation, error) {
421	l := make([]*sppb.Mutation, 0, len(ms))
422	for _, m := range ms {
423		pb, err := m.proto()
424		if err != nil {
425			return nil, err
426		}
427		l = append(l, pb)
428	}
429	return l, nil
430}
431