// Run starts a query for log records, which contain request and application // level log information. func (params *Query) Run(c context.Context) *Result { req, err := makeRequest(params, internal.FullyQualifiedAppID(c), appengine.VersionID(c)) return &Result{ context: c, request: req, err: err, } }
// DeleteMulti is a batch version of Delete. func DeleteMulti(c context.Context, key []*Key) error { if len(key) == 0 { return nil } if err := multiValid(key); err != nil { return err } req := &pb.DeleteRequest{ Key: multiKeyToProto(internal.FullyQualifiedAppID(c), key), } res := &pb.DeleteResponse{} return internal.Call(c, "datastore_v3", "Delete", req, res) }
// GetMulti is a batch version of Get. // // dst must be a []S, []*S, []I or []P, for some struct type S, some interface // type I, or some non-interface non-pointer type P such that P or *P // implements PropertyLoadSaver. If an []I, each element must be a valid dst // for Get: it must be a struct pointer or implement PropertyLoadSaver. // // As a special case, PropertyList is an invalid type for dst, even though a // PropertyList is a slice of structs. It is treated as invalid to avoid being // mistakenly passed when []PropertyList was intended. func GetMulti(c context.Context, key []*Key, dst interface{}) error { v := reflect.ValueOf(dst) multiArgType, _ := checkMultiArg(v) if multiArgType == multiArgTypeInvalid { return errors.New("datastore: dst has invalid type") } if len(key) != v.Len() { return errors.New("datastore: key and dst slices have different length") } if len(key) == 0 { return nil } if err := multiValid(key); err != nil { return err } req := &pb.GetRequest{ Key: multiKeyToProto(internal.FullyQualifiedAppID(c), key), } res := &pb.GetResponse{} if err := internal.Call(c, "datastore_v3", "Get", req, res); err != nil { return err } if len(key) != len(res.Entity) { return errors.New("datastore: internal error: server returned the wrong number of entities") } multiErr, any := make(appengine.MultiError, len(key)), false for i, e := range res.Entity { if e.Entity == nil { multiErr[i] = ErrNoSuchEntity } else { elem := v.Index(i) if multiArgType == multiArgTypePropertyLoadSaver || multiArgType == multiArgTypeStruct { elem = elem.Addr() } if multiArgType == multiArgTypeStructPtr && elem.IsNil() { elem.Set(reflect.New(elem.Type().Elem())) } multiErr[i] = loadEntity(elem.Interface(), e.Entity) } if multiErr[i] != nil { any = true } } if any { return multiErr } return nil }
// PutMulti is a batch version of Put. // // src must satisfy the same conditions as the dst argument to GetMulti. func PutMulti(c context.Context, key []*Key, src interface{}) ([]*Key, error) { v := reflect.ValueOf(src) multiArgType, _ := checkMultiArg(v) if multiArgType == multiArgTypeInvalid { return nil, errors.New("datastore: src has invalid type") } if len(key) != v.Len() { return nil, errors.New("datastore: key and src slices have different length") } if len(key) == 0 { return nil, nil } appID := internal.FullyQualifiedAppID(c) if err := multiValid(key); err != nil { return nil, err } req := &pb.PutRequest{} for i := range key { elem := v.Index(i) if multiArgType == multiArgTypePropertyLoadSaver || multiArgType == multiArgTypeStruct { elem = elem.Addr() } sProto, err := saveEntity(appID, key[i], elem.Interface()) if err != nil { return nil, err } req.Entity = append(req.Entity, sProto) } res := &pb.PutResponse{} if err := internal.Call(c, "datastore_v3", "Put", req, res); err != nil { return nil, err } if len(key) != len(res.Key) { return nil, errors.New("datastore: internal error: server returned the wrong number of keys") } ret := make([]*Key, len(key)) for i := range ret { var err error ret[i], err = protoToKey(res.Key[i]) if err != nil || ret[i].Incomplete() { return nil, errors.New("datastore: internal error: server returned an invalid key") } } return ret, nil }
// NewKey creates a new key. // kind cannot be empty. // Either one or both of stringID and intID must be zero. If both are zero, // the key returned is incomplete. // parent must either be a complete key or nil. func NewKey(c context.Context, kind, stringID string, intID int64, parent *Key) *Key { // If there's a parent key, use its namespace. // Otherwise, use any namespace attached to the context. var namespace string if parent != nil { namespace = parent.namespace } else { namespace = internal.NamespaceFromContext(c) } return &Key{ kind: kind, stringID: stringID, intID: intID, parent: parent, appID: internal.FullyQualifiedAppID(c), namespace: namespace, } }
// Run runs the query in the given context. func (q *Query) Run(c context.Context) *Iterator { if q.err != nil { return &Iterator{err: q.err} } t := &Iterator{ c: c, limit: q.limit, q: q, prevCC: q.start, } var req pb.Query if err := q.toProto(&req, internal.FullyQualifiedAppID(c)); err != nil { t.err = err return t } if err := internal.Call(c, "datastore_v3", "RunQuery", &req, &t.res); err != nil { t.err = err return t } offset := q.offset - t.res.GetSkippedResults() for offset > 0 && t.res.GetMoreResults() { t.prevCC = t.res.CompiledCursor if err := callNext(t.c, &t.res, offset, t.limit); err != nil { t.err = err break } skip := t.res.GetSkippedResults() if skip < 0 { t.err = errors.New("datastore: internal error: negative number of skipped_results") break } offset -= skip } if offset < 0 { t.err = errors.New("datastore: internal error: query offset was overshot") } return t }
func handle(w http.ResponseWriter, req *http.Request) { c := appengine.NewContext(req) u := user.Current(c) if u == nil { u, _ = user.CurrentOAuth(c, "https://www.googleapis.com/auth/cloud-platform", "https://www.googleapis.com/auth/appengine.apis", ) } if u == nil || !u.Admin { w.Header().Set("Content-Type", "text/plain; charset=utf-8") w.WriteHeader(http.StatusUnauthorized) io.WriteString(w, "You must be logged in as an administrator to access this.\n") return } if req.Header.Get("X-Appcfg-Api-Version") == "" { w.Header().Set("Content-Type", "text/plain; charset=utf-8") w.WriteHeader(http.StatusForbidden) io.WriteString(w, "This request did not contain a necessary header.\n") return } if req.Method != "POST" { // Response must be YAML. rtok := req.FormValue("rtok") if rtok == "" { rtok = "0" } w.Header().Set("Content-Type", "text/yaml; charset=utf-8") fmt.Fprintf(w, `{app_id: %q, rtok: %q}`, internal.FullyQualifiedAppID(c), rtok) return } defer req.Body.Close() body, err := ioutil.ReadAll(req.Body) if err != nil { w.WriteHeader(http.StatusBadRequest) log.Errorf(c, "Failed reading body: %v", err) return } remReq := &pb.Request{} if err := proto.Unmarshal(body, remReq); err != nil { w.WriteHeader(http.StatusBadRequest) log.Errorf(c, "Bad body: %v", err) return } service, method := *remReq.ServiceName, *remReq.Method if !requestSupported(service, method) { w.WriteHeader(http.StatusBadRequest) log.Errorf(c, "Unsupported RPC /%s.%s", service, method) return } rawReq := &rawMessage{remReq.Request} rawRes := &rawMessage{} err = internal.Call(c, service, method, rawReq, rawRes) remRes := &pb.Response{} if err == nil { remRes.Response = rawRes.buf } else if ae, ok := err.(*internal.APIError); ok { remRes.ApplicationError = &pb.ApplicationError{ Code: &ae.Code, Detail: &ae.Detail, } } else { // This shouldn't normally happen. log.Errorf(c, "appengine/remote_api: Unexpected error of type %T: %v", err, err) remRes.ApplicationError = &pb.ApplicationError{ Code: proto.Int32(0), Detail: proto.String(err.Error()), } } out, err := proto.Marshal(remRes) if err != nil { // This should not be possible. w.WriteHeader(500) log.Errorf(c, "proto.Marshal: %v", err) return } log.Infof(c, "Spooling %d bytes of response to /%s.%s", len(out), service, method) w.Header().Set("Content-Type", "application/octet-stream") w.Header().Set("Content-Length", strconv.Itoa(len(out))) w.Write(out) }
// Count returns the number of results for the query. func (q *Query) Count(c context.Context) (int, error) { // Check that the query is well-formed. if q.err != nil { return 0, q.err } // Run a copy of the query, with keysOnly true (if we're not a projection, // since the two are incompatible), and an adjusted offset. We also set the // limit to zero, as we don't want any actual entity data, just the number // of skipped results. newQ := q.clone() newQ.keysOnly = len(newQ.projection) == 0 newQ.limit = 0 if q.limit < 0 { // If the original query was unlimited, set the new query's offset to maximum. newQ.offset = math.MaxInt32 } else { newQ.offset = q.offset + q.limit if newQ.offset < 0 { // Do the best we can, in the presence of overflow. newQ.offset = math.MaxInt32 } } req := &pb.Query{} if err := newQ.toProto(req, internal.FullyQualifiedAppID(c)); err != nil { return 0, err } res := &pb.QueryResult{} if err := internal.Call(c, "datastore_v3", "RunQuery", req, res); err != nil { return 0, err } // n is the count we will return. For example, suppose that our original // query had an offset of 4 and a limit of 2008: the count will be 2008, // provided that there are at least 2012 matching entities. However, the // RPCs will only skip 1000 results at a time. The RPC sequence is: // call RunQuery with (offset, limit) = (2012, 0) // 2012 == newQ.offset // response has (skippedResults, moreResults) = (1000, true) // n += 1000 // n == 1000 // call Next with (offset, limit) = (1012, 0) // 1012 == newQ.offset - n // response has (skippedResults, moreResults) = (1000, true) // n += 1000 // n == 2000 // call Next with (offset, limit) = (12, 0) // 12 == newQ.offset - n // response has (skippedResults, moreResults) = (12, false) // n += 12 // n == 2012 // // exit the loop // n -= 4 // n == 2008 var n int32 for { // The QueryResult should have no actual entity data, just skipped results. if len(res.Result) != 0 { return 0, errors.New("datastore: internal error: Count request returned too much data") } n += res.GetSkippedResults() if !res.GetMoreResults() { break } if err := callNext(c, res, newQ.offset-n, 0); err != nil { return 0, err } } n -= q.offset if n < 0 { // If the offset was greater than the number of matching entities, // return 0 instead of negative. n = 0 } return int(n), nil }