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, *string, NullString - STRING
109//     []string, []*string, []NullString - STRING ARRAY
110//     []byte - BYTES
111//     [][]byte - BYTES ARRAY
112//     int, int64, *int64, NullInt64 - INT64
113//     []int, []int64, []*int64, []NullInt64 - INT64 ARRAY
114//     bool, *bool, NullBool - BOOL
115//     []bool, []*bool, []NullBool - BOOL ARRAY
116//     float64, *float64, NullFloat64 - FLOAT64
117//     []float64, []*float64, []NullFloat64 - FLOAT64 ARRAY
118//     time.Time, *time.Time, NullTime - TIMESTAMP
119//     []time.Time, []*time.Time, []NullTime - TIMESTAMP ARRAY
120//     Date, *Date, NullDate - DATE
121//     []Date, []*Date, []NullDate - DATE ARRAY
122//     big.Rat, *big.Rat, NullNumeric - NUMERIC
123//     []big.Rat, []*big.Rat, []NullNumeric - NUMERIC ARRAY
124//
125// To compare two Mutations for testing purposes, use reflect.DeepEqual.
126type Mutation struct {
127	// op is the operation type of the mutation.
128	// See documentation for spanner.op for more details.
129	op op
130	// Table is the name of the target table to be modified.
131	table string
132	// keySet is a set of primary keys that names the rows
133	// in a delete operation.
134	keySet KeySet
135	// columns names the set of columns that are going to be
136	// modified by Insert, InsertOrUpdate, Replace or Update
137	// operations.
138	columns []string
139	// values specifies the new values for the target columns
140	// named by Columns.
141	values []interface{}
142}
143
144// mapToMutationParams converts Go map into mutation parameters.
145func mapToMutationParams(in map[string]interface{}) ([]string, []interface{}) {
146	cols := []string{}
147	vals := []interface{}{}
148	for k, v := range in {
149		cols = append(cols, k)
150		vals = append(vals, v)
151	}
152	return cols, vals
153}
154
155// errNotStruct returns error for not getting a go struct type.
156func errNotStruct(in interface{}) error {
157	return spannerErrorf(codes.InvalidArgument, "%T is not a go struct type", in)
158}
159
160// structToMutationParams converts Go struct into mutation parameters.
161// If the input is not a valid Go struct type, structToMutationParams
162// returns error.
163func structToMutationParams(in interface{}) ([]string, []interface{}, error) {
164	if in == nil {
165		return nil, nil, errNotStruct(in)
166	}
167	v := reflect.ValueOf(in)
168	t := v.Type()
169	if t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct {
170		// t is a pointer to a struct.
171		if v.IsNil() {
172			// Return empty results.
173			return nil, nil, nil
174		}
175		// Get the struct value that in points to.
176		v = v.Elem()
177		t = t.Elem()
178	}
179	if t.Kind() != reflect.Struct {
180		return nil, nil, errNotStruct(in)
181	}
182	fields, err := fieldCache.Fields(t)
183	if err != nil {
184		return nil, nil, ToSpannerError(err)
185	}
186	var cols []string
187	var vals []interface{}
188	for _, f := range fields {
189		cols = append(cols, f.Name)
190		vals = append(vals, v.FieldByIndex(f.Index).Interface())
191	}
192	return cols, vals, nil
193}
194
195// Insert returns a Mutation to insert a row into a table. If the row already
196// exists, the write or transaction fails with codes.AlreadyExists.
197func Insert(table string, cols []string, vals []interface{}) *Mutation {
198	return &Mutation{
199		op:      opInsert,
200		table:   table,
201		columns: cols,
202		values:  vals,
203	}
204}
205
206// InsertMap returns a Mutation to insert a row into a table, specified by
207// a map of column name to value. If the row already exists, the write or
208// transaction fails with codes.AlreadyExists.
209func InsertMap(table string, in map[string]interface{}) *Mutation {
210	cols, vals := mapToMutationParams(in)
211	return Insert(table, cols, vals)
212}
213
214// InsertStruct returns a Mutation to insert a row into a table, specified by
215// a Go struct.  If the row already exists, the write or transaction fails with
216// codes.AlreadyExists.
217//
218// The in argument must be a struct or a pointer to a struct. Its exported
219// fields specify the column names and values. Use a field tag like "spanner:name"
220// to provide an alternative column name, or use "spanner:-" to ignore the field.
221func InsertStruct(table string, in interface{}) (*Mutation, error) {
222	cols, vals, err := structToMutationParams(in)
223	if err != nil {
224		return nil, err
225	}
226	return Insert(table, cols, vals), nil
227}
228
229// Update returns a Mutation to update a row in a table. If the row does not
230// already exist, the write or transaction fails.
231func Update(table string, cols []string, vals []interface{}) *Mutation {
232	return &Mutation{
233		op:      opUpdate,
234		table:   table,
235		columns: cols,
236		values:  vals,
237	}
238}
239
240// UpdateMap returns a Mutation to update a row in a table, specified by
241// a map of column to value. If the row does not already exist, the write or
242// transaction fails.
243func UpdateMap(table string, in map[string]interface{}) *Mutation {
244	cols, vals := mapToMutationParams(in)
245	return Update(table, cols, vals)
246}
247
248// UpdateStruct returns a Mutation to update a row in a table, specified by a Go
249// struct. If the row does not already exist, the write or transaction fails.
250func UpdateStruct(table string, in interface{}) (*Mutation, error) {
251	cols, vals, err := structToMutationParams(in)
252	if err != nil {
253		return nil, err
254	}
255	return Update(table, cols, vals), nil
256}
257
258// InsertOrUpdate returns a Mutation to insert a row into a table. If the row
259// already exists, it updates it instead. Any column values not explicitly
260// written are preserved.
261//
262// For a similar example, See Update.
263func InsertOrUpdate(table string, cols []string, vals []interface{}) *Mutation {
264	return &Mutation{
265		op:      opInsertOrUpdate,
266		table:   table,
267		columns: cols,
268		values:  vals,
269	}
270}
271
272// InsertOrUpdateMap returns a Mutation to insert a row into a table,
273// specified by a map of column to value. If the row already exists, it
274// updates it instead. Any column values not explicitly written are preserved.
275//
276// For a similar example, See UpdateMap.
277func InsertOrUpdateMap(table string, in map[string]interface{}) *Mutation {
278	cols, vals := mapToMutationParams(in)
279	return InsertOrUpdate(table, cols, vals)
280}
281
282// InsertOrUpdateStruct returns a Mutation to insert a row into a table,
283// specified by a Go struct. If the row already exists, it updates it instead.
284// Any column values not explicitly written are preserved.
285//
286// The in argument must be a struct or a pointer to a struct. Its exported
287// fields specify the column names and values. Use a field tag like
288// "spanner:name" to provide an alternative column name, or use "spanner:-" to
289// ignore the field.
290//
291// For a similar example, See UpdateStruct.
292func InsertOrUpdateStruct(table string, in interface{}) (*Mutation, error) {
293	cols, vals, err := structToMutationParams(in)
294	if err != nil {
295		return nil, err
296	}
297	return InsertOrUpdate(table, cols, vals), nil
298}
299
300// Replace returns a Mutation to insert a row into a table, deleting any
301// existing row. Unlike InsertOrUpdate, this means any values not explicitly
302// written become NULL.
303//
304// For a similar example, See Update.
305func Replace(table string, cols []string, vals []interface{}) *Mutation {
306	return &Mutation{
307		op:      opReplace,
308		table:   table,
309		columns: cols,
310		values:  vals,
311	}
312}
313
314// ReplaceMap returns a Mutation to insert a row into a table, deleting any
315// existing row. Unlike InsertOrUpdateMap, this means any values not explicitly
316// written become NULL.  The row is specified by a map of column to value.
317//
318// For a similar example, See UpdateMap.
319func ReplaceMap(table string, in map[string]interface{}) *Mutation {
320	cols, vals := mapToMutationParams(in)
321	return Replace(table, cols, vals)
322}
323
324// ReplaceStruct returns a Mutation to insert a row into a table, deleting any
325// existing row. Unlike InsertOrUpdateMap, this means any values not explicitly
326// written become NULL.  The row is specified by a Go struct.
327//
328// The in argument must be a struct or a pointer to a struct. Its exported
329// fields specify the column names and values. Use a field tag like "spanner:name"
330// to provide an alternative column name, or use "spanner:-" to ignore the field.
331//
332// For a similar example, See UpdateStruct.
333func ReplaceStruct(table string, in interface{}) (*Mutation, error) {
334	cols, vals, err := structToMutationParams(in)
335	if err != nil {
336		return nil, err
337	}
338	return Replace(table, cols, vals), nil
339}
340
341// Delete removes the rows described by the KeySet from the table. It succeeds
342// whether or not the keys were present.
343func Delete(table string, ks KeySet) *Mutation {
344	return &Mutation{
345		op:     opDelete,
346		table:  table,
347		keySet: ks,
348	}
349}
350
351// prepareWrite generates sppb.Mutation_Write from table name, column names
352// and new column values.
353func prepareWrite(table string, columns []string, vals []interface{}) (*sppb.Mutation_Write, error) {
354	v, err := encodeValueArray(vals)
355	if err != nil {
356		return nil, err
357	}
358	return &sppb.Mutation_Write{
359		Table:   table,
360		Columns: columns,
361		Values:  []*proto3.ListValue{v},
362	}, nil
363}
364
365// errInvdMutationOp returns error for unrecognized mutation operation.
366func errInvdMutationOp(m Mutation) error {
367	return spannerErrorf(codes.InvalidArgument, "Unknown op type: %d", m.op)
368}
369
370// proto converts spanner.Mutation to sppb.Mutation, in preparation to send
371// RPCs.
372func (m Mutation) proto() (*sppb.Mutation, error) {
373	var pb *sppb.Mutation
374	switch m.op {
375	case opDelete:
376		var kp *sppb.KeySet
377		if m.keySet != nil {
378			var err error
379			kp, err = m.keySet.keySetProto()
380			if err != nil {
381				return nil, err
382			}
383		}
384		pb = &sppb.Mutation{
385			Operation: &sppb.Mutation_Delete_{
386				Delete: &sppb.Mutation_Delete{
387					Table:  m.table,
388					KeySet: kp,
389				},
390			},
391		}
392	case opInsert:
393		w, err := prepareWrite(m.table, m.columns, m.values)
394		if err != nil {
395			return nil, err
396		}
397		pb = &sppb.Mutation{Operation: &sppb.Mutation_Insert{Insert: w}}
398	case opInsertOrUpdate:
399		w, err := prepareWrite(m.table, m.columns, m.values)
400		if err != nil {
401			return nil, err
402		}
403		pb = &sppb.Mutation{Operation: &sppb.Mutation_InsertOrUpdate{InsertOrUpdate: w}}
404	case opReplace:
405		w, err := prepareWrite(m.table, m.columns, m.values)
406		if err != nil {
407			return nil, err
408		}
409		pb = &sppb.Mutation{Operation: &sppb.Mutation_Replace{Replace: w}}
410	case opUpdate:
411		w, err := prepareWrite(m.table, m.columns, m.values)
412		if err != nil {
413			return nil, err
414		}
415		pb = &sppb.Mutation{Operation: &sppb.Mutation_Update{Update: w}}
416	default:
417		return nil, errInvdMutationOp(m)
418	}
419	return pb, nil
420}
421
422// mutationsProto turns a spanner.Mutation array into a sppb.Mutation array,
423// it is convenient for sending batch mutations to Cloud Spanner.
424func mutationsProto(ms []*Mutation) ([]*sppb.Mutation, error) {
425	l := make([]*sppb.Mutation, 0, len(ms))
426	for _, m := range ms {
427		pb, err := m.proto()
428		if err != nil {
429			return nil, err
430		}
431		l = append(l, pb)
432	}
433	return l, nil
434}
435