go.mercari.io/datastore@v1.8.2/save.go (about) 1 // Copyright 2014 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package datastore 16 17 import ( 18 "context" 19 "errors" 20 "fmt" 21 "reflect" 22 "time" 23 ) 24 25 type saveOpts struct { 26 noIndex bool 27 flatten bool 28 omitEmpty bool 29 } 30 31 // saveEntity saves an EntityProto into a PropertyLoadSaver or struct pointer. 32 func saveEntity(ctx context.Context, key Key, src interface{}) (*Entity, error) { 33 var err error 34 var props []Property 35 if e, ok := src.(PropertyLoadSaver); ok { 36 props, err = e.Save(ctx) 37 } else { 38 props, err = SaveStruct(ctx, src) 39 } 40 if err != nil { 41 return nil, err 42 } 43 44 entity, err := propertiesToProtoFake(ctx, key, props) 45 if err != nil { 46 return nil, err 47 } 48 return entity, nil 49 } 50 51 // TODO(djd): Convert this and below to return ([]Property, error). 52 func saveStructProperty(ctx context.Context, props *[]Property, name string, opts saveOpts, v reflect.Value) error { 53 p := Property{ 54 Name: name, 55 NoIndex: opts.noIndex, 56 } 57 58 if opts.omitEmpty && isEmptyValue(v) { 59 return nil 60 } 61 62 // First check if field type implements PLS. If so, use PLS to 63 // save. 64 ok, err := plsFieldSave(ctx, props, p, name, opts, v) 65 if err != nil { 66 return err 67 } 68 if ok { 69 return nil 70 } 71 72 if v.Type().AssignableTo(typeOfKey) { 73 p.Value = v.Interface() 74 75 } else { 76 // PropertyTranslator returns nil often (e.g. datastore.Key(nil)) 77 var allowNil bool 78 switch x := v.Interface().(type) { 79 case time.Time, GeoPoint: 80 p.Value = x 81 case PropertyTranslator: 82 // this case is peculiar to mercari/datastore. 83 v, err := x.ToPropertyValue(ctx) 84 if err != nil { 85 return err 86 } 87 p.Value = v 88 allowNil = true 89 default: 90 switch v.Kind() { 91 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 92 p.Value = v.Int() 93 case reflect.Bool: 94 p.Value = v.Bool() 95 case reflect.String: 96 p.Value = v.String() 97 case reflect.Float32, reflect.Float64: 98 p.Value = v.Float() 99 case reflect.Slice: 100 if v.Type().Elem().Kind() == reflect.Uint8 { 101 p.Value = v.Bytes() 102 } else { 103 return saveSliceProperty(ctx, props, name, opts, v) 104 } 105 case reflect.Ptr: 106 if isValidPointerType(v.Type().Elem()) { 107 if v.IsNil() { 108 // Nil pointer becomes a nil property value (unless omitempty, handled above). 109 p.Value = nil 110 *props = append(*props, p) 111 return nil 112 } 113 // When we recurse on the derefenced pointer, omitempty no longer applies: 114 // we already know the pointer is not empty, it doesn't matter if its referent 115 // is empty or not. 116 opts.omitEmpty = false 117 return saveStructProperty(ctx, props, name, opts, v.Elem()) 118 } 119 if v.Type().Elem().Kind() != reflect.Struct { 120 return fmt.Errorf("datastore: unsupported struct field type: %s", v.Type()) 121 } 122 // Pointer to struct is a special case. 123 if v.IsNil() { 124 return nil 125 } 126 v = v.Elem() 127 fallthrough 128 case reflect.Struct: 129 if !v.CanAddr() { 130 return fmt.Errorf("datastore: unsupported struct field: value is unaddressable") 131 } 132 vi := v.Addr().Interface() 133 134 sub, err := newStructPLS(vi) 135 if err != nil { 136 return fmt.Errorf("datastore: unsupported struct field: %v", err) 137 } 138 139 if opts.flatten { 140 return sub.save(ctx, props, opts, name+".") 141 } 142 143 var subProps []Property 144 err = sub.save(ctx, &subProps, opts, "") 145 if err != nil { 146 return err 147 } 148 subKey, err := sub.key(v) 149 if err != nil { 150 return err 151 } 152 153 p.Value = &Entity{ 154 Key: subKey, 155 Properties: subProps, 156 } 157 } 158 } 159 if !allowNil && p.Value == nil { 160 return fmt.Errorf("datastore: unsupported struct field type: %v", v.Type()) 161 } 162 } 163 164 *props = append(*props, p) 165 return nil 166 } 167 168 // plsFieldSave first tries to converts v's value to a PLS, then v's addressed 169 // value to a PLS. If neither succeeds, plsFieldSave returns false for first return 170 // value. 171 // If v is successfully converted to a PLS, plsFieldSave will then add the 172 // Value to property p by way of the PLS's Save method, and append it to props. 173 // 174 // If the flatten option is present in opts, name must be prepended to each property's 175 // name before it is appended to props. Eg. if name were "A" and a subproperty's name 176 // were "B", the resultant name of the property to be appended to props would be "A.B". 177 func plsFieldSave(ctx context.Context, props *[]Property, p Property, name string, opts saveOpts, v reflect.Value) (ok bool, err error) { 178 vpls, err := plsForSave(v) 179 if err != nil { 180 return false, err 181 } 182 183 if vpls == nil { 184 return false, nil 185 } 186 187 subProps, err := vpls.Save(ctx) 188 if err != nil { 189 return true, err 190 } 191 192 if opts.flatten { 193 for _, subp := range subProps { 194 subp.Name = name + "." + subp.Name 195 *props = append(*props, subp) 196 } 197 return true, nil 198 } 199 200 p.Value = &Entity{Properties: subProps} 201 *props = append(*props, p) 202 203 return true, nil 204 } 205 206 // key extracts the Key interface field from struct v based on the structCodec of s. 207 func (s structPLS) key(v reflect.Value) (Key, error) { 208 if v.Kind() != reflect.Struct { 209 return nil, errors.New("datastore: cannot save key of non-struct type") 210 } 211 212 keyField := s.codec.Match(keyFieldName) 213 214 if keyField == nil { 215 return nil, nil 216 } 217 218 f := v.FieldByIndex(keyField.Index) 219 k, ok := f.Interface().(Key) 220 if !ok { 221 return nil, fmt.Errorf("datastore: %s field on struct %T is not a datastore.Key", keyFieldName, v.Interface()) 222 } 223 224 return k, nil 225 } 226 227 func saveSliceProperty(ctx context.Context, props *[]Property, name string, opts saveOpts, v reflect.Value) error { 228 // Easy case: if the slice is empty, we're done. 229 if v.Len() == 0 { 230 return nil 231 } 232 // Work out the properties generated by the first element in the slice. This will 233 // usually be a single property, but will be more if this is a slice of structs. 234 var headProps []Property 235 if err := saveStructProperty(ctx, &headProps, name, opts, v.Index(0)); err != nil { 236 return err 237 } 238 239 // Convert the first element's properties into slice properties, and 240 // keep track of the values in a map. 241 values := make(map[string][]interface{}, len(headProps)) 242 for _, p := range headProps { 243 values[p.Name] = append(make([]interface{}, 0, v.Len()), p.Value) 244 } 245 246 // Find the elements for the subsequent elements. 247 for i := 1; i < v.Len(); i++ { 248 elemProps := make([]Property, 0, len(headProps)) 249 if err := saveStructProperty(ctx, &elemProps, name, opts, v.Index(i)); err != nil { 250 return err 251 } 252 for _, p := range elemProps { 253 v, ok := values[p.Name] 254 if !ok { 255 return fmt.Errorf("datastore: unexpected property %q in elem %d of slice", p.Name, i) 256 } 257 values[p.Name] = append(v, p.Value) 258 } 259 } 260 261 // Convert to the final properties. 262 for _, p := range headProps { 263 p.Value = values[p.Name] 264 *props = append(*props, p) 265 } 266 return nil 267 } 268 269 func (s structPLS) Save(ctx context.Context) ([]Property, error) { 270 var props []Property 271 if err := s.save(ctx, &props, saveOpts{}, ""); err != nil { 272 return nil, err 273 } 274 return props, nil 275 } 276 277 func (s structPLS) save(ctx context.Context, props *[]Property, opts saveOpts, prefix string) error { 278 for _, f := range s.codec { 279 name := prefix + f.Name 280 v := getField(s.v, f.Index) 281 if !v.IsValid() || !v.CanSet() { 282 continue 283 } 284 285 var tagOpts saveOpts 286 if f.ParsedTag != nil { 287 tagOpts = f.ParsedTag.(saveOpts) 288 } 289 290 var opts1 saveOpts 291 opts1.noIndex = opts.noIndex || tagOpts.noIndex 292 opts1.flatten = opts.flatten || tagOpts.flatten 293 opts1.omitEmpty = tagOpts.omitEmpty // don't propagate 294 if err := saveStructProperty(ctx, props, name, opts1, v); err != nil { 295 return err 296 } 297 } 298 return nil 299 } 300 301 // getField returns the field from v at the given index path. 302 // If it encounters a nil-valued field in the path, getField 303 // stops and returns a zero-valued reflect.Value, preventing the 304 // panic that would have been caused by reflect's FieldByIndex. 305 func getField(v reflect.Value, index []int) reflect.Value { 306 var zero reflect.Value 307 if v.Type().Kind() != reflect.Struct { 308 return zero 309 } 310 311 for _, i := range index { 312 if v.Kind() == reflect.Ptr && v.Type().Elem().Kind() == reflect.Struct { 313 if v.IsNil() { 314 return zero 315 } 316 v = v.Elem() 317 } 318 v = v.Field(i) 319 } 320 return v 321 } 322 323 func propertiesToProtoFake(ctx context.Context, key Key, props []Property) (*Entity, error) { 324 e := &Entity{ 325 Key: key, 326 Properties: props, 327 } 328 for idx, p := range props { 329 // Do not send a Key value a a field to datastore. 330 if p.Name == keyFieldName { 331 continue 332 } 333 334 val, err := interfaceToProtoFake(ctx, p.Value, p.NoIndex) 335 if err != nil { 336 return nil, fmt.Errorf("datastore: %v for a Property with Name %q", err, p.Name) 337 } 338 props[idx].Value = val 339 } 340 return e, nil 341 } 342 343 func interfaceToProtoFake(ctx context.Context, iv interface{}, noIndex bool) (interface{}, error) { 344 switch v := iv.(type) { 345 case time.Time: 346 if v.Before(minTime) || v.After(maxTime) { 347 return nil, errors.New("time value out of range") 348 } 349 // This rounding process reproduces the cloud.google.com/go/datastore 350 // don't use original time.Time's locale and UTC both. use machine default. 351 um := toUnixMicro(v) 352 return fromUnixMicro(um), nil 353 default: 354 return v, nil 355 } 356 } 357 358 // isEmptyValue is taken from the encoding/json package in the 359 // standard library. 360 func isEmptyValue(v reflect.Value) bool { 361 switch v.Kind() { 362 case reflect.Array, reflect.Map, reflect.Slice, reflect.String: 363 return v.Len() == 0 364 case reflect.Bool: 365 return !v.Bool() 366 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 367 return v.Int() == 0 368 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 369 return v.Uint() == 0 370 case reflect.Float32, reflect.Float64: 371 return v.Float() == 0 372 case reflect.Interface, reflect.Ptr: 373 return v.IsNil() 374 case reflect.Struct: 375 if t, ok := v.Interface().(time.Time); ok { 376 return t.IsZero() 377 } 378 } 379 return false 380 } 381 382 // isValidPointerType reports whether a struct field can be a pointer to type t 383 // for the purposes of saving and loading. 384 func isValidPointerType(t reflect.Type) bool { 385 if t == typeOfTime || t == typeOfGeoPoint { 386 return true 387 } 388 switch t.Kind() { 389 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 390 return true 391 case reflect.Bool: 392 return true 393 case reflect.String: 394 return true 395 case reflect.Float32, reflect.Float64: 396 return true 397 } 398 return false 399 }