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