PageRenderTime 48ms CodeModel.GetById 6ms RepoModel.GetById 0ms app.codeStats 0ms

/appengine/datastore/save.go

https://code.google.com/p/appengine-go/
Go | 247 lines | 222 code | 13 blank | 12 comment | 57 complexity | 8cfc2bacaefd266b6887c2e3557ab93f MD5 | raw file
Possible License(s): Apache-2.0
  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. "appengine"
  12. "code.google.com/p/goprotobuf/proto"
  13. pb "appengine_internal/datastore"
  14. )
  15. var (
  16. minTime = time.Unix(0, 0)
  17. maxTime = time.Unix(int64(math.MaxInt64)/1e6, (int64(math.MaxInt64)%1e6)*1e3)
  18. )
  19. // valueToProto converts a named value to a newly allocated Property.
  20. // The returned error string is empty on success.
  21. func valueToProto(defaultAppID, name string, v reflect.Value, multiple bool) (p *pb.Property, errStr string) {
  22. var (
  23. pv pb.PropertyValue
  24. unsupported bool
  25. )
  26. switch v.Kind() {
  27. case reflect.Invalid:
  28. // No-op.
  29. case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  30. pv.Int64Value = proto.Int64(v.Int())
  31. case reflect.Bool:
  32. pv.BooleanValue = proto.Bool(v.Bool())
  33. case reflect.String:
  34. pv.StringValue = proto.String(v.String())
  35. case reflect.Float32, reflect.Float64:
  36. pv.DoubleValue = proto.Float64(v.Float())
  37. case reflect.Ptr:
  38. if k, ok := v.Interface().(*Key); ok {
  39. if k != nil {
  40. pv.Referencevalue = keyToReferenceValue(defaultAppID, k)
  41. }
  42. } else {
  43. unsupported = true
  44. }
  45. case reflect.Struct:
  46. if t, ok := v.Interface().(time.Time); ok {
  47. if t.Before(minTime) || t.After(maxTime) {
  48. return nil, "time value out of range"
  49. }
  50. pv.Int64Value = proto.Int64(t.UnixNano() / 1e3)
  51. } else {
  52. unsupported = true
  53. }
  54. case reflect.Slice:
  55. if b, ok := v.Interface().([]byte); ok {
  56. pv.StringValue = proto.String(string(b))
  57. } else {
  58. // nvToProto should already catch slice values.
  59. // If we get here, we have a slice of slice values.
  60. unsupported = true
  61. }
  62. default:
  63. unsupported = true
  64. }
  65. if unsupported {
  66. return nil, "unsupported datastore value type: " + v.Type().String()
  67. }
  68. p = &pb.Property{
  69. Name: proto.String(name),
  70. Value: &pv,
  71. Multiple: proto.Bool(multiple),
  72. }
  73. if v.IsValid() {
  74. switch v.Interface().(type) {
  75. case []byte:
  76. p.Meaning = pb.NewProperty_Meaning(pb.Property_BLOB)
  77. case appengine.BlobKey:
  78. p.Meaning = pb.NewProperty_Meaning(pb.Property_BLOBKEY)
  79. case time.Time:
  80. p.Meaning = pb.NewProperty_Meaning(pb.Property_GD_WHEN)
  81. }
  82. }
  83. return p, ""
  84. }
  85. // saveEntity saves an EntityProto into a PropertyLoadSaver or struct pointer.
  86. func saveEntity(defaultAppID string, key *Key, src interface{}) (x *pb.EntityProto, err error) {
  87. c := make(chan Property, 32)
  88. donec := make(chan struct{})
  89. go func() {
  90. x, err = propertiesToProto(defaultAppID, key, c)
  91. close(donec)
  92. }()
  93. var err1 error
  94. if e, ok := src.(PropertyLoadSaver); ok {
  95. err1 = e.Save(c)
  96. } else {
  97. err1 = SaveStruct(src, c)
  98. }
  99. <-donec
  100. if err1 != nil {
  101. return nil, err1
  102. }
  103. return x, err
  104. }
  105. func saveStructProperty(c chan<- Property, name string, noIndex, multiple bool, v reflect.Value) error {
  106. p := Property{
  107. Name: name,
  108. NoIndex: noIndex,
  109. Multiple: multiple,
  110. }
  111. switch x := v.Interface().(type) {
  112. case *Key:
  113. p.Value = x
  114. case time.Time:
  115. p.Value = x
  116. case appengine.BlobKey:
  117. p.Value = x
  118. case []byte:
  119. p.NoIndex = true
  120. p.Value = x
  121. default:
  122. switch v.Kind() {
  123. case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  124. p.Value = v.Int()
  125. case reflect.Bool:
  126. p.Value = v.Bool()
  127. case reflect.String:
  128. p.Value = v.String()
  129. case reflect.Float32, reflect.Float64:
  130. p.Value = v.Float()
  131. }
  132. }
  133. if p.Value == nil {
  134. return fmt.Errorf("datastore: unsupported struct field type: %v", v.Type())
  135. }
  136. c <- p
  137. return nil
  138. }
  139. func (s structPLS) Save(c chan<- Property) error {
  140. defer close(c)
  141. for i, t := range s.codec.byIndex {
  142. if t.name == "-" {
  143. continue
  144. }
  145. v := s.v.Field(i)
  146. if !v.IsValid() || !v.CanSet() {
  147. continue
  148. }
  149. // For slice fields that aren't []byte, save each element.
  150. if v.Kind() == reflect.Slice && v.Type() != typeOfByteSlice {
  151. for j := 0; j < v.Len(); j++ {
  152. if err := saveStructProperty(c, t.name, t.noIndex, true, v.Index(j)); err != nil {
  153. return err
  154. }
  155. }
  156. continue
  157. }
  158. // Otherwise, save the field itself.
  159. if err := saveStructProperty(c, t.name, t.noIndex, false, v); err != nil {
  160. return err
  161. }
  162. }
  163. return nil
  164. }
  165. func propertiesToProto(defaultAppID string, key *Key, src <-chan Property) (*pb.EntityProto, error) {
  166. defer func() {
  167. for _ = range src {
  168. // Drain the src channel, if we exit early.
  169. }
  170. }()
  171. e := &pb.EntityProto{
  172. Key: keyToProto(defaultAppID, key),
  173. }
  174. if key.parent == nil {
  175. e.EntityGroup = &pb.Path{}
  176. } else {
  177. e.EntityGroup = keyToProto(defaultAppID, key.root()).Path
  178. }
  179. prevMultiple := make(map[string]bool)
  180. for p := range src {
  181. if pm, ok := prevMultiple[p.Name]; ok {
  182. if !pm || !p.Multiple {
  183. return nil, fmt.Errorf("datastore: multiple Properties with Name %q, but Multiple is false", p.Name)
  184. }
  185. } else {
  186. prevMultiple[p.Name] = p.Multiple
  187. }
  188. x := &pb.Property{
  189. Name: proto.String(p.Name),
  190. Value: new(pb.PropertyValue),
  191. Multiple: proto.Bool(p.Multiple),
  192. }
  193. switch v := p.Value.(type) {
  194. case int64:
  195. x.Value.Int64Value = proto.Int64(v)
  196. case bool:
  197. x.Value.BooleanValue = proto.Bool(v)
  198. case string:
  199. x.Value.StringValue = proto.String(v)
  200. case float64:
  201. x.Value.DoubleValue = proto.Float64(v)
  202. case *Key:
  203. if v != nil {
  204. x.Value.Referencevalue = keyToReferenceValue(defaultAppID, v)
  205. }
  206. case time.Time:
  207. if v.Before(minTime) || v.After(maxTime) {
  208. return nil, fmt.Errorf("datastore: time value out of range")
  209. }
  210. x.Value.Int64Value = proto.Int64(v.UnixNano() / 1e3)
  211. x.Meaning = pb.NewProperty_Meaning(pb.Property_GD_WHEN)
  212. case appengine.BlobKey:
  213. x.Value.StringValue = proto.String(string(v))
  214. x.Meaning = pb.NewProperty_Meaning(pb.Property_BLOBKEY)
  215. case []byte:
  216. x.Value.StringValue = proto.String(string(v))
  217. x.Meaning = pb.NewProperty_Meaning(pb.Property_BLOB)
  218. if !p.NoIndex {
  219. return nil, fmt.Errorf("datastore: cannot index a []byte valued Property with Name %q", p.Name)
  220. }
  221. default:
  222. return nil, fmt.Errorf("datastore: invalid Value type for a Property with Name %q", p.Name)
  223. }
  224. if p.NoIndex {
  225. e.RawProperty = append(e.RawProperty, x)
  226. } else {
  227. e.Property = append(e.Property, x)
  228. if len(e.Property) > maxIndexedProperties {
  229. return nil, errors.New("datastore: too many indexed properties")
  230. }
  231. }
  232. }
  233. return e, nil
  234. }