Home | History | Annotate | Download | only in datastore
      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 
      5 package datastore
      6 
      7 import (
      8 	"bytes"
      9 	"encoding/base64"
     10 	"encoding/gob"
     11 	"errors"
     12 	"fmt"
     13 	"strconv"
     14 	"strings"
     15 
     16 	"github.com/golang/protobuf/proto"
     17 	"golang.org/x/net/context"
     18 
     19 	"google.golang.org/appengine/internal"
     20 	pb "google.golang.org/appengine/internal/datastore"
     21 )
     22 
     23 // Key represents the datastore key for a stored entity, and is immutable.
     24 type Key struct {
     25 	kind      string
     26 	stringID  string
     27 	intID     int64
     28 	parent    *Key
     29 	appID     string
     30 	namespace string
     31 }
     32 
     33 // Kind returns the key's kind (also known as entity type).
     34 func (k *Key) Kind() string {
     35 	return k.kind
     36 }
     37 
     38 // StringID returns the key's string ID (also known as an entity name or key
     39 // name), which may be "".
     40 func (k *Key) StringID() string {
     41 	return k.stringID
     42 }
     43 
     44 // IntID returns the key's integer ID, which may be 0.
     45 func (k *Key) IntID() int64 {
     46 	return k.intID
     47 }
     48 
     49 // Parent returns the key's parent key, which may be nil.
     50 func (k *Key) Parent() *Key {
     51 	return k.parent
     52 }
     53 
     54 // AppID returns the key's application ID.
     55 func (k *Key) AppID() string {
     56 	return k.appID
     57 }
     58 
     59 // Namespace returns the key's namespace.
     60 func (k *Key) Namespace() string {
     61 	return k.namespace
     62 }
     63 
     64 // Incomplete returns whether the key does not refer to a stored entity.
     65 // In particular, whether the key has a zero StringID and a zero IntID.
     66 func (k *Key) Incomplete() bool {
     67 	return k.stringID == "" && k.intID == 0
     68 }
     69 
     70 // valid returns whether the key is valid.
     71 func (k *Key) valid() bool {
     72 	if k == nil {
     73 		return false
     74 	}
     75 	for ; k != nil; k = k.parent {
     76 		if k.kind == "" || k.appID == "" {
     77 			return false
     78 		}
     79 		if k.stringID != "" && k.intID != 0 {
     80 			return false
     81 		}
     82 		if k.parent != nil {
     83 			if k.parent.Incomplete() {
     84 				return false
     85 			}
     86 			if k.parent.appID != k.appID || k.parent.namespace != k.namespace {
     87 				return false
     88 			}
     89 		}
     90 	}
     91 	return true
     92 }
     93 
     94 // Equal returns whether two keys are equal.
     95 func (k *Key) Equal(o *Key) bool {
     96 	for k != nil && o != nil {
     97 		if k.kind != o.kind || k.stringID != o.stringID || k.intID != o.intID || k.appID != o.appID || k.namespace != o.namespace {
     98 			return false
     99 		}
    100 		k, o = k.parent, o.parent
    101 	}
    102 	return k == o
    103 }
    104 
    105 // root returns the furthest ancestor of a key, which may be itself.
    106 func (k *Key) root() *Key {
    107 	for k.parent != nil {
    108 		k = k.parent
    109 	}
    110 	return k
    111 }
    112 
    113 // marshal marshals the key's string representation to the buffer.
    114 func (k *Key) marshal(b *bytes.Buffer) {
    115 	if k.parent != nil {
    116 		k.parent.marshal(b)
    117 	}
    118 	b.WriteByte('/')
    119 	b.WriteString(k.kind)
    120 	b.WriteByte(',')
    121 	if k.stringID != "" {
    122 		b.WriteString(k.stringID)
    123 	} else {
    124 		b.WriteString(strconv.FormatInt(k.intID, 10))
    125 	}
    126 }
    127 
    128 // String returns a string representation of the key.
    129 func (k *Key) String() string {
    130 	if k == nil {
    131 		return ""
    132 	}
    133 	b := bytes.NewBuffer(make([]byte, 0, 512))
    134 	k.marshal(b)
    135 	return b.String()
    136 }
    137 
    138 type gobKey struct {
    139 	Kind      string
    140 	StringID  string
    141 	IntID     int64
    142 	Parent    *gobKey
    143 	AppID     string
    144 	Namespace string
    145 }
    146 
    147 func keyToGobKey(k *Key) *gobKey {
    148 	if k == nil {
    149 		return nil
    150 	}
    151 	return &gobKey{
    152 		Kind:      k.kind,
    153 		StringID:  k.stringID,
    154 		IntID:     k.intID,
    155 		Parent:    keyToGobKey(k.parent),
    156 		AppID:     k.appID,
    157 		Namespace: k.namespace,
    158 	}
    159 }
    160 
    161 func gobKeyToKey(gk *gobKey) *Key {
    162 	if gk == nil {
    163 		return nil
    164 	}
    165 	return &Key{
    166 		kind:      gk.Kind,
    167 		stringID:  gk.StringID,
    168 		intID:     gk.IntID,
    169 		parent:    gobKeyToKey(gk.Parent),
    170 		appID:     gk.AppID,
    171 		namespace: gk.Namespace,
    172 	}
    173 }
    174 
    175 func (k *Key) GobEncode() ([]byte, error) {
    176 	buf := new(bytes.Buffer)
    177 	if err := gob.NewEncoder(buf).Encode(keyToGobKey(k)); err != nil {
    178 		return nil, err
    179 	}
    180 	return buf.Bytes(), nil
    181 }
    182 
    183 func (k *Key) GobDecode(buf []byte) error {
    184 	gk := new(gobKey)
    185 	if err := gob.NewDecoder(bytes.NewBuffer(buf)).Decode(gk); err != nil {
    186 		return err
    187 	}
    188 	*k = *gobKeyToKey(gk)
    189 	return nil
    190 }
    191 
    192 func (k *Key) MarshalJSON() ([]byte, error) {
    193 	return []byte(`"` + k.Encode() + `"`), nil
    194 }
    195 
    196 func (k *Key) UnmarshalJSON(buf []byte) error {
    197 	if len(buf) < 2 || buf[0] != '"' || buf[len(buf)-1] != '"' {
    198 		return errors.New("datastore: bad JSON key")
    199 	}
    200 	k2, err := DecodeKey(string(buf[1 : len(buf)-1]))
    201 	if err != nil {
    202 		return err
    203 	}
    204 	*k = *k2
    205 	return nil
    206 }
    207 
    208 // Encode returns an opaque representation of the key
    209 // suitable for use in HTML and URLs.
    210 // This is compatible with the Python and Java runtimes.
    211 func (k *Key) Encode() string {
    212 	ref := keyToProto("", k)
    213 
    214 	b, err := proto.Marshal(ref)
    215 	if err != nil {
    216 		panic(err)
    217 	}
    218 
    219 	// Trailing padding is stripped.
    220 	return strings.TrimRight(base64.URLEncoding.EncodeToString(b), "=")
    221 }
    222 
    223 // DecodeKey decodes a key from the opaque representation returned by Encode.
    224 func DecodeKey(encoded string) (*Key, error) {
    225 	// Re-add padding.
    226 	if m := len(encoded) % 4; m != 0 {
    227 		encoded += strings.Repeat("=", 4-m)
    228 	}
    229 
    230 	b, err := base64.URLEncoding.DecodeString(encoded)
    231 	if err != nil {
    232 		return nil, err
    233 	}
    234 
    235 	ref := new(pb.Reference)
    236 	if err := proto.Unmarshal(b, ref); err != nil {
    237 		return nil, err
    238 	}
    239 
    240 	return protoToKey(ref)
    241 }
    242 
    243 // NewIncompleteKey creates a new incomplete key.
    244 // kind cannot be empty.
    245 func NewIncompleteKey(c context.Context, kind string, parent *Key) *Key {
    246 	return NewKey(c, kind, "", 0, parent)
    247 }
    248 
    249 // NewKey creates a new key.
    250 // kind cannot be empty.
    251 // Either one or both of stringID and intID must be zero. If both are zero,
    252 // the key returned is incomplete.
    253 // parent must either be a complete key or nil.
    254 func NewKey(c context.Context, kind, stringID string, intID int64, parent *Key) *Key {
    255 	// If there's a parent key, use its namespace.
    256 	// Otherwise, use any namespace attached to the context.
    257 	var namespace string
    258 	if parent != nil {
    259 		namespace = parent.namespace
    260 	} else {
    261 		namespace = internal.NamespaceFromContext(c)
    262 	}
    263 
    264 	return &Key{
    265 		kind:      kind,
    266 		stringID:  stringID,
    267 		intID:     intID,
    268 		parent:    parent,
    269 		appID:     internal.FullyQualifiedAppID(c),
    270 		namespace: namespace,
    271 	}
    272 }
    273 
    274 // AllocateIDs returns a range of n integer IDs with the given kind and parent
    275 // combination. kind cannot be empty; parent may be nil. The IDs in the range
    276 // returned will not be used by the datastore's automatic ID sequence generator
    277 // and may be used with NewKey without conflict.
    278 //
    279 // The range is inclusive at the low end and exclusive at the high end. In
    280 // other words, valid intIDs x satisfy low <= x && x < high.
    281 //
    282 // If no error is returned, low + n == high.
    283 func AllocateIDs(c context.Context, kind string, parent *Key, n int) (low, high int64, err error) {
    284 	if kind == "" {
    285 		return 0, 0, errors.New("datastore: AllocateIDs given an empty kind")
    286 	}
    287 	if n < 0 {
    288 		return 0, 0, fmt.Errorf("datastore: AllocateIDs given a negative count: %d", n)
    289 	}
    290 	if n == 0 {
    291 		return 0, 0, nil
    292 	}
    293 	req := &pb.AllocateIdsRequest{
    294 		ModelKey: keyToProto("", NewIncompleteKey(c, kind, parent)),
    295 		Size:     proto.Int64(int64(n)),
    296 	}
    297 	res := &pb.AllocateIdsResponse{}
    298 	if err := internal.Call(c, "datastore_v3", "AllocateIds", req, res); err != nil {
    299 		return 0, 0, err
    300 	}
    301 	// The protobuf is inclusive at both ends. Idiomatic Go (e.g. slices, for loops)
    302 	// is inclusive at the low end and exclusive at the high end, so we add 1.
    303 	low = res.GetStart()
    304 	high = res.GetEnd() + 1
    305 	if low+int64(n) != high {
    306 		return 0, 0, fmt.Errorf("datastore: internal error: could not allocate %d IDs", n)
    307 	}
    308 	return low, high, nil
    309 }
    310