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