diff --git a/internal/crdbtest/crdb.go b/internal/crdbtest/crdb.go index fe6b77af8f..83ad2cb950 100644 --- a/internal/crdbtest/crdb.go +++ b/internal/crdbtest/crdb.go @@ -431,6 +431,7 @@ type cockroachKeyWriter struct { logicalTimes colblk.UintBuilder untypedVersions colblk.RawBytesBuilder suffixTypes suffixTypes + prevRoachKeyLen int32 prevSuffix []byte } @@ -449,35 +450,57 @@ func (kw *cockroachKeyWriter) Reset() { kw.logicalTimes.Reset() kw.untypedVersions.Reset() kw.suffixTypes = 0 + kw.prevRoachKeyLen = 0 } func (kw *cockroachKeyWriter) ComparePrev(key []byte) colblk.KeyComparison { - var cmpv colblk.KeyComparison - cmpv.PrefixLen = int32(Split(key)) // TODO(jackson): Inline + prefixLen := Split(key) if kw.roachKeys.Rows() == 0 { - cmpv.UserKeyComparison = 1 - return cmpv - } - lp := kw.roachKeys.UnsafeGet(kw.roachKeys.Rows() - 1) - cmpv.CommonPrefixLen = int32(crbytes.CommonPrefix(lp, key[:cmpv.PrefixLen-1])) - if cmpv.CommonPrefixLen == cmpv.PrefixLen-1 { - // Adjust CommonPrefixLen to include the sentinel byte. - cmpv.CommonPrefixLen = cmpv.PrefixLen - cmpv.UserKeyComparison = int32(ComparePointSuffixes(key[cmpv.PrefixLen:], kw.prevSuffix)) - return cmpv - } - // The keys have different MVCC prefixes. We haven't determined which is - // greater, but we know the index at which they diverge. The base.Comparer - // contract dictates that prefixes must be lexicographically ordered. - if len(lp) == int(cmpv.CommonPrefixLen) { - // cmpv.PrefixLen > cmpv.PrefixLenShared; key is greater. - cmpv.UserKeyComparison = +1 - } else { - // Both keys have at least 1 additional byte at which they diverge. - // Compare the diverging byte. - cmpv.UserKeyComparison = int32(cmp.Compare(key[cmpv.CommonPrefixLen], lp[cmpv.CommonPrefixLen])) + return colblk.KeyComparison{ + PrefixLen: int32(prefixLen), + CommonPrefixLen: 0, + UserKeyComparison: +1, + } + } + lastRoachKey := kw.roachKeys.UnsafeGet(kw.roachKeys.Rows() - 1) + commonPrefixLen := crbytes.CommonPrefix(lastRoachKey, key[:prefixLen-1]) + if len(lastRoachKey) == commonPrefixLen { + if invariants.Enabled && len(lastRoachKey) > prefixLen-1 { + panic(errors.AssertionFailedf("out-of-order keys: previous roach key %q > roach key of key %q", + lastRoachKey, key)) + } + // All the bytes of the previous roach key form a byte-wise prefix of + // [key]'s prefix. The last byte of the previous prefix is the 0x00 + // sentinel byte, which is not stored within roachKeys. It's possible + // that [key] also has a 0x00 byte in the same position (either also + // serving as a sentinel byte, in which case the prefixes are equal, or + // not in which case [key] is greater). In both cases, we need to + // increment CommonPrefixLen. + if key[commonPrefixLen] == 0x00 { + commonPrefixLen++ + if commonPrefixLen == prefixLen { + // The prefixes are equal; compare the suffixes. + return colblk.KeyComparison{ + PrefixLen: int32(prefixLen), + CommonPrefixLen: int32(commonPrefixLen), + UserKeyComparison: int32(ComparePointSuffixes(key[prefixLen:], kw.prevSuffix)), + } + } + } + // prefixLen > commonPrefixLen; key is greater. + return colblk.KeyComparison{ + PrefixLen: int32(prefixLen), + CommonPrefixLen: int32(commonPrefixLen), + UserKeyComparison: +1, + } + } + // Both keys have at least 1 additional byte at which they diverge. + // Compare the diverging byte. + return colblk.KeyComparison{ + PrefixLen: int32(prefixLen), + CommonPrefixLen: int32(commonPrefixLen), + UserKeyComparison: int32(cmp.Compare(key[commonPrefixLen], lastRoachKey[commonPrefixLen])), } - return cmpv } func (kw *cockroachKeyWriter) WriteKey( @@ -496,9 +519,10 @@ func (kw *cockroachKeyWriter) WriteKey( // TODO(jackson): Avoid copying the previous suffix. kw.prevSuffix = append(kw.prevSuffix[:0], key[keyPrefixLen:]...) - // When the roach key is the same, keyPrefixLenSharedWithPrev includes the - // separator byte. - kw.roachKeys.Put(key[:keyPrefixLen-1], min(int(keyPrefixLenSharedWithPrev), int(keyPrefixLen)-1)) + // When the roach key is the same or contain the previous key as a prefix, + // keyPrefixLenSharedWithPrev includes the previous key's separator byte. + kw.roachKeys.Put(key[:keyPrefixLen-1], min(int(keyPrefixLenSharedWithPrev), int(kw.prevRoachKeyLen))) + kw.prevRoachKeyLen = keyPrefixLen - 1 // NB: The w.logicalTimes builder was initialized with InitWithDefault, so // if we don't set a value, the column value is implicitly zero. We only diff --git a/internal/crdbtest/crdb_test.go b/internal/crdbtest/crdb_test.go index b15698b7a2..1a6e0e81e9 100644 --- a/internal/crdbtest/crdb_test.go +++ b/internal/crdbtest/crdb_test.go @@ -9,6 +9,7 @@ import ( "fmt" "math/rand/v2" "slices" + "strconv" "strings" "testing" "time" @@ -386,6 +387,9 @@ func formatUserKey(key []byte) string { if key[len(key)-1] != byte(len(suffix)+1) { panic("invalid suffix length byte") } + if bytes.ContainsFunc(prefix, func(r rune) bool { return r < ' ' || r > '~' }) { + return fmt.Sprintf("%q @ %X", prefix, suffix) + } return fmt.Sprintf("%s @ %X", prefix, suffix) } @@ -423,6 +427,15 @@ func formatKV(kv base.InternalKV) string { // foo @ 0001020304050607 func parseUserKey(userKeyStr string) []byte { roachKey, versionStr := splitStringAt(userKeyStr, " @ ") + + if len(roachKey) >= 2 && roachKey[0] == '"' && roachKey[len(roachKey)-1] == '"' { + var err error + roachKey, err = strconv.Unquote(roachKey) + if err != nil { + panic(fmt.Sprintf("invalid user key string %s: %v", userKeyStr, err)) + } + } + // Append sentinel byte. userKey := append([]byte(roachKey), 0) if versionStr != "" { diff --git a/internal/crdbtest/key_schema_test.go b/internal/crdbtest/key_schema_test.go index b9de75aea1..e45694122e 100644 --- a/internal/crdbtest/key_schema_test.go +++ b/internal/crdbtest/key_schema_test.go @@ -43,7 +43,7 @@ func runDataDrivenTest(t *testing.T, path string) { e.Add(key, value, 0, kcmp, false /* isObsolete */) buf = e.MaterializeLastUserKey(buf[:0]) if !Comparer.Equal(key.UserKey, buf) { - td.Fatalf(t, "incorect MaterializeLastKey: %s instead of %s", formatUserKey(buf), formatUserKey(key.UserKey)) + td.Fatalf(t, "incorrect MaterializeLastKey: %s instead of %s", formatUserKey(buf), formatUserKey(key.UserKey)) } } numRows := e.Rows() diff --git a/internal/crdbtest/testdata/block_encoding b/internal/crdbtest/testdata/block_encoding index 0a71e663bd..e981160bf3 100644 --- a/internal/crdbtest/testdata/block_encoding +++ b/internal/crdbtest/testdata/block_encoding @@ -13,10 +13,11 @@ foo @ 010203040506070800000000 #4,SET = mvcc4 foo @ 0102030405060708 #3,SET = mvcc3 foo @ 01020304050607080000000001 #2,SET = mvcc2 foo @ 0102030405060708 #1,SET = mvcc1 +"foo\x00" @ 0102030405060708 #1,SET = mvcc1 fop #1,SET = foo x #1,SET = x ---- -12 rows, total size 411B +13 rows, total size 433B keys ---- @@ -30,6 +31,7 @@ foo @ 0102030405060708 #4,SET = mvcc4 foo @ 0102030405060708 #3,SET = mvcc3 foo @ 0102030405060708 #2,SET = mvcc2 foo @ 0102030405060708 #1,SET = mvcc1 +"foo\x00" @ 0102030405060708 #1,SET = mvcc1 fop #1,SET = foo x #1,SET = x @@ -41,61 +43,63 @@ data block header │ ├── 001-005: x 16000000 # maximum key length: 22 │ ├── 005-006: x 01 # version 1 │ ├── 006-008: x 0900 # 9 columns - │ ├── 008-012: x 0c000000 # 12 rows + │ ├── 008-012: x 0d000000 # 13 rows │ ├── 012-013: b 00000100 # col 0: prefixbytes │ ├── 013-017: x 39000000 # col 0: page start 57 │ ├── 017-018: b 00000010 # col 1: uint - │ ├── 018-022: x 50000000 # col 1: page start 80 + │ ├── 018-022: x 55000000 # col 1: page start 85 │ ├── 022-023: b 00000010 # col 2: uint - │ ├── 023-027: x b8000000 # col 2: page start 184 + │ ├── 023-027: x c0000000 # col 2: page start 192 │ ├── 027-028: b 00000011 # col 3: bytes - │ ├── 028-032: x ec000000 # col 3: page start 236 + │ ├── 028-032: x f8000000 # col 3: page start 248 │ ├── 032-033: b 00000010 # col 4: uint - │ ├── 033-037: x 0b010000 # col 4: page start 267 + │ ├── 033-037: x 18010000 # col 4: page start 280 │ ├── 037-038: b 00000001 # col 5: bool - │ ├── 038-042: x 24010000 # col 5: page start 292 + │ ├── 038-042: x 34010000 # col 5: page start 308 │ ├── 042-043: b 00000011 # col 6: bytes - │ ├── 043-047: x 38010000 # col 6: page start 312 + │ ├── 043-047: x 48010000 # col 6: page start 328 │ ├── 047-048: b 00000001 # col 7: bool - │ ├── 048-052: x 98010000 # col 7: page start 408 + │ ├── 048-052: x ae010000 # col 7: page start 430 │ ├── 052-053: b 00000001 # col 8: bool - │ └── 053-057: x 99010000 # col 8: page start 409 + │ └── 053-057: x af010000 # col 8: page start 431 ├── data for column 0 (prefixbytes) │ ├── 057-058: x 04 # bundle size: 16 │ ├── offsets table │ │ ├── 058-059: x 01 # encoding: 1b - │ │ ├── 059-060: x 00 # data[0] = 0 [73 overall] - │ │ ├── 060-061: x 00 # data[1] = 0 [73 overall] - │ │ ├── 061-062: x 03 # data[2] = 3 [76 overall] - │ │ ├── 062-063: x 03 # data[3] = 3 [76 overall] - │ │ ├── 063-064: x 03 # data[4] = 3 [76 overall] - │ │ ├── 064-065: x 03 # data[5] = 3 [76 overall] - │ │ ├── 065-066: x 03 # data[6] = 3 [76 overall] - │ │ ├── 066-067: x 03 # data[7] = 3 [76 overall] - │ │ ├── 067-068: x 03 # data[8] = 3 [76 overall] - │ │ ├── 068-069: x 03 # data[9] = 3 [76 overall] - │ │ ├── 069-070: x 03 # data[10] = 3 [76 overall] - │ │ ├── 070-071: x 03 # data[11] = 3 [76 overall] - │ │ ├── 071-072: x 06 # data[12] = 6 [79 overall] - │ │ └── 072-073: x 07 # data[13] = 7 [80 overall] + │ │ ├── 059-060: x 00 # data[0] = 0 [74 overall] + │ │ ├── 060-061: x 00 # data[1] = 0 [74 overall] + │ │ ├── 061-062: x 03 # data[2] = 3 [77 overall] + │ │ ├── 062-063: x 03 # data[3] = 3 [77 overall] + │ │ ├── 063-064: x 03 # data[4] = 3 [77 overall] + │ │ ├── 064-065: x 03 # data[5] = 3 [77 overall] + │ │ ├── 065-066: x 03 # data[6] = 3 [77 overall] + │ │ ├── 066-067: x 03 # data[7] = 3 [77 overall] + │ │ ├── 067-068: x 03 # data[8] = 3 [77 overall] + │ │ ├── 068-069: x 03 # data[9] = 3 [77 overall] + │ │ ├── 069-070: x 03 # data[10] = 3 [77 overall] + │ │ ├── 070-071: x 03 # data[11] = 3 [77 overall] + │ │ ├── 071-072: x 07 # data[12] = 7 [81 overall] + │ │ ├── 072-073: x 0a # data[13] = 10 [84 overall] + │ │ └── 073-074: x 0b # data[14] = 11 [85 overall] │ └── data - │ ├── 073-073: x # data[00]: (block prefix) - │ ├── 073-073: x # data[01]: (bundle prefix) - │ ├── 073-076: x 666f6f # data[02]: foo - │ ├── 076-076: x # data[03]: ... - │ ├── 076-076: x # data[04]: ... - │ ├── 076-076: x # data[05]: ... - │ ├── 076-076: x # data[06]: ... - │ ├── 076-076: x # data[07]: ... - │ ├── 076-076: x # data[08]: ... - │ ├── 076-076: x # data[09]: ... - │ ├── 076-076: x # data[10]: ... - │ ├── 076-076: x # data[11]: ... - │ ├── 076-079: x 666f70 # data[12]: fop - │ └── 079-080: x 78 # data[13]: x + │ ├── 074-074: x # data[00]: (block prefix) + │ ├── 074-074: x # data[01]: (bundle prefix) + │ ├── 074-077: x 666f6f # data[02]: foo + │ ├── 077-077: x # data[03]: ... + │ ├── 077-077: x # data[04]: ... + │ ├── 077-077: x # data[05]: ... + │ ├── 077-077: x # data[06]: ... + │ ├── 077-077: x # data[07]: ... + │ ├── 077-077: x # data[08]: ... + │ ├── 077-077: x # data[09]: ... + │ ├── 077-077: x # data[10]: ... + │ ├── 077-077: x # data[11]: ... + │ ├── 077-081: x 666f6f00 # data[12]: "foo\x00" + │ ├── 081-084: x 666f70 # data[13]: fop + │ └── 084-085: x 78 # data[14]: x ├── data for column 1 (uint) - │ ├── 080-081: x 08 # encoding: 8b - │ ├── 081-088: x 00000000000000 # padding (aligning to 64-bit boundary) + │ ├── 085-086: x 08 # encoding: 8b + │ ├── 086-088: x 0000 # padding (aligning to 64-bit boundary) │ ├── 088-096: x 0000000000000000 # data[0] = 0 │ ├── 096-104: x 0000000000000000 # data[1] = 0 │ ├── 104-112: x 0000000000000000 # data[2] = 0 @@ -106,102 +110,110 @@ data block header │ ├── 144-152: x 0807060504030201 # data[7] = 72623859790382856 │ ├── 152-160: x 0807060504030201 # data[8] = 72623859790382856 │ ├── 160-168: x 0807060504030201 # data[9] = 72623859790382856 - │ ├── 168-176: x 0000000000000000 # data[10] = 0 - │ └── 176-184: x 0000000000000000 # data[11] = 0 + │ ├── 168-176: x 0807060504030201 # data[10] = 72623859790382856 + │ ├── 176-184: x 0000000000000000 # data[11] = 0 + │ └── 184-192: x 0000000000000000 # data[12] = 0 ├── data for column 2 (uint) - │ ├── 184-185: x 04 # encoding: 4b - │ ├── 185-188: x 000000 # padding (aligning to 32-bit boundary) - │ ├── 188-192: x 00000000 # data[0] = 0 - │ ├── 192-196: x 00000000 # data[1] = 0 - │ ├── 196-200: x 00000000 # data[2] = 0 - │ ├── 200-204: x 00000000 # data[3] = 0 - │ ├── 204-208: x 04030201 # data[4] = 16909060 - │ ├── 208-212: x 04030201 # data[5] = 16909060 - │ ├── 212-216: x 00000000 # data[6] = 0 - │ ├── 216-220: x 00000000 # data[7] = 0 - │ ├── 220-224: x 00000000 # data[8] = 0 - │ ├── 224-228: x 00000000 # data[9] = 0 - │ ├── 228-232: x 00000000 # data[10] = 0 - │ └── 232-236: x 00000000 # data[11] = 0 + │ ├── 192-193: x 04 # encoding: 4b + │ ├── 193-196: x 000000 # padding (aligning to 32-bit boundary) + │ ├── 196-200: x 00000000 # data[0] = 0 + │ ├── 200-204: x 00000000 # data[1] = 0 + │ ├── 204-208: x 00000000 # data[2] = 0 + │ ├── 208-212: x 00000000 # data[3] = 0 + │ ├── 212-216: x 04030201 # data[4] = 16909060 + │ ├── 216-220: x 04030201 # data[5] = 16909060 + │ ├── 220-224: x 00000000 # data[6] = 0 + │ ├── 224-228: x 00000000 # data[7] = 0 + │ ├── 228-232: x 00000000 # data[8] = 0 + │ ├── 232-236: x 00000000 # data[9] = 0 + │ ├── 236-240: x 00000000 # data[10] = 0 + │ ├── 240-244: x 00000000 # data[11] = 0 + │ └── 244-248: x 00000000 # data[12] = 0 ├── data for column 3 (bytes) │ ├── offsets table - │ │ ├── 236-237: x 01 # encoding: 1b - │ │ ├── 237-238: x 00 # data[0] = 0 [250 overall] - │ │ ├── 238-239: x 00 # data[1] = 0 [250 overall] - │ │ ├── 239-240: x 00 # data[2] = 0 [250 overall] - │ │ ├── 240-241: x 00 # data[3] = 0 [250 overall] - │ │ ├── 241-242: x 11 # data[4] = 17 [267 overall] - │ │ ├── 242-243: x 11 # data[5] = 17 [267 overall] - │ │ ├── 243-244: x 11 # data[6] = 17 [267 overall] - │ │ ├── 244-245: x 11 # data[7] = 17 [267 overall] - │ │ ├── 245-246: x 11 # data[8] = 17 [267 overall] - │ │ ├── 246-247: x 11 # data[9] = 17 [267 overall] - │ │ ├── 247-248: x 11 # data[10] = 17 [267 overall] - │ │ ├── 248-249: x 11 # data[11] = 17 [267 overall] - │ │ └── 249-250: x 11 # data[12] = 17 [267 overall] + │ │ ├── 248-249: x 01 # encoding: 1b + │ │ ├── 249-250: x 00 # data[0] = 0 [263 overall] + │ │ ├── 250-251: x 00 # data[1] = 0 [263 overall] + │ │ ├── 251-252: x 00 # data[2] = 0 [263 overall] + │ │ ├── 252-253: x 00 # data[3] = 0 [263 overall] + │ │ ├── 253-254: x 11 # data[4] = 17 [280 overall] + │ │ ├── 254-255: x 11 # data[5] = 17 [280 overall] + │ │ ├── 255-256: x 11 # data[6] = 17 [280 overall] + │ │ ├── 256-257: x 11 # data[7] = 17 [280 overall] + │ │ ├── 257-258: x 11 # data[8] = 17 [280 overall] + │ │ ├── 258-259: x 11 # data[9] = 17 [280 overall] + │ │ ├── 259-260: x 11 # data[10] = 17 [280 overall] + │ │ ├── 260-261: x 11 # data[11] = 17 [280 overall] + │ │ ├── 261-262: x 11 # data[12] = 17 [280 overall] + │ │ └── 262-263: x 11 # data[13] = 17 [280 overall] │ └── data - │ ├── 250-250: x # data[0]: - │ ├── 250-250: x # data[1]: - │ ├── 250-250: x # data[2]: - │ ├── 250-267: x 0102030405060708091011121314151617 # data[3]: "\x01\x02\x03\x04\x05\x06\a\b\t\x10\x11\x12\x13\x14\x15\x16\x17" - │ ├── 267-267: x # data[4]: - │ ├── 267-267: x # data[5]: - │ ├── 267-267: x # data[6]: - │ ├── 267-267: x # data[7]: - │ ├── 267-267: x # data[8]: - │ ├── 267-267: x # data[9]: - │ ├── 267-267: x # data[10]: - │ └── 267-267: x # data[11]: + │ ├── 263-263: x # data[0]: + │ ├── 263-263: x # data[1]: + │ ├── 263-263: x # data[2]: + │ ├── 263-280: x 0102030405060708091011121314151617 # data[3]: "\x01\x02\x03\x04\x05\x06\a\b\t\x10\x11\x12\x13\x14\x15\x16\x17" + │ ├── 280-280: x # data[4]: + │ ├── 280-280: x # data[5]: + │ ├── 280-280: x # data[6]: + │ ├── 280-280: x # data[7]: + │ ├── 280-280: x # data[8]: + │ ├── 280-280: x # data[9]: + │ ├── 280-280: x # data[10]: + │ ├── 280-280: x # data[11]: + │ └── 280-280: x # data[12]: ├── data for column 4 (uint) - │ ├── 267-268: x 02 # encoding: 2b - │ ├── 268-270: x 0103 # data[0] = 769 - │ ├── 270-272: x 0102 # data[1] = 513 - │ ├── 272-274: x 0101 # data[2] = 257 - │ ├── 274-276: x 0101 # data[3] = 257 - │ ├── 276-278: x 0102 # data[4] = 513 - │ ├── 278-280: x 0101 # data[5] = 257 - │ ├── 280-282: x 0104 # data[6] = 1025 - │ ├── 282-284: x 0103 # data[7] = 769 - │ ├── 284-286: x 0102 # data[8] = 513 - │ ├── 286-288: x 0101 # data[9] = 257 - │ ├── 288-290: x 0101 # data[10] = 257 - │ └── 290-292: x 0101 # data[11] = 257 + │ ├── 280-281: x 02 # encoding: 2b + │ ├── 281-282: x 00 # padding (aligning to 16-bit boundary) + │ ├── 282-284: x 0103 # data[0] = 769 + │ ├── 284-286: x 0102 # data[1] = 513 + │ ├── 286-288: x 0101 # data[2] = 257 + │ ├── 288-290: x 0101 # data[3] = 257 + │ ├── 290-292: x 0102 # data[4] = 513 + │ ├── 292-294: x 0101 # data[5] = 257 + │ ├── 294-296: x 0104 # data[6] = 1025 + │ ├── 296-298: x 0103 # data[7] = 769 + │ ├── 298-300: x 0102 # data[8] = 513 + │ ├── 300-302: x 0101 # data[9] = 257 + │ ├── 302-304: x 0101 # data[10] = 257 + │ ├── 304-306: x 0101 # data[11] = 257 + │ └── 306-308: x 0101 # data[12] = 257 ├── data for column 5 (bool) - │ ├── 292-293: x 00 # default bitmap encoding - │ ├── 293-296: x 000000 # padding to align to 64-bit boundary - │ ├── 296-304: b 0000000100001100000000000000000000000000000000000000000000000000 # bitmap word 0 - │ └── 304-312: b 0000000100000000000000000000000000000000000000000000000000000000 # bitmap summary word 0-63 + │ ├── 308-309: x 00 # default bitmap encoding + │ ├── 309-312: x 000000 # padding to align to 64-bit boundary + │ ├── 312-320: b 0000000100011100000000000000000000000000000000000000000000000000 # bitmap word 0 + │ └── 320-328: b 0000000100000000000000000000000000000000000000000000000000000000 # bitmap summary word 0-63 ├── data for column 6 (bytes) │ ├── offsets table - │ │ ├── 312-313: x 01 # encoding: 1b - │ │ ├── 313-314: x 00 # data[0] = 0 [326 overall] - │ │ ├── 314-315: x 09 # data[1] = 9 [335 overall] - │ │ ├── 315-316: x 12 # data[2] = 18 [344 overall] - │ │ ├── 316-317: x 1b # data[3] = 27 [353 overall] - │ │ ├── 317-318: x 22 # data[4] = 34 [360 overall] - │ │ ├── 318-319: x 2e # data[5] = 46 [372 overall] - │ │ ├── 319-320: x 3a # data[6] = 58 [384 overall] - │ │ ├── 320-321: x 3f # data[7] = 63 [389 overall] - │ │ ├── 321-322: x 44 # data[8] = 68 [394 overall] - │ │ ├── 322-323: x 49 # data[9] = 73 [399 overall] - │ │ ├── 323-324: x 4e # data[10] = 78 [404 overall] - │ │ ├── 324-325: x 51 # data[11] = 81 [407 overall] - │ │ └── 325-326: x 52 # data[12] = 82 [408 overall] + │ │ ├── 328-329: x 01 # encoding: 1b + │ │ ├── 329-330: x 00 # data[0] = 0 [343 overall] + │ │ ├── 330-331: x 09 # data[1] = 9 [352 overall] + │ │ ├── 331-332: x 12 # data[2] = 18 [361 overall] + │ │ ├── 332-333: x 1b # data[3] = 27 [370 overall] + │ │ ├── 333-334: x 22 # data[4] = 34 [377 overall] + │ │ ├── 334-335: x 2e # data[5] = 46 [389 overall] + │ │ ├── 335-336: x 3a # data[6] = 58 [401 overall] + │ │ ├── 336-337: x 3f # data[7] = 63 [406 overall] + │ │ ├── 337-338: x 44 # data[8] = 68 [411 overall] + │ │ ├── 338-339: x 49 # data[9] = 73 [416 overall] + │ │ ├── 339-340: x 4e # data[10] = 78 [421 overall] + │ │ ├── 340-341: x 53 # data[11] = 83 [426 overall] + │ │ ├── 341-342: x 56 # data[12] = 86 [429 overall] + │ │ └── 342-343: x 57 # data[13] = 87 [430 overall] │ └── data - │ ├── 326-335: x 6e6f73756666697833 # data[0]: nosuffix3 - │ ├── 335-344: x 6e6f73756666697832 # data[1]: nosuffix2 - │ ├── 344-353: x 6e6f73756666697831 # data[2]: nosuffix1 - │ ├── 353-360: x 6c6f636b6b6579 # data[3]: lockkey - │ ├── 360-372: x 6d7663636c6f676963616c32 # data[4]: mvcclogical2 - │ ├── 372-384: x 6d7663636c6f676963616c31 # data[5]: mvcclogical1 - │ ├── 384-389: x 6d76636334 # data[6]: mvcc4 - │ ├── 389-394: x 6d76636333 # data[7]: mvcc3 - │ ├── 394-399: x 6d76636332 # data[8]: mvcc2 - │ ├── 399-404: x 6d76636331 # data[9]: mvcc1 - │ ├── 404-407: x 666f6f # data[10]: foo - │ └── 407-408: x 78 # data[11]: x + │ ├── 343-352: x 6e6f73756666697833 # data[0]: nosuffix3 + │ ├── 352-361: x 6e6f73756666697832 # data[1]: nosuffix2 + │ ├── 361-370: x 6e6f73756666697831 # data[2]: nosuffix1 + │ ├── 370-377: x 6c6f636b6b6579 # data[3]: lockkey + │ ├── 377-389: x 6d7663636c6f676963616c32 # data[4]: mvcclogical2 + │ ├── 389-401: x 6d7663636c6f676963616c31 # data[5]: mvcclogical1 + │ ├── 401-406: x 6d76636334 # data[6]: mvcc4 + │ ├── 406-411: x 6d76636333 # data[7]: mvcc3 + │ ├── 411-416: x 6d76636332 # data[8]: mvcc2 + │ ├── 416-421: x 6d76636331 # data[9]: mvcc1 + │ ├── 421-426: x 6d76636331 # data[10]: mvcc1 + │ ├── 426-429: x 666f6f # data[11]: foo + │ └── 429-430: x 78 # data[12]: x ├── data for column 7 (bool) - │ └── 408-409: x 01 # zero bitmap encoding + │ └── 430-431: x 01 # zero bitmap encoding ├── data for column 8 (bool) - │ └── 409-410: x 01 # zero bitmap encoding - └── 410-411: x 00 # block padding byte + │ └── 431-432: x 01 # zero bitmap encoding + └── 432-433: x 00 # block padding byte