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