save.go 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. // Copyright 2011 Google Inc. All rights reserved.
  2. // Use of this source code is governed by the Apache 2.0
  3. // license that can be found in the LICENSE file.
  4. package datastore
  5. import (
  6. "errors"
  7. "fmt"
  8. "math"
  9. "reflect"
  10. "time"
  11. "github.com/golang/protobuf/proto"
  12. "google.golang.org/appengine"
  13. pb "google.golang.org/appengine/internal/datastore"
  14. )
  15. func toUnixMicro(t time.Time) int64 {
  16. // We cannot use t.UnixNano() / 1e3 because we want to handle times more than
  17. // 2^63 nanoseconds (which is about 292 years) away from 1970, and those cannot
  18. // be represented in the numerator of a single int64 divide.
  19. return t.Unix()*1e6 + int64(t.Nanosecond()/1e3)
  20. }
  21. func fromUnixMicro(t int64) time.Time {
  22. return time.Unix(t/1e6, (t%1e6)*1e3).UTC()
  23. }
  24. var (
  25. minTime = time.Unix(int64(math.MinInt64)/1e6, (int64(math.MinInt64)%1e6)*1e3)
  26. maxTime = time.Unix(int64(math.MaxInt64)/1e6, (int64(math.MaxInt64)%1e6)*1e3)
  27. )
  28. // valueToProto converts a named value to a newly allocated Property.
  29. // The returned error string is empty on success.
  30. func valueToProto(defaultAppID, name string, v reflect.Value, multiple bool) (p *pb.Property, errStr string) {
  31. var (
  32. pv pb.PropertyValue
  33. unsupported bool
  34. )
  35. switch v.Kind() {
  36. case reflect.Invalid:
  37. // No-op.
  38. case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  39. pv.Int64Value = proto.Int64(v.Int())
  40. case reflect.Bool:
  41. pv.BooleanValue = proto.Bool(v.Bool())
  42. case reflect.String:
  43. pv.StringValue = proto.String(v.String())
  44. case reflect.Float32, reflect.Float64:
  45. pv.DoubleValue = proto.Float64(v.Float())
  46. case reflect.Ptr:
  47. if k, ok := v.Interface().(*Key); ok {
  48. if k != nil {
  49. pv.Referencevalue = keyToReferenceValue(defaultAppID, k)
  50. }
  51. } else {
  52. unsupported = true
  53. }
  54. case reflect.Struct:
  55. switch t := v.Interface().(type) {
  56. case time.Time:
  57. if t.Before(minTime) || t.After(maxTime) {
  58. return nil, "time value out of range"
  59. }
  60. pv.Int64Value = proto.Int64(toUnixMicro(t))
  61. case appengine.GeoPoint:
  62. if !t.Valid() {
  63. return nil, "invalid GeoPoint value"
  64. }
  65. // NOTE: Strangely, latitude maps to X, longitude to Y.
  66. pv.Pointvalue = &pb.PropertyValue_PointValue{X: &t.Lat, Y: &t.Lng}
  67. default:
  68. unsupported = true
  69. }
  70. case reflect.Slice:
  71. if b, ok := v.Interface().([]byte); ok {
  72. pv.StringValue = proto.String(string(b))
  73. } else {
  74. // nvToProto should already catch slice values.
  75. // If we get here, we have a slice of slice values.
  76. unsupported = true
  77. }
  78. default:
  79. unsupported = true
  80. }
  81. if unsupported {
  82. return nil, "unsupported datastore value type: " + v.Type().String()
  83. }
  84. p = &pb.Property{
  85. Name: proto.String(name),
  86. Value: &pv,
  87. Multiple: proto.Bool(multiple),
  88. }
  89. if v.IsValid() {
  90. switch v.Interface().(type) {
  91. case []byte:
  92. p.Meaning = pb.Property_BLOB.Enum()
  93. case ByteString:
  94. p.Meaning = pb.Property_BYTESTRING.Enum()
  95. case appengine.BlobKey:
  96. p.Meaning = pb.Property_BLOBKEY.Enum()
  97. case time.Time:
  98. p.Meaning = pb.Property_GD_WHEN.Enum()
  99. case appengine.GeoPoint:
  100. p.Meaning = pb.Property_GEORSS_POINT.Enum()
  101. }
  102. }
  103. return p, ""
  104. }
  105. type saveOpts struct {
  106. noIndex bool
  107. multiple bool
  108. omitEmpty bool
  109. }
  110. // saveEntity saves an EntityProto into a PropertyLoadSaver or struct pointer.
  111. func saveEntity(defaultAppID string, key *Key, src interface{}) (*pb.EntityProto, error) {
  112. var err error
  113. var props []Property
  114. if e, ok := src.(PropertyLoadSaver); ok {
  115. props, err = e.Save()
  116. } else {
  117. props, err = SaveStruct(src)
  118. }
  119. if err != nil {
  120. return nil, err
  121. }
  122. return propertiesToProto(defaultAppID, key, props)
  123. }
  124. func saveStructProperty(props *[]Property, name string, opts saveOpts, v reflect.Value) error {
  125. if opts.omitEmpty && isEmptyValue(v) {
  126. return nil
  127. }
  128. p := Property{
  129. Name: name,
  130. NoIndex: opts.noIndex,
  131. Multiple: opts.multiple,
  132. }
  133. switch x := v.Interface().(type) {
  134. case *Key:
  135. p.Value = x
  136. case time.Time:
  137. p.Value = x
  138. case appengine.BlobKey:
  139. p.Value = x
  140. case appengine.GeoPoint:
  141. p.Value = x
  142. case ByteString:
  143. p.Value = x
  144. default:
  145. switch v.Kind() {
  146. case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  147. p.Value = v.Int()
  148. case reflect.Bool:
  149. p.Value = v.Bool()
  150. case reflect.String:
  151. p.Value = v.String()
  152. case reflect.Float32, reflect.Float64:
  153. p.Value = v.Float()
  154. case reflect.Slice:
  155. if v.Type().Elem().Kind() == reflect.Uint8 {
  156. p.NoIndex = true
  157. p.Value = v.Bytes()
  158. }
  159. case reflect.Struct:
  160. if !v.CanAddr() {
  161. return fmt.Errorf("datastore: unsupported struct field: value is unaddressable")
  162. }
  163. sub, err := newStructPLS(v.Addr().Interface())
  164. if err != nil {
  165. return fmt.Errorf("datastore: unsupported struct field: %v", err)
  166. }
  167. return sub.save(props, name+".", opts)
  168. }
  169. }
  170. if p.Value == nil {
  171. return fmt.Errorf("datastore: unsupported struct field type: %v", v.Type())
  172. }
  173. *props = append(*props, p)
  174. return nil
  175. }
  176. func (s structPLS) Save() ([]Property, error) {
  177. var props []Property
  178. if err := s.save(&props, "", saveOpts{}); err != nil {
  179. return nil, err
  180. }
  181. return props, nil
  182. }
  183. func (s structPLS) save(props *[]Property, prefix string, opts saveOpts) error {
  184. for name, f := range s.codec.fields {
  185. name = prefix + name
  186. v := s.v.FieldByIndex(f.path)
  187. if !v.IsValid() || !v.CanSet() {
  188. continue
  189. }
  190. var opts1 saveOpts
  191. opts1.noIndex = opts.noIndex || f.noIndex
  192. opts1.multiple = opts.multiple
  193. opts1.omitEmpty = f.omitEmpty // don't propagate
  194. // For slice fields that aren't []byte, save each element.
  195. if v.Kind() == reflect.Slice && v.Type().Elem().Kind() != reflect.Uint8 {
  196. opts1.multiple = true
  197. for j := 0; j < v.Len(); j++ {
  198. if err := saveStructProperty(props, name, opts1, v.Index(j)); err != nil {
  199. return err
  200. }
  201. }
  202. continue
  203. }
  204. // Otherwise, save the field itself.
  205. if err := saveStructProperty(props, name, opts1, v); err != nil {
  206. return err
  207. }
  208. }
  209. return nil
  210. }
  211. func propertiesToProto(defaultAppID string, key *Key, props []Property) (*pb.EntityProto, error) {
  212. e := &pb.EntityProto{
  213. Key: keyToProto(defaultAppID, key),
  214. }
  215. if key.parent == nil {
  216. e.EntityGroup = &pb.Path{}
  217. } else {
  218. e.EntityGroup = keyToProto(defaultAppID, key.root()).Path
  219. }
  220. prevMultiple := make(map[string]bool)
  221. for _, p := range props {
  222. if pm, ok := prevMultiple[p.Name]; ok {
  223. if !pm || !p.Multiple {
  224. return nil, fmt.Errorf("datastore: multiple Properties with Name %q, but Multiple is false", p.Name)
  225. }
  226. } else {
  227. prevMultiple[p.Name] = p.Multiple
  228. }
  229. x := &pb.Property{
  230. Name: proto.String(p.Name),
  231. Value: new(pb.PropertyValue),
  232. Multiple: proto.Bool(p.Multiple),
  233. }
  234. switch v := p.Value.(type) {
  235. case int64:
  236. x.Value.Int64Value = proto.Int64(v)
  237. case bool:
  238. x.Value.BooleanValue = proto.Bool(v)
  239. case string:
  240. x.Value.StringValue = proto.String(v)
  241. if p.NoIndex {
  242. x.Meaning = pb.Property_TEXT.Enum()
  243. }
  244. case float64:
  245. x.Value.DoubleValue = proto.Float64(v)
  246. case *Key:
  247. if v != nil {
  248. x.Value.Referencevalue = keyToReferenceValue(defaultAppID, v)
  249. }
  250. case time.Time:
  251. if v.Before(minTime) || v.After(maxTime) {
  252. return nil, fmt.Errorf("datastore: time value out of range")
  253. }
  254. x.Value.Int64Value = proto.Int64(toUnixMicro(v))
  255. x.Meaning = pb.Property_GD_WHEN.Enum()
  256. case appengine.BlobKey:
  257. x.Value.StringValue = proto.String(string(v))
  258. x.Meaning = pb.Property_BLOBKEY.Enum()
  259. case appengine.GeoPoint:
  260. if !v.Valid() {
  261. return nil, fmt.Errorf("datastore: invalid GeoPoint value")
  262. }
  263. // NOTE: Strangely, latitude maps to X, longitude to Y.
  264. x.Value.Pointvalue = &pb.PropertyValue_PointValue{X: &v.Lat, Y: &v.Lng}
  265. x.Meaning = pb.Property_GEORSS_POINT.Enum()
  266. case []byte:
  267. x.Value.StringValue = proto.String(string(v))
  268. x.Meaning = pb.Property_BLOB.Enum()
  269. if !p.NoIndex {
  270. return nil, fmt.Errorf("datastore: cannot index a []byte valued Property with Name %q", p.Name)
  271. }
  272. case ByteString:
  273. x.Value.StringValue = proto.String(string(v))
  274. x.Meaning = pb.Property_BYTESTRING.Enum()
  275. default:
  276. if p.Value != nil {
  277. return nil, fmt.Errorf("datastore: invalid Value type for a Property with Name %q", p.Name)
  278. }
  279. }
  280. if p.NoIndex {
  281. e.RawProperty = append(e.RawProperty, x)
  282. } else {
  283. e.Property = append(e.Property, x)
  284. if len(e.Property) > maxIndexedProperties {
  285. return nil, errors.New("datastore: too many indexed properties")
  286. }
  287. }
  288. }
  289. return e, nil
  290. }
  291. // isEmptyValue is taken from the encoding/json package in the standard library.
  292. func isEmptyValue(v reflect.Value) bool {
  293. switch v.Kind() {
  294. case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
  295. // TODO(performance): Only reflect.String needed, other property types are not supported (copy/paste from json package)
  296. return v.Len() == 0
  297. case reflect.Bool:
  298. return !v.Bool()
  299. case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  300. return v.Int() == 0
  301. case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
  302. // TODO(performance): Uint* are unsupported property types - should be removed (copy/paste from json package)
  303. return v.Uint() == 0
  304. case reflect.Float32, reflect.Float64:
  305. return v.Float() == 0
  306. case reflect.Interface, reflect.Ptr:
  307. return v.IsNil()
  308. case reflect.Struct:
  309. switch x := v.Interface().(type) {
  310. case time.Time:
  311. return x.IsZero()
  312. }
  313. }
  314. return false
  315. }