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  }