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 24 sppb "google.golang.org/genproto/googleapis/spanner/v1" 25 "google.golang.org/grpc/codes" 26) 27 28// op is the mutation operation. 29type op int 30 31const ( 32 // opDelete removes a row from a table. Succeeds whether or not the 33 // key was present. 34 opDelete op = iota 35 // opInsert inserts a row into a table. If the row already exists, the 36 // write or transaction fails. 37 opInsert 38 // opInsertOrUpdate inserts a row into a table. If the row already 39 // exists, it updates it instead. Any column values not explicitly 40 // written are preserved. 41 opInsertOrUpdate 42 // opReplace inserts a row into a table, deleting any existing row. 43 // Unlike InsertOrUpdate, this means any values not explicitly written 44 // become NULL. 45 opReplace 46 // opUpdate updates a row in a table. If the row does not already 47 // exist, the write or transaction fails. 48 opUpdate 49) 50 51// A Mutation describes a modification to one or more Cloud Spanner rows. The 52// mutation represents an insert, update, delete, etc on a table. 53// 54// Many mutations can be applied in a single atomic commit. For purposes of 55// constraint checking (such as foreign key constraints), the operations can be 56// viewed as applying in the same order as the mutations are provided (so that, e.g., 57// a row and its logical "child" can be inserted in the same commit). 58// 59// The Apply function applies series of mutations. For example, 60// 61// m := spanner.Insert("User", 62// []string{"user_id", "profile"}, 63// []interface{}{UserID, profile}) 64// _, err := client.Apply(ctx, []*spanner.Mutation{m}) 65// 66// inserts a new row into the User table. The primary key 67// for the new row is UserID (presuming that "user_id" has been declared as the 68// primary key of the "User" table). 69// 70// To apply a series of mutations as part of an atomic read-modify-write operation, 71// use ReadWriteTransaction. 72// 73// Updating a row 74// 75// Changing the values of columns in an existing row is very similar to 76// inserting a new row: 77// 78// m := spanner.Update("User", 79// []string{"user_id", "profile"}, 80// []interface{}{UserID, profile}) 81// _, err := client.Apply(ctx, []*spanner.Mutation{m}) 82// 83// Deleting a row 84// 85// To delete a row, use spanner.Delete: 86// 87// m := spanner.Delete("User", spanner.Key{UserId}) 88// _, err := client.Apply(ctx, []*spanner.Mutation{m}) 89// 90// spanner.Delete accepts a KeySet, so you can also pass in a KeyRange, or use the 91// spanner.KeySets function to build any combination of Keys and KeyRanges. 92// 93// Note that deleting a row in a table may also delete rows from other tables 94// if cascading deletes are specified in those tables' schemas. Delete does 95// nothing if the named row does not exist (does not yield an error). 96// 97// Deleting a field 98// 99// To delete/clear a field within a row, use spanner.Update with the value nil: 100// 101// m := spanner.Update("User", 102// []string{"user_id", "profile"}, 103// []interface{}{UserID, nil}) 104// _, err := client.Apply(ctx, []*spanner.Mutation{m}) 105// 106// The valid Go types and their corresponding Cloud Spanner types that can be 107// used in the Insert/Update/InsertOrUpdate functions are: 108// 109// string, NullString - STRING 110// []string, []NullString - STRING ARRAY 111// []byte - BYTES 112// [][]byte - BYTES ARRAY 113// int, int64, NullInt64 - INT64 114// []int, []int64, []NullInt64 - INT64 ARRAY 115// bool, NullBool - BOOL 116// []bool, []NullBool - BOOL ARRAY 117// float64, NullFloat64 - FLOAT64 118// []float64, []NullFloat64 - FLOAT64 ARRAY 119// time.Time, NullTime - TIMESTAMP 120// []time.Time, []NullTime - TIMESTAMP ARRAY 121// Date, NullDate - DATE 122// []Date, []NullDate - DATE ARRAY 123// 124// To compare two Mutations for testing purposes, use reflect.DeepEqual. 125type Mutation struct { 126 // op is the operation type of the mutation. 127 // See documentation for spanner.op for more details. 128 op op 129 // Table is the name of the target table to be modified. 130 table string 131 // keySet is a set of primary keys that names the rows 132 // in a delete operation. 133 keySet KeySet 134 // columns names the set of columns that are going to be 135 // modified by Insert, InsertOrUpdate, Replace or Update 136 // operations. 137 columns []string 138 // values specifies the new values for the target columns 139 // named by Columns. 140 values []interface{} 141} 142 143// mapToMutationParams converts Go map into mutation parameters. 144func mapToMutationParams(in map[string]interface{}) ([]string, []interface{}) { 145 cols := []string{} 146 vals := []interface{}{} 147 for k, v := range in { 148 cols = append(cols, k) 149 vals = append(vals, v) 150 } 151 return cols, vals 152} 153 154// errNotStruct returns error for not getting a go struct type. 155func errNotStruct(in interface{}) error { 156 return spannerErrorf(codes.InvalidArgument, "%T is not a go struct type", in) 157} 158 159// structToMutationParams converts Go struct into mutation parameters. 160// If the input is not a valid Go struct type, structToMutationParams 161// returns error. 162func structToMutationParams(in interface{}) ([]string, []interface{}, error) { 163 if in == nil { 164 return nil, nil, errNotStruct(in) 165 } 166 v := reflect.ValueOf(in) 167 t := v.Type() 168 if t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct { 169 // t is a pointer to a struct. 170 if v.IsNil() { 171 // Return empty results. 172 return nil, nil, nil 173 } 174 // Get the struct value that in points to. 175 v = v.Elem() 176 t = t.Elem() 177 } 178 if t.Kind() != reflect.Struct { 179 return nil, nil, errNotStruct(in) 180 } 181 fields, err := fieldCache.Fields(t) 182 if err != nil { 183 return nil, nil, toSpannerError(err) 184 } 185 var cols []string 186 var vals []interface{} 187 for _, f := range fields { 188 cols = append(cols, f.Name) 189 vals = append(vals, v.FieldByIndex(f.Index).Interface()) 190 } 191 return cols, vals, nil 192} 193 194// Insert returns a Mutation to insert a row into a table. If the row already 195// exists, the write or transaction fails. 196func Insert(table string, cols []string, vals []interface{}) *Mutation { 197 return &Mutation{ 198 op: opInsert, 199 table: table, 200 columns: cols, 201 values: vals, 202 } 203} 204 205// InsertMap returns a Mutation to insert a row into a table, specified by 206// a map of column name to value. If the row already exists, the write or 207// transaction fails. 208func InsertMap(table string, in map[string]interface{}) *Mutation { 209 cols, vals := mapToMutationParams(in) 210 return Insert(table, cols, vals) 211} 212 213// InsertStruct returns a Mutation to insert a row into a table, specified by 214// a Go struct. If the row already exists, the write or transaction fails. 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 "spanner:name" 286// to provide an alternative column name, or use "spanner:-" to 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