save.go 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  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)
  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. // saveEntity saves an EntityProto into a PropertyLoadSaver or struct pointer.
  106. func saveEntity(defaultAppID string, key *Key, src interface{}) (*pb.EntityProto, error) {
  107. var err error
  108. var props []Property
  109. if e, ok := src.(PropertyLoadSaver); ok {
  110. props, err = e.Save()
  111. } else {
  112. props, err = SaveStruct(src)
  113. }
  114. if err != nil {
  115. return nil, err
  116. }
  117. return propertiesToProto(defaultAppID, key, props)
  118. }
  119. func saveStructProperty(props *[]Property, name string, noIndex, multiple bool, v reflect.Value) error {
  120. p := Property{
  121. Name: name,
  122. NoIndex: noIndex,
  123. Multiple: multiple,
  124. }
  125. switch x := v.Interface().(type) {
  126. case *Key:
  127. p.Value = x
  128. case time.Time:
  129. p.Value = x
  130. case appengine.BlobKey:
  131. p.Value = x
  132. case appengine.GeoPoint:
  133. p.Value = x
  134. case ByteString:
  135. p.Value = x
  136. default:
  137. switch v.Kind() {
  138. case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  139. p.Value = v.Int()
  140. case reflect.Bool:
  141. p.Value = v.Bool()
  142. case reflect.String:
  143. p.Value = v.String()
  144. case reflect.Float32, reflect.Float64:
  145. p.Value = v.Float()
  146. case reflect.Slice:
  147. if v.Type().Elem().Kind() == reflect.Uint8 {
  148. p.NoIndex = true
  149. p.Value = v.Bytes()
  150. }
  151. case reflect.Struct:
  152. if !v.CanAddr() {
  153. return fmt.Errorf("datastore: unsupported struct field: value is unaddressable")
  154. }
  155. sub, err := newStructPLS(v.Addr().Interface())
  156. if err != nil {
  157. return fmt.Errorf("datastore: unsupported struct field: %v", err)
  158. }
  159. return sub.(structPLS).save(props, name, noIndex, multiple)
  160. }
  161. }
  162. if p.Value == nil {
  163. return fmt.Errorf("datastore: unsupported struct field type: %v", v.Type())
  164. }
  165. *props = append(*props, p)
  166. return nil
  167. }
  168. func (s structPLS) Save() ([]Property, error) {
  169. var props []Property
  170. if err := s.save(&props, "", false, false); err != nil {
  171. return nil, err
  172. }
  173. return props, nil
  174. }
  175. func (s structPLS) save(props *[]Property, prefix string, noIndex, multiple bool) error {
  176. for i, t := range s.codec.byIndex {
  177. if t.name == "-" {
  178. continue
  179. }
  180. name := t.name
  181. if prefix != "" {
  182. name = prefix + name
  183. }
  184. v := s.v.Field(i)
  185. if !v.IsValid() || !v.CanSet() {
  186. continue
  187. }
  188. noIndex1 := noIndex || t.noIndex
  189. // For slice fields that aren't []byte, save each element.
  190. if v.Kind() == reflect.Slice && v.Type().Elem().Kind() != reflect.Uint8 {
  191. for j := 0; j < v.Len(); j++ {
  192. if err := saveStructProperty(props, name, noIndex1, true, v.Index(j)); err != nil {
  193. return err
  194. }
  195. }
  196. continue
  197. }
  198. // Otherwise, save the field itself.
  199. if err := saveStructProperty(props, name, noIndex1, multiple, v); err != nil {
  200. return err
  201. }
  202. }
  203. return nil
  204. }
  205. func propertiesToProto(defaultAppID string, key *Key, props []Property) (*pb.EntityProto, error) {
  206. e := &pb.EntityProto{
  207. Key: keyToProto(defaultAppID, key),
  208. }
  209. if key.parent == nil {
  210. e.EntityGroup = &pb.Path{}
  211. } else {
  212. e.EntityGroup = keyToProto(defaultAppID, key.root()).Path
  213. }
  214. prevMultiple := make(map[string]bool)
  215. for _, p := range props {
  216. if pm, ok := prevMultiple[p.Name]; ok {
  217. if !pm || !p.Multiple {
  218. return nil, fmt.Errorf("datastore: multiple Properties with Name %q, but Multiple is false", p.Name)
  219. }
  220. } else {
  221. prevMultiple[p.Name] = p.Multiple
  222. }
  223. x := &pb.Property{
  224. Name: proto.String(p.Name),
  225. Value: new(pb.PropertyValue),
  226. Multiple: proto.Bool(p.Multiple),
  227. }
  228. switch v := p.Value.(type) {
  229. case int64:
  230. x.Value.Int64Value = proto.Int64(v)
  231. case bool:
  232. x.Value.BooleanValue = proto.Bool(v)
  233. case string:
  234. x.Value.StringValue = proto.String(v)
  235. if p.NoIndex {
  236. x.Meaning = pb.Property_TEXT.Enum()
  237. }
  238. case float64:
  239. x.Value.DoubleValue = proto.Float64(v)
  240. case *Key:
  241. if v != nil {
  242. x.Value.Referencevalue = keyToReferenceValue(defaultAppID, v)
  243. }
  244. case time.Time:
  245. if v.Before(minTime) || v.After(maxTime) {
  246. return nil, fmt.Errorf("datastore: time value out of range")
  247. }
  248. x.Value.Int64Value = proto.Int64(toUnixMicro(v))
  249. x.Meaning = pb.Property_GD_WHEN.Enum()
  250. case appengine.BlobKey:
  251. x.Value.StringValue = proto.String(string(v))
  252. x.Meaning = pb.Property_BLOBKEY.Enum()
  253. case appengine.GeoPoint:
  254. if !v.Valid() {
  255. return nil, fmt.Errorf("datastore: invalid GeoPoint value")
  256. }
  257. // NOTE: Strangely, latitude maps to X, longitude to Y.
  258. x.Value.Pointvalue = &pb.PropertyValue_PointValue{X: &v.Lat, Y: &v.Lng}
  259. x.Meaning = pb.Property_GEORSS_POINT.Enum()
  260. case []byte:
  261. x.Value.StringValue = proto.String(string(v))
  262. x.Meaning = pb.Property_BLOB.Enum()
  263. if !p.NoIndex {
  264. return nil, fmt.Errorf("datastore: cannot index a []byte valued Property with Name %q", p.Name)
  265. }
  266. case ByteString:
  267. x.Value.StringValue = proto.String(string(v))
  268. x.Meaning = pb.Property_BYTESTRING.Enum()
  269. default:
  270. if p.Value != nil {
  271. return nil, fmt.Errorf("datastore: invalid Value type for a Property with Name %q", p.Name)
  272. }
  273. }
  274. if p.NoIndex {
  275. e.RawProperty = append(e.RawProperty, x)
  276. } else {
  277. e.Property = append(e.Property, x)
  278. if len(e.Property) > maxIndexedProperties {
  279. return nil, errors.New("datastore: too many indexed properties")
  280. }
  281. }
  282. }
  283. return e, nil
  284. }