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