github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/row/fetcher_mvcc_test.go (about) 1 // Copyright 2018 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package row_test 12 13 import ( 14 "context" 15 "reflect" 16 "testing" 17 18 "github.com/cockroachdb/cockroach/pkg/base" 19 "github.com/cockroachdb/cockroach/pkg/keys" 20 "github.com/cockroachdb/cockroach/pkg/kv/kvserver" 21 "github.com/cockroachdb/cockroach/pkg/roachpb" 22 "github.com/cockroachdb/cockroach/pkg/sql/row" 23 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 24 "github.com/cockroachdb/cockroach/pkg/sql/sqlbase" 25 "github.com/cockroachdb/cockroach/pkg/storage" 26 "github.com/cockroachdb/cockroach/pkg/testutils" 27 "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" 28 "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" 29 "github.com/cockroachdb/cockroach/pkg/util" 30 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 31 "github.com/cockroachdb/cockroach/pkg/util/log" 32 "github.com/cockroachdb/errors" 33 ) 34 35 func slurpUserDataKVs(t testing.TB, e storage.Engine) []roachpb.KeyValue { 36 t.Helper() 37 38 // Scan meta keys directly from engine. We put this in a retry loop 39 // because the application of all of a transactions committed writes 40 // is not always synchronous with it committing. 41 var kvs []roachpb.KeyValue 42 testutils.SucceedsSoon(t, func() error { 43 kvs = nil 44 it := e.NewIterator(storage.IterOptions{UpperBound: roachpb.KeyMax}) 45 defer it.Close() 46 for it.SeekGE(storage.MVCCKey{Key: keys.UserTableDataMin}); ; it.NextKey() { 47 ok, err := it.Valid() 48 if err != nil { 49 t.Fatal(err) 50 } 51 if !ok { 52 break 53 } 54 if !it.UnsafeKey().IsValue() { 55 return errors.Errorf("found intent key %v", it.UnsafeKey()) 56 } 57 kvs = append(kvs, roachpb.KeyValue{ 58 Key: it.Key().Key, 59 Value: roachpb.Value{RawBytes: it.Value(), Timestamp: it.UnsafeKey().Timestamp}, 60 }) 61 } 62 return nil 63 }) 64 return kvs 65 } 66 67 func TestRowFetcherMVCCMetadata(t *testing.T) { 68 defer leaktest.AfterTest(t)() 69 70 ctx := context.Background() 71 s, db, kvDB := serverutils.StartServer(t, base.TestServerArgs{}) 72 defer s.Stopper().Stop(ctx) 73 store, _ := s.GetStores().(*kvserver.Stores).GetStore(s.GetFirstStoreID()) 74 sqlDB := sqlutils.MakeSQLRunner(db) 75 76 sqlDB.Exec(t, `CREATE DATABASE d`) 77 sqlDB.Exec(t, `USE d`) 78 sqlDB.Exec(t, `CREATE TABLE parent ( 79 a STRING PRIMARY KEY, b STRING, c STRING, d STRING, 80 FAMILY (a, b, c), FAMILY (d) 81 )`) 82 sqlDB.Exec(t, `CREATE TABLE child ( 83 e STRING, f STRING, PRIMARY KEY (e, f) 84 ) INTERLEAVE IN PARENT parent (e)`) 85 86 parentDesc := sqlbase.GetImmutableTableDescriptor(kvDB, keys.SystemSQLCodec, `d`, `parent`) 87 childDesc := sqlbase.GetImmutableTableDescriptor(kvDB, keys.SystemSQLCodec, `d`, `child`) 88 var args []row.FetcherTableArgs 89 for _, desc := range []*sqlbase.ImmutableTableDescriptor{parentDesc, childDesc} { 90 colIdxMap := make(map[sqlbase.ColumnID]int) 91 var valNeededForCol util.FastIntSet 92 for colIdx := range desc.Columns { 93 id := desc.Columns[colIdx].ID 94 colIdxMap[id] = colIdx 95 valNeededForCol.Add(colIdx) 96 } 97 args = append(args, row.FetcherTableArgs{ 98 Spans: desc.AllIndexSpans(keys.SystemSQLCodec), 99 Desc: desc, 100 Index: &desc.PrimaryIndex, 101 ColIdxMap: colIdxMap, 102 IsSecondaryIndex: false, 103 Cols: desc.Columns, 104 ValNeededForCol: valNeededForCol, 105 }) 106 } 107 var rf row.Fetcher 108 if err := rf.Init( 109 keys.SystemSQLCodec, 110 false, /* reverse */ 111 sqlbase.ScanLockingStrength_FOR_NONE, 112 false, /* returnRangeInfo */ 113 true, /* isCheck */ 114 &sqlbase.DatumAlloc{}, 115 args..., 116 ); err != nil { 117 t.Fatal(err) 118 } 119 type rowWithMVCCMetadata struct { 120 PrimaryKey []string 121 RowIsDeleted bool 122 RowLastModified string 123 } 124 kvsToRows := func(kvs []roachpb.KeyValue) []rowWithMVCCMetadata { 125 t.Helper() 126 for _, kv := range kvs { 127 log.Infof(ctx, "%v %v %v", kv.Key, kv.Value.Timestamp, kv.Value.PrettyPrint()) 128 } 129 130 if err := rf.StartScanFrom(ctx, &row.SpanKVFetcher{KVs: kvs}); err != nil { 131 t.Fatal(err) 132 } 133 var rows []rowWithMVCCMetadata 134 for { 135 datums, _, _, err := rf.NextRowDecoded(ctx) 136 if err != nil { 137 t.Fatal(err) 138 } 139 if datums == nil { 140 break 141 } 142 row := rowWithMVCCMetadata{ 143 RowIsDeleted: rf.RowIsDeleted(), 144 RowLastModified: tree.TimestampToDecimal(rf.RowLastModified()).String(), 145 } 146 for _, datum := range datums { 147 if datum == tree.DNull { 148 row.PrimaryKey = append(row.PrimaryKey, `NULL`) 149 } else { 150 row.PrimaryKey = append(row.PrimaryKey, string(*datum.(*tree.DString))) 151 } 152 } 153 rows = append(rows, row) 154 } 155 return rows 156 } 157 158 var ts1 string 159 sqlDB.QueryRow(t, `BEGIN; 160 INSERT INTO parent VALUES ('1', 'a', 'a', 'a'), ('2', 'b', 'b', 'b'); 161 INSERT INTO child VALUES ('1', '10'), ('2', '20'); 162 SELECT cluster_logical_timestamp(); 163 END;`).Scan(&ts1) 164 165 if actual, expected := kvsToRows(slurpUserDataKVs(t, store.Engine())), []rowWithMVCCMetadata{ 166 {[]string{`1`, `a`, `a`, `a`}, false, ts1}, 167 {[]string{`1`, `10`}, false, ts1}, 168 {[]string{`2`, `b`, `b`, `b`}, false, ts1}, 169 {[]string{`2`, `20`}, false, ts1}, 170 }; !reflect.DeepEqual(expected, actual) { 171 t.Errorf(`expected %v got %v`, expected, actual) 172 } 173 174 var ts2 string 175 sqlDB.QueryRow(t, `BEGIN; 176 UPDATE parent SET b = NULL, c = NULL, d = NULL WHERE a = '1'; 177 UPDATE parent SET d = NULL WHERE a = '2'; 178 UPDATE child SET f = '21' WHERE e = '2'; 179 SELECT cluster_logical_timestamp(); 180 END;`).Scan(&ts2) 181 182 if actual, expected := kvsToRows(slurpUserDataKVs(t, store.Engine())), []rowWithMVCCMetadata{ 183 {[]string{`1`, `NULL`, `NULL`, `NULL`}, false, ts2}, 184 {[]string{`1`, `10`}, false, ts1}, 185 {[]string{`2`, `b`, `b`, `NULL`}, false, ts2}, 186 {[]string{`2`, `20`}, true, ts2}, 187 {[]string{`2`, `21`}, false, ts2}, 188 }; !reflect.DeepEqual(expected, actual) { 189 t.Errorf(`expected %v got %v`, expected, actual) 190 } 191 192 var ts3 string 193 sqlDB.QueryRow(t, `BEGIN; 194 DELETE FROM parent WHERE a = '1'; 195 DELETE FROM child WHERE e = '2'; 196 SELECT cluster_logical_timestamp(); 197 END;`).Scan(&ts3) 198 if actual, expected := kvsToRows(slurpUserDataKVs(t, store.Engine())), []rowWithMVCCMetadata{ 199 {[]string{`1`, `NULL`, `NULL`, `NULL`}, true, ts3}, 200 {[]string{`1`, `10`}, false, ts1}, 201 {[]string{`2`, `b`, `b`, `NULL`}, false, ts2}, 202 {[]string{`2`, `20`}, true, ts2}, // ignore me: artifact of how the test is written 203 {[]string{`2`, `21`}, true, ts3}, 204 }; !reflect.DeepEqual(expected, actual) { 205 t.Errorf(`expected %v got %v`, expected, actual) 206 } 207 }