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 5package datastore 6 7import ( 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. 24type 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). 34func (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 "". 40func (k *Key) StringID() string { 41 return k.stringID 42} 43 44// IntID returns the key's integer ID, which may be 0. 45func (k *Key) IntID() int64 { 46 return k.intID 47} 48 49// Parent returns the key's parent key, which may be nil. 50func (k *Key) Parent() *Key { 51 return k.parent 52} 53 54// AppID returns the key's application ID. 55func (k *Key) AppID() string { 56 return k.appID 57} 58 59// Namespace returns the key's namespace. 60func (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. 66func (k *Key) Incomplete() bool { 67 return k.stringID == "" && k.intID == 0 68} 69 70// valid returns whether the key is valid. 71func (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. 95func (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. 106func (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. 114func (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. 129func (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 138type gobKey struct { 139 Kind string 140 StringID string 141 IntID int64 142 Parent *gobKey 143 AppID string 144 Namespace string 145} 146 147func 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 161func 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 175func (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 183func (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 192func (k *Key) MarshalJSON() ([]byte, error) { 193 return []byte(`"` + k.Encode() + `"`), nil 194} 195 196func (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. 211func (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. 224func 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. 245func 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. 254func 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. 283func 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