datastore.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644
  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. package datastore
  15. import (
  16. "context"
  17. "errors"
  18. "fmt"
  19. "log"
  20. "os"
  21. "reflect"
  22. "cloud.google.com/go/internal/trace"
  23. "google.golang.org/api/option"
  24. gtransport "google.golang.org/api/transport/grpc"
  25. pb "google.golang.org/genproto/googleapis/datastore/v1"
  26. "google.golang.org/grpc"
  27. )
  28. const (
  29. prodAddr = "datastore.googleapis.com:443"
  30. userAgent = "gcloud-golang-datastore/20160401"
  31. )
  32. // ScopeDatastore grants permissions to view and/or manage datastore entities
  33. const ScopeDatastore = "https://www.googleapis.com/auth/datastore"
  34. // resourcePrefixHeader is the name of the metadata header used to indicate
  35. // the resource being operated on.
  36. const resourcePrefixHeader = "google-cloud-resource-prefix"
  37. // Client is a client for reading and writing data in a datastore dataset.
  38. type Client struct {
  39. conn *grpc.ClientConn
  40. client pb.DatastoreClient
  41. dataset string // Called dataset by the datastore API, synonym for project ID.
  42. }
  43. // NewClient creates a new Client for a given dataset.
  44. // If the project ID is empty, it is derived from the DATASTORE_PROJECT_ID environment variable.
  45. // If the DATASTORE_EMULATOR_HOST environment variable is set, client will use its value
  46. // to connect to a locally-running datastore emulator.
  47. func NewClient(ctx context.Context, projectID string, opts ...option.ClientOption) (*Client, error) {
  48. var o []option.ClientOption
  49. // Environment variables for gcd emulator:
  50. // https://cloud.google.com/datastore/docs/tools/datastore-emulator
  51. // If the emulator is available, dial it without passing any credentials.
  52. if addr := os.Getenv("DATASTORE_EMULATOR_HOST"); addr != "" {
  53. o = []option.ClientOption{
  54. option.WithEndpoint(addr),
  55. option.WithoutAuthentication(),
  56. option.WithGRPCDialOption(grpc.WithInsecure()),
  57. }
  58. } else {
  59. o = []option.ClientOption{
  60. option.WithEndpoint(prodAddr),
  61. option.WithScopes(ScopeDatastore),
  62. option.WithUserAgent(userAgent),
  63. }
  64. }
  65. // Warn if we see the legacy emulator environment variables.
  66. if os.Getenv("DATASTORE_HOST") != "" && os.Getenv("DATASTORE_EMULATOR_HOST") == "" {
  67. log.Print("WARNING: legacy environment variable DATASTORE_HOST is ignored. Use DATASTORE_EMULATOR_HOST instead.")
  68. }
  69. if os.Getenv("DATASTORE_DATASET") != "" && os.Getenv("DATASTORE_PROJECT_ID") == "" {
  70. log.Print("WARNING: legacy environment variable DATASTORE_DATASET is ignored. Use DATASTORE_PROJECT_ID instead.")
  71. }
  72. if projectID == "" {
  73. projectID = os.Getenv("DATASTORE_PROJECT_ID")
  74. }
  75. if projectID == "" {
  76. return nil, errors.New("datastore: missing project/dataset id")
  77. }
  78. o = append(o, opts...)
  79. conn, err := gtransport.Dial(ctx, o...)
  80. if err != nil {
  81. return nil, fmt.Errorf("dialing: %v", err)
  82. }
  83. return &Client{
  84. conn: conn,
  85. client: newDatastoreClient(conn, projectID),
  86. dataset: projectID,
  87. }, nil
  88. }
  89. var (
  90. // ErrInvalidEntityType is returned when functions like Get or Next are
  91. // passed a dst or src argument of invalid type.
  92. ErrInvalidEntityType = errors.New("datastore: invalid entity type")
  93. // ErrInvalidKey is returned when an invalid key is presented.
  94. ErrInvalidKey = errors.New("datastore: invalid key")
  95. // ErrNoSuchEntity is returned when no entity was found for a given key.
  96. ErrNoSuchEntity = errors.New("datastore: no such entity")
  97. )
  98. type multiArgType int
  99. const (
  100. multiArgTypeInvalid multiArgType = iota
  101. multiArgTypePropertyLoadSaver
  102. multiArgTypeStruct
  103. multiArgTypeStructPtr
  104. multiArgTypeInterface
  105. )
  106. // ErrFieldMismatch is returned when a field is to be loaded into a different
  107. // type than the one it was stored from, or when a field is missing or
  108. // unexported in the destination struct.
  109. // StructType is the type of the struct pointed to by the destination argument
  110. // passed to Get or to Iterator.Next.
  111. type ErrFieldMismatch struct {
  112. StructType reflect.Type
  113. FieldName string
  114. Reason string
  115. }
  116. func (e *ErrFieldMismatch) Error() string {
  117. return fmt.Sprintf("datastore: cannot load field %q into a %q: %s",
  118. e.FieldName, e.StructType, e.Reason)
  119. }
  120. // GeoPoint represents a location as latitude/longitude in degrees.
  121. type GeoPoint struct {
  122. Lat, Lng float64
  123. }
  124. // Valid returns whether a GeoPoint is within [-90, 90] latitude and [-180, 180] longitude.
  125. func (g GeoPoint) Valid() bool {
  126. return -90 <= g.Lat && g.Lat <= 90 && -180 <= g.Lng && g.Lng <= 180
  127. }
  128. func keyToProto(k *Key) *pb.Key {
  129. if k == nil {
  130. return nil
  131. }
  132. var path []*pb.Key_PathElement
  133. for {
  134. el := &pb.Key_PathElement{Kind: k.Kind}
  135. if k.ID != 0 {
  136. el.IdType = &pb.Key_PathElement_Id{Id: k.ID}
  137. } else if k.Name != "" {
  138. el.IdType = &pb.Key_PathElement_Name{Name: k.Name}
  139. }
  140. path = append(path, el)
  141. if k.Parent == nil {
  142. break
  143. }
  144. k = k.Parent
  145. }
  146. // The path should be in order [grandparent, parent, child]
  147. // We did it backward above, so reverse back.
  148. for i := 0; i < len(path)/2; i++ {
  149. path[i], path[len(path)-i-1] = path[len(path)-i-1], path[i]
  150. }
  151. key := &pb.Key{Path: path}
  152. if k.Namespace != "" {
  153. key.PartitionId = &pb.PartitionId{
  154. NamespaceId: k.Namespace,
  155. }
  156. }
  157. return key
  158. }
  159. // protoToKey decodes a protocol buffer representation of a key into an
  160. // equivalent *Key object. If the key is invalid, protoToKey will return the
  161. // invalid key along with ErrInvalidKey.
  162. func protoToKey(p *pb.Key) (*Key, error) {
  163. var key *Key
  164. var namespace string
  165. if partition := p.PartitionId; partition != nil {
  166. namespace = partition.NamespaceId
  167. }
  168. for _, el := range p.Path {
  169. key = &Key{
  170. Namespace: namespace,
  171. Kind: el.Kind,
  172. ID: el.GetId(),
  173. Name: el.GetName(),
  174. Parent: key,
  175. }
  176. }
  177. if !key.valid() { // Also detects key == nil.
  178. return key, ErrInvalidKey
  179. }
  180. return key, nil
  181. }
  182. // multiKeyToProto is a batch version of keyToProto.
  183. func multiKeyToProto(keys []*Key) []*pb.Key {
  184. ret := make([]*pb.Key, len(keys))
  185. for i, k := range keys {
  186. ret[i] = keyToProto(k)
  187. }
  188. return ret
  189. }
  190. // multiKeyToProto is a batch version of keyToProto.
  191. func multiProtoToKey(keys []*pb.Key) ([]*Key, error) {
  192. hasErr := false
  193. ret := make([]*Key, len(keys))
  194. err := make(MultiError, len(keys))
  195. for i, k := range keys {
  196. ret[i], err[i] = protoToKey(k)
  197. if err[i] != nil {
  198. hasErr = true
  199. }
  200. }
  201. if hasErr {
  202. return nil, err
  203. }
  204. return ret, nil
  205. }
  206. // multiValid is a batch version of Key.valid. It returns an error, not a
  207. // []bool.
  208. func multiValid(key []*Key) error {
  209. invalid := false
  210. for _, k := range key {
  211. if !k.valid() {
  212. invalid = true
  213. break
  214. }
  215. }
  216. if !invalid {
  217. return nil
  218. }
  219. err := make(MultiError, len(key))
  220. for i, k := range key {
  221. if !k.valid() {
  222. err[i] = ErrInvalidKey
  223. }
  224. }
  225. return err
  226. }
  227. // checkMultiArg checks that v has type []S, []*S, []I, or []P, for some struct
  228. // type S, for some interface type I, or some non-interface non-pointer type P
  229. // such that P or *P implements PropertyLoadSaver.
  230. //
  231. // It returns what category the slice's elements are, and the reflect.Type
  232. // that represents S, I or P.
  233. //
  234. // As a special case, PropertyList is an invalid type for v.
  235. func checkMultiArg(v reflect.Value) (m multiArgType, elemType reflect.Type) {
  236. // TODO(djd): multiArg is very confusing. Fold this logic into the
  237. // relevant Put/Get methods to make the logic less opaque.
  238. if v.Kind() != reflect.Slice {
  239. return multiArgTypeInvalid, nil
  240. }
  241. if v.Type() == typeOfPropertyList {
  242. return multiArgTypeInvalid, nil
  243. }
  244. elemType = v.Type().Elem()
  245. if reflect.PtrTo(elemType).Implements(typeOfPropertyLoadSaver) {
  246. return multiArgTypePropertyLoadSaver, elemType
  247. }
  248. switch elemType.Kind() {
  249. case reflect.Struct:
  250. return multiArgTypeStruct, elemType
  251. case reflect.Interface:
  252. return multiArgTypeInterface, elemType
  253. case reflect.Ptr:
  254. elemType = elemType.Elem()
  255. if elemType.Kind() == reflect.Struct {
  256. return multiArgTypeStructPtr, elemType
  257. }
  258. }
  259. return multiArgTypeInvalid, nil
  260. }
  261. // Close closes the Client.
  262. func (c *Client) Close() error {
  263. return c.conn.Close()
  264. }
  265. // Get loads the entity stored for key into dst, which must be a struct pointer
  266. // or implement PropertyLoadSaver. If there is no such entity for the key, Get
  267. // returns ErrNoSuchEntity.
  268. //
  269. // The values of dst's unmatched struct fields are not modified, and matching
  270. // slice-typed fields are not reset before appending to them. In particular, it
  271. // is recommended to pass a pointer to a zero valued struct on each Get call.
  272. //
  273. // ErrFieldMismatch is returned when a field is to be loaded into a different
  274. // type than the one it was stored from, or when a field is missing or
  275. // unexported in the destination struct. ErrFieldMismatch is only returned if
  276. // dst is a struct pointer.
  277. func (c *Client) Get(ctx context.Context, key *Key, dst interface{}) (err error) {
  278. ctx = trace.StartSpan(ctx, "cloud.google.com/go/datastore.Get")
  279. defer func() { trace.EndSpan(ctx, err) }()
  280. if dst == nil { // get catches nil interfaces; we need to catch nil ptr here
  281. return ErrInvalidEntityType
  282. }
  283. err = c.get(ctx, []*Key{key}, []interface{}{dst}, nil)
  284. if me, ok := err.(MultiError); ok {
  285. return me[0]
  286. }
  287. return err
  288. }
  289. // GetMulti is a batch version of Get.
  290. //
  291. // dst must be a []S, []*S, []I or []P, for some struct type S, some interface
  292. // type I, or some non-interface non-pointer type P such that P or *P
  293. // implements PropertyLoadSaver. If an []I, each element must be a valid dst
  294. // for Get: it must be a struct pointer or implement PropertyLoadSaver.
  295. //
  296. // As a special case, PropertyList is an invalid type for dst, even though a
  297. // PropertyList is a slice of structs. It is treated as invalid to avoid being
  298. // mistakenly passed when []PropertyList was intended.
  299. //
  300. // err may be a MultiError. See ExampleMultiError to check it.
  301. func (c *Client) GetMulti(ctx context.Context, keys []*Key, dst interface{}) (err error) {
  302. ctx = trace.StartSpan(ctx, "cloud.google.com/go/datastore.GetMulti")
  303. defer func() { trace.EndSpan(ctx, err) }()
  304. return c.get(ctx, keys, dst, nil)
  305. }
  306. func (c *Client) get(ctx context.Context, keys []*Key, dst interface{}, opts *pb.ReadOptions) error {
  307. v := reflect.ValueOf(dst)
  308. multiArgType, _ := checkMultiArg(v)
  309. // Sanity checks
  310. if multiArgType == multiArgTypeInvalid {
  311. return errors.New("datastore: dst has invalid type")
  312. }
  313. if len(keys) != v.Len() {
  314. return errors.New("datastore: keys and dst slices have different length")
  315. }
  316. if len(keys) == 0 {
  317. return nil
  318. }
  319. // Go through keys, validate them, serialize then, and create a dict mapping them to their indices.
  320. // Equal keys are deduped.
  321. multiErr, any := make(MultiError, len(keys)), false
  322. keyMap := make(map[string][]int, len(keys))
  323. pbKeys := make([]*pb.Key, 0, len(keys))
  324. for i, k := range keys {
  325. if !k.valid() {
  326. multiErr[i] = ErrInvalidKey
  327. any = true
  328. } else if k.Incomplete() {
  329. multiErr[i] = fmt.Errorf("datastore: can't get the incomplete key: %v", k)
  330. any = true
  331. } else {
  332. ks := k.String()
  333. if _, ok := keyMap[ks]; !ok {
  334. pbKeys = append(pbKeys, keyToProto(k))
  335. }
  336. keyMap[ks] = append(keyMap[ks], i)
  337. }
  338. }
  339. if any {
  340. return multiErr
  341. }
  342. req := &pb.LookupRequest{
  343. ProjectId: c.dataset,
  344. Keys: pbKeys,
  345. ReadOptions: opts,
  346. }
  347. resp, err := c.client.Lookup(ctx, req)
  348. if err != nil {
  349. return err
  350. }
  351. found := resp.Found
  352. missing := resp.Missing
  353. // Upper bound 100 iterations to prevent infinite loop.
  354. // We choose 100 iterations somewhat logically:
  355. // Max number of Entities you can request from Datastore is 1,000.
  356. // Max size for a Datastore Entity is 1 MiB.
  357. // Max request size is 10 MiB, so we assume max response size is also 10 MiB.
  358. // 1,000 / 10 = 100.
  359. // Note that if ctx has a deadline, the deadline will probably
  360. // be hit before we reach 100 iterations.
  361. for i := 0; len(resp.Deferred) > 0 && i < 100; i++ {
  362. req.Keys = resp.Deferred
  363. resp, err = c.client.Lookup(ctx, req)
  364. if err != nil {
  365. return err
  366. }
  367. found = append(found, resp.Found...)
  368. missing = append(missing, resp.Missing...)
  369. }
  370. filled := 0
  371. for _, e := range found {
  372. k, err := protoToKey(e.Entity.Key)
  373. if err != nil {
  374. return errors.New("datastore: internal error: server returned an invalid key")
  375. }
  376. filled += len(keyMap[k.String()])
  377. for _, index := range keyMap[k.String()] {
  378. elem := v.Index(index)
  379. if multiArgType == multiArgTypePropertyLoadSaver || multiArgType == multiArgTypeStruct {
  380. elem = elem.Addr()
  381. }
  382. if multiArgType == multiArgTypeStructPtr && elem.IsNil() {
  383. elem.Set(reflect.New(elem.Type().Elem()))
  384. }
  385. if err := loadEntityProto(elem.Interface(), e.Entity); err != nil {
  386. multiErr[index] = err
  387. any = true
  388. }
  389. }
  390. }
  391. for _, e := range missing {
  392. k, err := protoToKey(e.Entity.Key)
  393. if err != nil {
  394. return errors.New("datastore: internal error: server returned an invalid key")
  395. }
  396. filled += len(keyMap[k.String()])
  397. for _, index := range keyMap[k.String()] {
  398. multiErr[index] = ErrNoSuchEntity
  399. }
  400. any = true
  401. }
  402. if filled != len(keys) {
  403. return errors.New("datastore: internal error: server returned the wrong number of entities")
  404. }
  405. if any {
  406. return multiErr
  407. }
  408. return nil
  409. }
  410. // Put saves the entity src into the datastore with the given key. src must be
  411. // a struct pointer or implement PropertyLoadSaver; if the struct pointer has
  412. // any unexported fields they will be skipped. If the key is incomplete, the
  413. // returned key will be a unique key generated by the datastore.
  414. func (c *Client) Put(ctx context.Context, key *Key, src interface{}) (*Key, error) {
  415. k, err := c.PutMulti(ctx, []*Key{key}, []interface{}{src})
  416. if err != nil {
  417. if me, ok := err.(MultiError); ok {
  418. return nil, me[0]
  419. }
  420. return nil, err
  421. }
  422. return k[0], nil
  423. }
  424. // PutMulti is a batch version of Put.
  425. //
  426. // src must satisfy the same conditions as the dst argument to GetMulti.
  427. // err may be a MultiError. See ExampleMultiError to check it.
  428. func (c *Client) PutMulti(ctx context.Context, keys []*Key, src interface{}) (ret []*Key, err error) {
  429. // TODO(jba): rewrite in terms of Mutate.
  430. ctx = trace.StartSpan(ctx, "cloud.google.com/go/datastore.PutMulti")
  431. defer func() { trace.EndSpan(ctx, err) }()
  432. mutations, err := putMutations(keys, src)
  433. if err != nil {
  434. return nil, err
  435. }
  436. // Make the request.
  437. req := &pb.CommitRequest{
  438. ProjectId: c.dataset,
  439. Mutations: mutations,
  440. Mode: pb.CommitRequest_NON_TRANSACTIONAL,
  441. }
  442. resp, err := c.client.Commit(ctx, req)
  443. if err != nil {
  444. return nil, err
  445. }
  446. // Copy any newly minted keys into the returned keys.
  447. ret = make([]*Key, len(keys))
  448. for i, key := range keys {
  449. if key.Incomplete() {
  450. // This key is in the mutation results.
  451. ret[i], err = protoToKey(resp.MutationResults[i].Key)
  452. if err != nil {
  453. return nil, errors.New("datastore: internal error: server returned an invalid key")
  454. }
  455. } else {
  456. ret[i] = key
  457. }
  458. }
  459. return ret, nil
  460. }
  461. func putMutations(keys []*Key, src interface{}) ([]*pb.Mutation, error) {
  462. v := reflect.ValueOf(src)
  463. multiArgType, _ := checkMultiArg(v)
  464. if multiArgType == multiArgTypeInvalid {
  465. return nil, errors.New("datastore: src has invalid type")
  466. }
  467. if len(keys) != v.Len() {
  468. return nil, errors.New("datastore: key and src slices have different length")
  469. }
  470. if len(keys) == 0 {
  471. return nil, nil
  472. }
  473. if err := multiValid(keys); err != nil {
  474. return nil, err
  475. }
  476. mutations := make([]*pb.Mutation, 0, len(keys))
  477. multiErr := make(MultiError, len(keys))
  478. hasErr := false
  479. for i, k := range keys {
  480. elem := v.Index(i)
  481. // Two cases where we need to take the address:
  482. // 1) multiArgTypePropertyLoadSaver => &elem implements PLS
  483. // 2) multiArgTypeStruct => saveEntity needs *struct
  484. if multiArgType == multiArgTypePropertyLoadSaver || multiArgType == multiArgTypeStruct {
  485. elem = elem.Addr()
  486. }
  487. p, err := saveEntity(k, elem.Interface())
  488. if err != nil {
  489. multiErr[i] = err
  490. hasErr = true
  491. }
  492. var mut *pb.Mutation
  493. if k.Incomplete() {
  494. mut = &pb.Mutation{Operation: &pb.Mutation_Insert{Insert: p}}
  495. } else {
  496. mut = &pb.Mutation{Operation: &pb.Mutation_Upsert{Upsert: p}}
  497. }
  498. mutations = append(mutations, mut)
  499. }
  500. if hasErr {
  501. return nil, multiErr
  502. }
  503. return mutations, nil
  504. }
  505. // Delete deletes the entity for the given key.
  506. func (c *Client) Delete(ctx context.Context, key *Key) error {
  507. err := c.DeleteMulti(ctx, []*Key{key})
  508. if me, ok := err.(MultiError); ok {
  509. return me[0]
  510. }
  511. return err
  512. }
  513. // DeleteMulti is a batch version of Delete.
  514. //
  515. // err may be a MultiError. See ExampleMultiError to check it.
  516. func (c *Client) DeleteMulti(ctx context.Context, keys []*Key) (err error) {
  517. // TODO(jba): rewrite in terms of Mutate.
  518. ctx = trace.StartSpan(ctx, "cloud.google.com/go/datastore.DeleteMulti")
  519. defer func() { trace.EndSpan(ctx, err) }()
  520. mutations, err := deleteMutations(keys)
  521. if err != nil {
  522. return err
  523. }
  524. req := &pb.CommitRequest{
  525. ProjectId: c.dataset,
  526. Mutations: mutations,
  527. Mode: pb.CommitRequest_NON_TRANSACTIONAL,
  528. }
  529. _, err = c.client.Commit(ctx, req)
  530. return err
  531. }
  532. func deleteMutations(keys []*Key) ([]*pb.Mutation, error) {
  533. mutations := make([]*pb.Mutation, 0, len(keys))
  534. set := make(map[string]bool, len(keys))
  535. multiErr := make(MultiError, len(keys))
  536. hasErr := false
  537. for i, k := range keys {
  538. if !k.valid() {
  539. multiErr[i] = ErrInvalidKey
  540. hasErr = true
  541. } else if k.Incomplete() {
  542. multiErr[i] = fmt.Errorf("datastore: can't delete the incomplete key: %v", k)
  543. hasErr = true
  544. } else {
  545. ks := k.String()
  546. if !set[ks] {
  547. mutations = append(mutations, &pb.Mutation{
  548. Operation: &pb.Mutation_Delete{Delete: keyToProto(k)},
  549. })
  550. }
  551. set[ks] = true
  552. }
  553. }
  554. if hasErr {
  555. return nil, multiErr
  556. }
  557. return mutations, nil
  558. }
  559. // Mutate applies one or more mutations atomically.
  560. // It returns the keys of the argument Mutations, in the same order.
  561. //
  562. // If any of the mutations are invalid, Mutate returns a MultiError with the errors.
  563. // Mutate returns a MultiError in this case even if there is only one Mutation.
  564. // See ExampleMultiError to check it.
  565. func (c *Client) Mutate(ctx context.Context, muts ...*Mutation) (ret []*Key, err error) {
  566. ctx = trace.StartSpan(ctx, "cloud.google.com/go/datastore.Mutate")
  567. defer func() { trace.EndSpan(ctx, err) }()
  568. pmuts, err := mutationProtos(muts)
  569. if err != nil {
  570. return nil, err
  571. }
  572. req := &pb.CommitRequest{
  573. ProjectId: c.dataset,
  574. Mutations: pmuts,
  575. Mode: pb.CommitRequest_NON_TRANSACTIONAL,
  576. }
  577. resp, err := c.client.Commit(ctx, req)
  578. if err != nil {
  579. return nil, err
  580. }
  581. // Copy any newly minted keys into the returned keys.
  582. ret = make([]*Key, len(muts))
  583. for i, mut := range muts {
  584. if mut.key.Incomplete() {
  585. // This key is in the mutation results.
  586. ret[i], err = protoToKey(resp.MutationResults[i].Key)
  587. if err != nil {
  588. return nil, errors.New("datastore: internal error: server returned an invalid key")
  589. }
  590. } else {
  591. ret[i] = mut.key
  592. }
  593. }
  594. return ret, nil
  595. }