Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 

628 řádky
18 KiB

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