// Encodes datum at the end of key, using direction `dir` for the encoding. // It takes in an inclusive key and returns an inclusive key if // isLastEndConstraint is not set, and an exclusive key otherwise (the idea is // that, for inclusive constraints, the value for the last column in the // constraint needs to be adapted to an exclusive span.EndKey). func encodeInclusiveEndValue( key roachpb.Key, datum parser.Datum, dir encoding.Direction, isLastEndConstraint bool, ) roachpb.Key { // Since the end of a span is exclusive, if the last constraint is an // inclusive one, we might need to make the key exclusive by applying a // PrefixEnd(). We normally avoid doing this by transforming "a = x" to // "a = x±1" for the last end constraint, depending on the encoding direction // (since this keeps the key nice and pretty-printable). // However, we might not be able to do the ±1. needExclusiveKey := false if isLastEndConstraint { if dir == encoding.Ascending { if datum.IsMax() || !datum.HasNext() { needExclusiveKey = true } else { datum = datum.Next() } } else { if datum.IsMin() || !datum.HasPrev() { needExclusiveKey = true } else { datum = datum.Prev() } } } key, err := sqlbase.EncodeTableKey(key, datum, dir) if err != nil { panic(err) } if needExclusiveKey { key = key.PrefixEnd() } return key }
func (tc *timestampCache) getMax( start, end roachpb.Key, readTSCache bool, ) (hlc.Timestamp, *uuid.UUID, bool) { if len(end) == 0 { end = start.Next() } var ok bool maxTS := tc.lowWater var maxTxnID *uuid.UUID cache := tc.wCache if readTSCache { cache = tc.rCache } for _, o := range cache.GetOverlaps(start, end) { ce := o.Value.(*cacheValue) if maxTS.Less(ce.timestamp) { ok = true maxTS = ce.timestamp maxTxnID = ce.txnID } else if maxTS.Equal(ce.timestamp) && maxTxnID != nil && (ce.txnID == nil || *maxTxnID != *ce.txnID) { maxTxnID = nil } } return maxTS, maxTxnID, ok }
func checkKeyCount(t *testing.T, kvDB *client.DB, prefix roachpb.Key, numKeys int) { if kvs, err := kvDB.Scan(context.TODO(), prefix, prefix.PrefixEnd(), 0); err != nil { t.Fatal(err) } else if l := numKeys; len(kvs) != l { t.Fatalf("expected %d key value pairs, but got %d", l, len(kvs)) } }
// prettyPrintInternal parse key with prefix in keyDict, // if the key don't march any prefix in keyDict, return its byte value with quotation and false, // or else return its human readable value and true. func prettyPrintInternal(key roachpb.Key) (string, bool) { var buf bytes.Buffer for _, k := range keyDict { if key.Compare(k.start) >= 0 && (k.end == nil || key.Compare(k.end) <= 0) { buf.WriteString(k.name) if k.end != nil && k.end.Compare(key) == 0 { buf.WriteString("/Max") return buf.String(), true } hasPrefix := false for _, e := range k.entries { if bytes.HasPrefix(key, e.prefix) { hasPrefix = true key = key[len(e.prefix):] fmt.Fprintf(&buf, "%s%s", e.name, e.ppFunc(key)) break } } if !hasPrefix { key = key[len(k.start):] fmt.Fprintf(&buf, "/%q", []byte(key)) } return buf.String(), true } } return fmt.Sprintf("%q", []byte(key)), false }
// GetIndex searches the kv list for 'key' and returns its index if found. func (s SystemConfig) GetIndex(key roachpb.Key) (int, bool) { l := len(s.Values) index := sort.Search(l, func(i int) bool { return bytes.Compare(s.Values[i].Key, key) >= 0 }) if index == l || !key.Equal(s.Values[index].Key) { return 0, false } return index, true }
// prettyKey pretty-prints the specified key, skipping over the first `skip` // fields. The pretty printed key looks like: // // /Table/<tableID>/<indexID>/... // // We always strip off the /Table prefix and then `skip` more fields. Note that // this assumes that the fields themselves do not contain '/', but that is // currently true for the fields we care about stripping (the table and index // ID). func prettyKey(key roachpb.Key, skip int) string { p := key.String() for i := 0; i <= skip; i++ { n := strings.IndexByte(p[1:], '/') if n == -1 { return "" } p = p[n+1:] } return p }
// TODO(dt): Batch checks of many rows. func (f baseFKHelper) check(values parser.DTuple) (parser.DTuple, error) { var key roachpb.Key if values != nil { keyBytes, _, err := sqlbase.EncodeIndexKey( f.searchTable, f.searchIdx, f.ids, values, f.searchPrefix) if err != nil { return nil, err } key = roachpb.Key(keyBytes) } else { key = roachpb.Key(f.searchPrefix) } spans := roachpb.Spans{roachpb.Span{Key: key, EndKey: key.PrefixEnd()}} if err := f.rf.StartScan(f.txn, spans, true /* limit batches */, 1); err != nil { return nil, err } return f.rf.NextRowDecoded() }
// PrettyPrint prints the key in a human readable format: // // Key's Format Key's Value // /Local/... "\x01"+... // /Store/... "\x01s"+... // /RangeID/... "\x01s"+[rangeid] // /[rangeid]/AbortCache/[id] "\x01s"+[rangeid]+"abc-"+[id] // /[rangeid]/Lease "\x01s"+[rangeid]+"rfll" // /[rangeid]/RaftTombstone "\x01s"+[rangeid]+"rftb" // /[rangeid]/RaftHardState "\x01s"+[rangeid]+"rfth" // /[rangeid]/RaftAppliedIndex "\x01s"+[rangeid]+"rfta" // /[rangeid]/RaftLog/logIndex:[logIndex] "\x01s"+[rangeid]+"rftl"+[logIndex] // /[rangeid]/RaftTruncatedState "\x01s"+[rangeid]+"rftt" // /[rangeid]/RaftLastIndex "\x01s"+[rangeid]+"rfti" // /[rangeid]/RangeLastReplicaGCTimestamp "\x01s"+[rangeid]+"rlrt" // /[rangeid]/RangeLastVerificationTimestamp "\x01s"+[rangeid]+"rlvt" // /[rangeid]/RangeStats "\x01s"+[rangeid]+"stat" // /Range/... "\x01k"+... // /RangeDescriptor/[key] "\x01k"+[key]+"rdsc" // /Transaction/addrKey:[key]/id:[id] "\x01k"+[key]+"txn-"+[id] // /Local/Max "\x02" // // /Meta1/[key] "\x02"+[key] // /Meta2/[key] "\x03"+[key] // /System/... "\x04" // /NodeLiveness/[key] "\x04\0x00liveness-"+[key] // /StatusNode/[key] "\x04status-node-"+[key] // /System/Max "\x05" // // /Table/[key] [key] // // /Min "" // /Max "\xff\xff" func PrettyPrint(key roachpb.Key) string { for _, k := range constKeyDict { if key.Equal(k.value) { return k.name } } for _, k := range keyOfKeyDict { if bytes.HasPrefix(key, k.prefix) { key = key[len(k.prefix):] str, formatted := prettyPrintInternal(key) if formatted { return k.name + str } return k.name + "/" + str } } str, _ := prettyPrintInternal(key) return str }
// add the specified timestamp to the cache as covering the range of // keys from start to end. If end is nil, the range covers the start // key only. txnID is nil for no transaction. readTSCache specifies // whether the command adding this timestamp should update the read // timestamp; false to update the write timestamp cache. func (tc *timestampCache) add( start, end roachpb.Key, timestamp hlc.Timestamp, txnID *uuid.UUID, readTSCache bool, ) { // This gives us a memory-efficient end key if end is empty. if len(end) == 0 { end = start.Next() start = end[:len(start)] } tc.latest.Forward(timestamp) // Only add to the cache if the timestamp is more recent than the // low water mark. if tc.lowWater.Less(timestamp) { tcache := tc.wCache if readTSCache { tcache = tc.rCache } addRange := func(r interval.Range) { value := cacheValue{timestamp: timestamp, txnID: txnID} key := tcache.MakeKey(r.Start, r.End) entry := makeCacheEntry(key, value) tcache.AddEntry(entry) } r := interval.Range{ Start: interval.Comparable(start), End: interval.Comparable(end), } // Check existing, overlapping entries and truncate/split/remove if // superseded and in the past. If existing entries are in the future, // subtract from the range/ranges that need to be added to cache. for _, entry := range tcache.GetOverlaps(r.Start, r.End) { cv := entry.Value.(*cacheValue) key := entry.Key.(*cache.IntervalKey) sCmp := r.Start.Compare(key.Start) eCmp := r.End.Compare(key.End) if cv.timestamp.Less(timestamp) { // The existing interval has a timestamp less than the new // interval. Compare interval ranges to determine how to // modify existing interval. switch { case sCmp == 0 && eCmp == 0: // New and old are equal; replace old with new and avoid the need to insert new. // // New: ------------ // Old: ------------ // // New: ------------ // Old: *cv = cacheValue{timestamp: timestamp, txnID: txnID} tcache.MoveToEnd(entry) return case sCmp <= 0 && eCmp >= 0: // New contains or is equal to old; delete old. // // New: ------------ ------------ ------------ // Old: -------- or ---------- or ---------- // // New: ------------ ------------ ------------ // Old: tcache.DelEntry(entry) case sCmp > 0 && eCmp < 0: // Old contains new; split up old into two. // // New: ---- // Old: ------------ // // New: ---- // Old: ---- ---- oldEnd := key.End key.End = r.Start newKey := tcache.MakeKey(r.End, oldEnd) newEntry := makeCacheEntry(newKey, *cv) tcache.AddEntryAfter(newEntry, entry) case eCmp >= 0: // Left partial overlap; truncate old end. // // New: -------- -------- // Old: -------- or ------------ // // New: -------- -------- // Old: ---- ---- key.End = r.Start case sCmp <= 0: // Right partial overlap; truncate old start. // // New: -------- -------- // Old: -------- or ------------ // // New: -------- -------- // Old: ---- ---- key.Start = r.End default: panic(fmt.Sprintf("no overlap between %v and %v", key.Range, r)) } } else if timestamp.Less(cv.timestamp) { // The existing interval has a timestamp greater than the new interval. // Compare interval ranges to determine how to modify new interval before // adding it to the timestamp cache. switch { case sCmp >= 0 && eCmp <= 0: // Old contains or is equal to new; no need to add. // // Old: ----------- ----------- ----------- ----------- // New: ----- or ----------- or -------- or -------- // // Old: ----------- ----------- ----------- ----------- // New: return case sCmp < 0 && eCmp > 0: // New contains old; split up old into two. We can add the left piece // immediately because it is guaranteed to be before the rest of the // overlaps. // // Old: ------ // New: ------------ // // Old: ------ // New: --- --- lr := interval.Range{Start: r.Start, End: key.Start} addRange(lr) r.Start = key.End case eCmp > 0: // Left partial overlap; truncate new start. // // Old: -------- -------- // New: -------- or ------------ // // Old: -------- -------- // New: ---- ---- r.Start = key.End case sCmp < 0: // Right partial overlap; truncate new end. // // Old: -------- -------- // New: -------- or ------------ // // Old: -------- -------- // New: ---- ---- r.End = key.Start default: panic(fmt.Sprintf("no overlap between %v and %v", key.Range, r)) } } else if (cv.txnID == nil && txnID == nil) || (cv.txnID != nil && txnID != nil && *cv.txnID == *txnID) { // The existing interval has a timestamp equal to the new // interval, and the same transaction ID. switch { case sCmp >= 0 && eCmp <= 0: // Old contains or is equal to new; no need to add. // // New: ----- or ----------- or -------- or -------- // Old: ----------- ----------- ----------- ----------- // // New: // Old: ----------- ----------- ----------- ----------- return case sCmp <= 0 && eCmp >= 0: // New contains old; delete old. // // New: ------------ ------------ ------------ // Old: -------- or ---------- or ---------- // // New: ------------ ------------ ------------ // Old: tcache.DelEntry(entry) case eCmp >= 0: // Left partial overlap; truncate old end. // // New: -------- -------- // Old: -------- or ------------ // // New: -------- -------- // Old: ---- ---- key.End = r.Start case sCmp <= 0: // Right partial overlap; truncate old start. // // New: -------- -------- // Old: -------- or ------------ // // New: -------- -------- // Old: ---- ---- key.Start = r.End default: panic(fmt.Sprintf("no overlap between %v and %v", key.Range, r)) } } else { // The existing interval has a timestamp equal to the new // interval and a different transaction ID. switch { case sCmp == 0 && eCmp == 0: // New and old are equal. Segment is no longer owned by any // transaction. // // New: ------------ // Old: ------------ // // New: // Nil: ============ // Old: cv.txnID = nil return case sCmp == 0 && eCmp > 0: // New contains old, left-aligned. Clear ownership of the // existing segment and truncate new. // // New: ------------ // Old: ---------- // // New: -- // Nil: ========== // Old: cv.txnID = nil r.Start = key.End case sCmp < 0 && eCmp == 0: // New contains old, right-aligned. Clear ownership of the // existing segment and truncate new. // // New: ------------ // Old: ---------- // // New: -- // Nil: ========== // Old: cv.txnID = nil r.End = key.Start case sCmp < 0 && eCmp > 0: // New contains old; split into three segments with the // overlap owned by no txn. // // New: ------------ // Old: -------- // // New: -- -- // Nil: ======== // Old: cv.txnID = nil newKey := tcache.MakeKey(r.Start, key.Start) newEntry := makeCacheEntry(newKey, cacheValue{timestamp: timestamp, txnID: txnID}) tcache.AddEntryAfter(newEntry, entry) r.Start = key.End case sCmp > 0 && eCmp < 0: // Old contains new; split up old into two. New segment is // owned by no txn. // // New: ---- // Old: ------------ // // New: // Nil: ==== // Old: ---- ---- txnID = nil oldEnd := key.End key.End = r.Start newKey := tcache.MakeKey(r.End, oldEnd) newEntry := makeCacheEntry(newKey, *cv) tcache.AddEntryAfter(newEntry, entry) case eCmp == 0: // Old contains new, right-aligned; truncate old end and clear // ownership of new segment. // // New: -------- // Old: ------------ // // New: // Nil: ======== // Old: ---- txnID = nil key.End = r.Start case sCmp == 0: // Old contains new, left-aligned; truncate old start and // clear ownership of new segment. // New: -------- // Old: ------------ // // New: // Nil: ======== // Old: ---- txnID = nil key.Start = r.End case eCmp > 0: // Left partial overlap; truncate old end and split new into // segments owned by no txn (the overlap) and the new txn. // // New: -------- // Old: -------- // // New: ---- // Nil: ==== // Old: ---- key.End, r.Start = r.Start, key.End newKey := tcache.MakeKey(key.End, r.Start) newCV := cacheValue{timestamp: cv.timestamp, txnID: nil} newEntry := makeCacheEntry(newKey, newCV) tcache.AddEntryAfter(newEntry, entry) case sCmp < 0: // Right partial overlap; truncate old start and split new into // segments owned by no txn (the overlap) and the new txn. // // New: -------- // Old: -------- // // New: ---- // Nil: ==== // Old: ---- key.Start, r.End = r.End, key.Start newKey := tcache.MakeKey(r.End, key.Start) newCV := cacheValue{timestamp: cv.timestamp, txnID: nil} newEntry := makeCacheEntry(newKey, newCV) tcache.AddEntryAfter(newEntry, entry) default: panic(fmt.Sprintf("no overlap between %v and %v", key.Range, r)) } } } addRange(r) } }