github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/rowexec/index_skip_table_reader_test.go (about)

     1  // Copyright 2019 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 rowexec
    12  
    13  import (
    14  	"context"
    15  	"fmt"
    16  	"sort"
    17  	"testing"
    18  
    19  	"github.com/cockroachdb/cockroach/pkg/base"
    20  	"github.com/cockroachdb/cockroach/pkg/keys"
    21  	"github.com/cockroachdb/cockroach/pkg/kv"
    22  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    23  	"github.com/cockroachdb/cockroach/pkg/sql/execinfra"
    24  	"github.com/cockroachdb/cockroach/pkg/sql/execinfrapb"
    25  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    26  	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
    27  	"github.com/cockroachdb/cockroach/pkg/testutils/serverutils"
    28  	"github.com/cockroachdb/cockroach/pkg/testutils/sqlutils"
    29  	"github.com/cockroachdb/cockroach/pkg/util/encoding"
    30  	"github.com/cockroachdb/cockroach/pkg/util/leaktest"
    31  	"github.com/cockroachdb/cockroach/pkg/util/log"
    32  )
    33  
    34  func TestIndexSkipTableReader(t *testing.T) {
    35  	defer leaktest.AfterTest(t)()
    36  	ctx := context.Background()
    37  
    38  	s, sqlDB, kvDB := serverutils.StartServer(t, base.TestServerArgs{})
    39  	defer s.Stopper().Stop(ctx)
    40  
    41  	// create a table t1 where each row is:
    42  	//
    43  	// |     x     |     y     |
    44  	// |-----------------------|
    45  	// | rowId/10  | rowId%10  |
    46  
    47  	xFnt1 := func(row int) tree.Datum {
    48  		return tree.NewDInt(tree.DInt(row / 10))
    49  	}
    50  	yFnt1 := func(row int) tree.Datum {
    51  		return tree.NewDInt(tree.DInt(row % 10))
    52  	}
    53  
    54  	sqlutils.CreateTable(t, sqlDB, "t1",
    55  		"x INT, y INT, PRIMARY KEY (x, y)",
    56  		99,
    57  		sqlutils.ToRowFn(xFnt1, yFnt1),
    58  	)
    59  
    60  	// create a table t2 where each row is:
    61  	//
    62  	// |     x     |     y         |     z     |
    63  	// |---------------------------------------|
    64  	// | rowId / 3 | rowId / 3 + 1 |  rowId    |
    65  
    66  	xFnt2 := func(row int) tree.Datum {
    67  		return tree.NewDInt(tree.DInt(row / 3))
    68  	}
    69  	yFnt2 := func(row int) tree.Datum {
    70  		return tree.NewDInt(tree.DInt(row/3 + 1))
    71  	}
    72  	zFnt2 := func(row int) tree.Datum {
    73  		return tree.NewDInt(tree.DInt(row))
    74  	}
    75  	sqlutils.CreateTable(t, sqlDB, "t2",
    76  		"x INT, y INT, z INT, PRIMARY KEY (x, y, z)",
    77  		9,
    78  		sqlutils.ToRowFn(xFnt2, yFnt2, zFnt2),
    79  	)
    80  
    81  	// create a table t3 where each row is:
    82  	//
    83  	// |     x     |     y      |                      z                   |
    84  	// |-------------------------------------------------------------------|
    85  	// | rowId / 3 | rowId % 3  |  if rowId % 2 == 0 then NULL else rowID  |
    86  
    87  	xFnt3 := func(row int) tree.Datum {
    88  		return tree.NewDInt(tree.DInt(row / 3))
    89  	}
    90  	yFnt3 := func(row int) tree.Datum {
    91  		return tree.NewDInt(tree.DInt(row % 3))
    92  	}
    93  	zFnt3 := func(row int) tree.Datum {
    94  		if row%2 == 0 {
    95  			return tree.DNull
    96  		}
    97  		return tree.NewDInt(tree.DInt(row))
    98  	}
    99  	sqlutils.CreateTable(t, sqlDB, "t3",
   100  		"x INT, y INT, z INT, PRIMARY KEY (x, y)",
   101  		9,
   102  		sqlutils.ToRowFn(xFnt3, yFnt3, zFnt3),
   103  	)
   104  
   105  	// create a table t4 where each row is:
   106  	//
   107  	// |     x     |        y      |
   108  	// |---------------------------|
   109  	// | rowId/10  | rowId%10 + 1  |
   110  
   111  	xFnt4 := func(row int) tree.Datum {
   112  		return tree.NewDInt(tree.DInt(row / 10))
   113  	}
   114  	yFnt4 := func(row int) tree.Datum {
   115  		return tree.NewDInt(tree.DInt(row%10 + 1))
   116  	}
   117  
   118  	sqlutils.CreateTable(t, sqlDB, "t4",
   119  		"x INT, y INT, PRIMARY KEY (x, y)",
   120  		99,
   121  		sqlutils.ToRowFn(xFnt4, yFnt4),
   122  	)
   123  	// create a secondary index on (y, x) on t4
   124  	runner := sqlutils.MakeSQLRunner(sqlDB)
   125  	runner.Exec(t, "CREATE INDEX t4_test_index ON test.t4 (y, x)")
   126  
   127  	// create some interleaved tables
   128  	sqlutils.CreateTable(t, sqlDB, "t5",
   129  		"x INT PRIMARY KEY",
   130  		10,
   131  		sqlutils.ToRowFn(yFnt1))
   132  
   133  	xFnt6 := func(row int) tree.Datum {
   134  		return tree.NewDInt(tree.DInt(row/10) + 1)
   135  	}
   136  	yFnt6 := func(row int) tree.Datum {
   137  		return tree.NewDInt(tree.DInt(row%10) + 1)
   138  	}
   139  	// interleave a table now
   140  	sqlutils.CreateTableInterleaved(t, sqlDB, "t6",
   141  		"x INT, y INT, PRIMARY KEY(x, y)",
   142  		"t5 (x)",
   143  		99,
   144  		sqlutils.ToRowFn(xFnt6, yFnt6))
   145  
   146  	// create a table t7 where each row is:
   147  	//
   148  	// |     x     |   y   |  z  |
   149  	// |-------------------------|
   150  	// | rowId%10  | NULL | NULL|
   151  	xFnt7 := func(row int) tree.Datum {
   152  		return tree.NewDInt(tree.DInt(row % 10))
   153  	}
   154  	nullt7 := func(_ int) tree.Datum {
   155  		return tree.DNull
   156  	}
   157  	sqlutils.CreateTable(t, sqlDB, "t7",
   158  		"x INT, y INT, z INT, PRIMARY KEY (x), INDEX i1 (x, y DESC, z DESC), INDEX i2 (y DESC, z DESC)",
   159  		10,
   160  		sqlutils.ToRowFn(xFnt7, nullt7, nullt7))
   161  
   162  	td1 := sqlbase.GetTableDescriptor(kvDB, keys.SystemSQLCodec, "test", "t1")
   163  	td2 := sqlbase.GetTableDescriptor(kvDB, keys.SystemSQLCodec, "test", "t2")
   164  	td3 := sqlbase.GetTableDescriptor(kvDB, keys.SystemSQLCodec, "test", "t3")
   165  	td4 := sqlbase.GetTableDescriptor(kvDB, keys.SystemSQLCodec, "test", "t4")
   166  	td5 := sqlbase.GetTableDescriptor(kvDB, keys.SystemSQLCodec, "test", "t5")
   167  	td6 := sqlbase.GetTableDescriptor(kvDB, keys.SystemSQLCodec, "test", "t6")
   168  	td7 := sqlbase.GetTableDescriptor(kvDB, keys.SystemSQLCodec, "test", "t7")
   169  
   170  	makeIndexSpan := func(td *sqlbase.TableDescriptor, start, end int) execinfrapb.TableReaderSpan {
   171  		var span roachpb.Span
   172  		prefix := roachpb.Key(sqlbase.MakeIndexKeyPrefix(keys.SystemSQLCodec, td, td.PrimaryIndex.ID))
   173  		span.Key = append(prefix, encoding.EncodeVarintAscending(nil, int64(start))...)
   174  		span.EndKey = append(span.EndKey, prefix...)
   175  		span.EndKey = append(span.EndKey, encoding.EncodeVarintAscending(nil, int64(end))...)
   176  		return execinfrapb.TableReaderSpan{Span: span}
   177  	}
   178  
   179  	testCases := []struct {
   180  		desc      string
   181  		tableDesc *sqlbase.TableDescriptor
   182  		spec      execinfrapb.IndexSkipTableReaderSpec
   183  		post      execinfrapb.PostProcessSpec
   184  		expected  string
   185  	}{
   186  		{
   187  			// Distinct scan simple.
   188  			desc:      "SimpleForward",
   189  			tableDesc: td1,
   190  			spec: execinfrapb.IndexSkipTableReaderSpec{
   191  				Spans: []execinfrapb.TableReaderSpan{{Span: td1.PrimaryIndexSpan(keys.SystemSQLCodec)}},
   192  			},
   193  			post: execinfrapb.PostProcessSpec{
   194  				Projection:    true,
   195  				OutputColumns: []uint32{0},
   196  			},
   197  			expected: "[[0] [1] [2] [3] [4] [5] [6] [7] [8] [9]]",
   198  		},
   199  		{
   200  			// Distinct scan on interleaved table parent.
   201  			desc:      "InterleavedParent",
   202  			tableDesc: td5,
   203  			spec: execinfrapb.IndexSkipTableReaderSpec{
   204  				Spans: []execinfrapb.TableReaderSpan{{Span: td5.PrimaryIndexSpan(keys.SystemSQLCodec)}},
   205  			},
   206  			post: execinfrapb.PostProcessSpec{
   207  				Projection:    true,
   208  				OutputColumns: []uint32{0},
   209  			},
   210  			expected: "[[0] [1] [2] [3] [4] [5] [6] [7] [8] [9]]",
   211  		},
   212  		{
   213  			// Distinct scan on interleaved table child.
   214  			desc:      "InterleavedChild",
   215  			tableDesc: td6,
   216  			spec: execinfrapb.IndexSkipTableReaderSpec{
   217  				Spans: []execinfrapb.TableReaderSpan{{Span: td6.PrimaryIndexSpan(keys.SystemSQLCodec)}},
   218  			},
   219  			post: execinfrapb.PostProcessSpec{
   220  				Projection:    true,
   221  				OutputColumns: []uint32{0},
   222  			},
   223  			expected: "[[1] [2] [3] [4] [5] [6] [7] [8] [9] [10]]",
   224  		},
   225  		{
   226  			// Distinct scan with multiple spans.
   227  			desc:      "MultipleSpans",
   228  			tableDesc: td1,
   229  			spec: execinfrapb.IndexSkipTableReaderSpec{
   230  				Spans: []execinfrapb.TableReaderSpan{makeIndexSpan(td1, 0, 3), makeIndexSpan(td1, 5, 8)},
   231  			},
   232  			post: execinfrapb.PostProcessSpec{
   233  				Projection:    true,
   234  				OutputColumns: []uint32{0},
   235  			},
   236  			expected: "[[0] [1] [2] [5] [6] [7]]",
   237  		},
   238  		{
   239  			// Distinct scan with multiple spans and filter,
   240  			desc:      "MultipleSpansWithFilter",
   241  			tableDesc: td1,
   242  			spec: execinfrapb.IndexSkipTableReaderSpec{
   243  				Spans: []execinfrapb.TableReaderSpan{makeIndexSpan(td1, 0, 3), makeIndexSpan(td1, 5, 8)},
   244  			},
   245  			post: execinfrapb.PostProcessSpec{
   246  				Filter:        execinfrapb.Expression{Expr: "@1 > 3 AND @1 < 7"},
   247  				Projection:    true,
   248  				OutputColumns: []uint32{0},
   249  			},
   250  			expected: "[[5] [6]]",
   251  		},
   252  		{
   253  			// Distinct scan with filter.
   254  			desc:      "Filter",
   255  			tableDesc: td1,
   256  			spec: execinfrapb.IndexSkipTableReaderSpec{
   257  				Spans: []execinfrapb.TableReaderSpan{{Span: td1.PrimaryIndexSpan(keys.SystemSQLCodec)}},
   258  			},
   259  			post: execinfrapb.PostProcessSpec{
   260  				Filter:        execinfrapb.Expression{Expr: "@1 > 3 AND @1 < 7"},
   261  				Projection:    true,
   262  				OutputColumns: []uint32{0},
   263  			},
   264  			expected: "[[4] [5] [6]]",
   265  		},
   266  		{
   267  			// Distinct scan with multiple requested columns.
   268  			desc:      "MultipleOutputCols",
   269  			tableDesc: td2,
   270  			spec: execinfrapb.IndexSkipTableReaderSpec{
   271  				Spans: []execinfrapb.TableReaderSpan{{Span: td2.PrimaryIndexSpan(keys.SystemSQLCodec)}},
   272  			},
   273  			post: execinfrapb.PostProcessSpec{
   274  				Projection:    true,
   275  				OutputColumns: []uint32{0, 1},
   276  			},
   277  			expected: "[[0 1] [1 2] [2 3] [3 4]]",
   278  		},
   279  		{
   280  			// Distinct scan on table with NULLs.
   281  			desc:      "Nulls",
   282  			tableDesc: td3,
   283  			spec: execinfrapb.IndexSkipTableReaderSpec{
   284  				Spans: []execinfrapb.TableReaderSpan{{Span: td3.PrimaryIndexSpan(keys.SystemSQLCodec)}},
   285  			},
   286  			post: execinfrapb.PostProcessSpec{
   287  				Projection:    true,
   288  				OutputColumns: []uint32{0},
   289  			},
   290  			expected: "[[0] [1] [2] [3]]",
   291  		},
   292  		{
   293  			// Distinct scan on secondary index",
   294  			desc:      "SecondaryIdx",
   295  			tableDesc: td4,
   296  			spec: execinfrapb.IndexSkipTableReaderSpec{
   297  				Spans:    []execinfrapb.TableReaderSpan{{Span: td4.IndexSpan(keys.SystemSQLCodec, 2)}},
   298  				IndexIdx: 1,
   299  			},
   300  			post: execinfrapb.PostProcessSpec{
   301  				Projection:    true,
   302  				OutputColumns: []uint32{1},
   303  			},
   304  			expected: "[[1] [2] [3] [4] [5] [6] [7] [8] [9] [10]]",
   305  		},
   306  		{
   307  			// Distinct reverse scan simple.
   308  			desc:      "SimpleReverse",
   309  			tableDesc: td1,
   310  			spec: execinfrapb.IndexSkipTableReaderSpec{
   311  				Spans:   []execinfrapb.TableReaderSpan{{Span: td1.PrimaryIndexSpan(keys.SystemSQLCodec)}},
   312  				Reverse: true,
   313  			},
   314  			post: execinfrapb.PostProcessSpec{
   315  				Projection:    true,
   316  				OutputColumns: []uint32{0},
   317  			},
   318  			expected: "[[9] [8] [7] [6] [5] [4] [3] [2] [1] [0]]",
   319  		},
   320  		{
   321  			// Distinct reverse scan with multiple spans.
   322  			desc:      "MultipleSpansReverse",
   323  			tableDesc: td1,
   324  			spec: execinfrapb.IndexSkipTableReaderSpec{
   325  				Spans:   []execinfrapb.TableReaderSpan{makeIndexSpan(td1, 0, 3), makeIndexSpan(td1, 5, 8)},
   326  				Reverse: true,
   327  			},
   328  			post: execinfrapb.PostProcessSpec{
   329  				Projection:    true,
   330  				OutputColumns: []uint32{0},
   331  			},
   332  			expected: "[[7] [6] [5] [2] [1] [0]]",
   333  		},
   334  		{
   335  			// Distinct reverse scan with multiple spans and filter.
   336  			desc:      "MultipleSpansWithFilterReverse",
   337  			tableDesc: td1,
   338  			spec: execinfrapb.IndexSkipTableReaderSpec{
   339  				Spans:   []execinfrapb.TableReaderSpan{makeIndexSpan(td1, 0, 3), makeIndexSpan(td1, 5, 8)},
   340  				Reverse: true,
   341  			},
   342  			post: execinfrapb.PostProcessSpec{
   343  				Filter:        execinfrapb.Expression{Expr: "@1 > 3 AND @1 < 7"},
   344  				Projection:    true,
   345  				OutputColumns: []uint32{0},
   346  			},
   347  			expected: "[[6] [5]]",
   348  		},
   349  		{
   350  			// Distinct reverse scan on interleaved parent.
   351  			desc:      "InterleavedParentReverse",
   352  			tableDesc: td5,
   353  			spec: execinfrapb.IndexSkipTableReaderSpec{
   354  				Spans:   []execinfrapb.TableReaderSpan{{Span: td5.PrimaryIndexSpan(keys.SystemSQLCodec)}},
   355  				Reverse: true,
   356  			},
   357  			post: execinfrapb.PostProcessSpec{
   358  				Projection:    true,
   359  				OutputColumns: []uint32{0},
   360  			},
   361  			expected: "[[9] [8] [7] [6] [5] [4] [3] [2] [1] [0]]",
   362  		},
   363  		{
   364  			// Distinct reverse scan with multiple spans on interleaved parent.
   365  			desc:      "InterleavedParentMultipleSpansReverse",
   366  			tableDesc: td5,
   367  			spec: execinfrapb.IndexSkipTableReaderSpec{
   368  				Spans:   []execinfrapb.TableReaderSpan{makeIndexSpan(td5, 0, 3), makeIndexSpan(td5, 5, 8)},
   369  				Reverse: true,
   370  			},
   371  			post: execinfrapb.PostProcessSpec{
   372  				Projection:    true,
   373  				OutputColumns: []uint32{0},
   374  			},
   375  			expected: "[[7] [6] [5] [2] [1] [0]]",
   376  		},
   377  		{
   378  			// Distinct reverse scan on interleaved child.
   379  			desc:      "InterleavedChildReverse",
   380  			tableDesc: td6,
   381  			spec: execinfrapb.IndexSkipTableReaderSpec{
   382  				Spans:   []execinfrapb.TableReaderSpan{{Span: td6.PrimaryIndexSpan(keys.SystemSQLCodec)}},
   383  				Reverse: true,
   384  			},
   385  			post: execinfrapb.PostProcessSpec{
   386  				Projection:    true,
   387  				OutputColumns: []uint32{0},
   388  			},
   389  			expected: "[[10] [9] [8] [7] [6] [5] [4] [3] [2] [1]]",
   390  		},
   391  		{
   392  			// Distinct scan on index with multiple null values
   393  			desc:      "IndexMultipleNulls",
   394  			tableDesc: td7,
   395  			spec: execinfrapb.IndexSkipTableReaderSpec{
   396  				Spans:    []execinfrapb.TableReaderSpan{{Span: td7.IndexSpan(keys.SystemSQLCodec, 2)}},
   397  				IndexIdx: 1,
   398  			},
   399  			post: execinfrapb.PostProcessSpec{
   400  				Projection:    true,
   401  				OutputColumns: []uint32{0},
   402  			},
   403  			expected: "[[0] [1] [2] [3] [4] [5] [6] [7] [8] [9]]",
   404  		},
   405  		{
   406  			// Distinct scan on index with only null values
   407  			desc:      "IndexAllNulls",
   408  			tableDesc: td7,
   409  			spec: execinfrapb.IndexSkipTableReaderSpec{
   410  				Spans:    []execinfrapb.TableReaderSpan{{Span: td7.IndexSpan(keys.SystemSQLCodec, 3)}},
   411  				IndexIdx: 2,
   412  			},
   413  			post: execinfrapb.PostProcessSpec{
   414  				Projection:    true,
   415  				OutputColumns: []uint32{1},
   416  			},
   417  			expected: "[[NULL]]",
   418  		},
   419  	}
   420  
   421  	for _, c := range testCases {
   422  		t.Run(c.desc, func(t *testing.T) {
   423  			ts := c.spec
   424  			ts.Table = *c.tableDesc
   425  
   426  			evalCtx := tree.MakeTestingEvalContext(s.ClusterSettings())
   427  			defer evalCtx.Stop(ctx)
   428  			flowCtx := execinfra.FlowCtx{
   429  				EvalCtx: &evalCtx,
   430  				Cfg:     &execinfra.ServerConfig{Settings: s.ClusterSettings()},
   431  				Txn:     kv.NewTxn(ctx, s.DB(), s.NodeID()),
   432  				NodeID:  evalCtx.NodeID,
   433  			}
   434  
   435  			tr, err := newIndexSkipTableReader(&flowCtx, 0 /* processorID */, &ts, &c.post, nil)
   436  
   437  			if err != nil {
   438  				t.Fatal(err)
   439  			}
   440  
   441  			var results execinfra.RowSource
   442  			tr.Start(ctx)
   443  			results = tr
   444  
   445  			var res sqlbase.EncDatumRows
   446  			for {
   447  				row, meta := results.Next()
   448  				if meta != nil && meta.LeafTxnFinalState == nil {
   449  					t.Fatalf("unexpected metadata: %+v", meta)
   450  				}
   451  				if row == nil {
   452  					break
   453  				}
   454  				res = append(res, row.Copy())
   455  			}
   456  			if result := res.String(tr.OutputTypes()); result != c.expected {
   457  				t.Errorf("invalid results: %s, expected %s'", result, c.expected)
   458  			}
   459  
   460  		})
   461  	}
   462  
   463  }
   464  
   465  func TestIndexSkipTableReaderMisplannedRangesMetadata(t *testing.T) {
   466  	defer leaktest.AfterTest(t)()
   467  	ctx := context.Background()
   468  	tc := serverutils.StartTestCluster(t, 3,
   469  		base.TestClusterArgs{
   470  			ReplicationMode: base.ReplicationManual,
   471  			ServerArgs: base.TestServerArgs{
   472  				UseDatabase: "test",
   473  			},
   474  		})
   475  	defer tc.Stopper().Stop(ctx)
   476  
   477  	db := tc.ServerConn(0)
   478  	sqlutils.CreateTable(t, db, "t",
   479  		"num INT PRIMARY KEY",
   480  		3,
   481  		sqlutils.ToRowFn(sqlutils.RowIdxFn),
   482  	)
   483  
   484  	_, err := db.Exec(`
   485  ALTER TABLE t SPLIT AT VALUES (1), (2), (3);
   486  ALTER TABLE t EXPERIMENTAL_RELOCATE VALUES (ARRAY[2], 1), (ARRAY[1], 2), (ARRAY[3], 3);
   487  `)
   488  	if err != nil {
   489  		t.Fatal(err)
   490  	}
   491  
   492  	kvDB := tc.Server(0).DB()
   493  	td := sqlbase.GetTableDescriptor(kvDB, keys.SystemSQLCodec, "test", "t")
   494  
   495  	st := tc.Server(0).ClusterSettings()
   496  	evalCtx := tree.MakeTestingEvalContext(st)
   497  	defer evalCtx.Stop(ctx)
   498  	nodeID := tc.Server(0).NodeID()
   499  	flowCtx := execinfra.FlowCtx{
   500  		EvalCtx: &evalCtx,
   501  		Cfg:     &execinfra.ServerConfig{Settings: st},
   502  		Txn:     kv.NewTxn(ctx, tc.Server(0).DB(), nodeID),
   503  		NodeID:  evalCtx.NodeID,
   504  	}
   505  	spec := execinfrapb.IndexSkipTableReaderSpec{
   506  		Spans: []execinfrapb.TableReaderSpan{{Span: td.PrimaryIndexSpan(keys.SystemSQLCodec)}},
   507  		Table: *td,
   508  	}
   509  	post := execinfrapb.PostProcessSpec{
   510  		Projection:    true,
   511  		OutputColumns: []uint32{0},
   512  	}
   513  
   514  	t.Run("", func(t *testing.T) {
   515  		tr, err := newIndexSkipTableReader(&flowCtx, 0, &spec, &post, nil)
   516  		if err != nil {
   517  			t.Fatal(err)
   518  		}
   519  		tr.Start(ctx)
   520  		var res sqlbase.EncDatumRows
   521  		var metas []*execinfrapb.ProducerMetadata
   522  		for {
   523  			row, meta := tr.Next()
   524  			if meta != nil {
   525  				metas = append(metas, meta)
   526  				continue
   527  			}
   528  			if row == nil {
   529  				break
   530  			}
   531  			res = append(res, row)
   532  		}
   533  		if len(res) != 3 {
   534  			t.Fatalf("expected 3 rows, got: %d", len(res))
   535  		}
   536  		var misplannedRanges []roachpb.RangeInfo
   537  		for _, m := range metas {
   538  			if len(m.Ranges) > 0 {
   539  				misplannedRanges = m.Ranges
   540  			} else if m.LeafTxnFinalState == nil {
   541  				t.Fatalf("expected only txn coord meta or misplanned ranges, got: %+v", metas)
   542  			}
   543  		}
   544  
   545  		if len(misplannedRanges) != 2 {
   546  			t.Fatalf("expected 2 misplanned ranges, got: %+v", misplannedRanges)
   547  		}
   548  
   549  		// The metadata about misplanned ranges can come in any order (it depends on
   550  		// the order in which parallel sub-batches complete after having been split by
   551  		// DistSender).
   552  		sort.Slice(misplannedRanges, func(i, j int) bool {
   553  			return misplannedRanges[i].Lease.Replica.NodeID < misplannedRanges[j].Lease.Replica.NodeID
   554  		})
   555  		if misplannedRanges[0].Lease.Replica.NodeID != 2 ||
   556  			misplannedRanges[1].Lease.Replica.NodeID != 3 {
   557  			t.Fatalf("expected misplanned ranges from nodes 2 and 3, got: %+v", metas[0])
   558  		}
   559  	})
   560  }
   561  
   562  func BenchmarkIndexScanTableReader(b *testing.B) {
   563  	defer leaktest.AfterTest(b)()
   564  
   565  	logScope := log.Scope(b)
   566  	defer logScope.Close(b)
   567  
   568  	ctx := context.Background()
   569  
   570  	s, sqlDB, kvDB := serverutils.StartServer(b, base.TestServerArgs{})
   571  	defer s.Stopper().Stop(ctx)
   572  
   573  	evalCtx := tree.MakeTestingEvalContext(s.ClusterSettings())
   574  	defer evalCtx.Stop(ctx)
   575  
   576  	// test for number of rows in the table
   577  	for _, numRows := range []int{1 << 4, 1 << 8, 1 << 12, 1 << 16, 1 << 18} {
   578  		// test for a ratio of values from 1 unique value of the first column
   579  		// of the primary key to x values of the second column
   580  		for _, valueRatio := range []int{1, 100, 500, 1000, 5000, 10000} {
   581  			if valueRatio > numRows {
   582  				continue
   583  			}
   584  			tableName := fmt.Sprintf("t_%d_%d", numRows, valueRatio)
   585  			xFn := func(row int) tree.Datum {
   586  				return tree.NewDInt(tree.DInt(row / valueRatio))
   587  			}
   588  			yFn := func(row int) tree.Datum {
   589  				return tree.NewDInt(tree.DInt(row % valueRatio))
   590  			}
   591  
   592  			sqlutils.CreateTable(
   593  				b, sqlDB, tableName,
   594  				"x INT, y INT, PRIMARY KEY (x, y)",
   595  				numRows,
   596  				sqlutils.ToRowFn(xFn, yFn))
   597  
   598  			expectedCount := (numRows / valueRatio)
   599  			if valueRatio != 1 {
   600  				expectedCount++
   601  			}
   602  
   603  			tableDesc := sqlbase.GetTableDescriptor(kvDB, keys.SystemSQLCodec, "test", tableName)
   604  
   605  			runner := func(reader execinfra.RowSource, b *testing.B) {
   606  				reader.Start(ctx)
   607  				count := 0
   608  				for {
   609  					row, meta := reader.Next()
   610  					if meta != nil && meta.LeafTxnFinalState == nil && meta.Metrics == nil {
   611  						b.Fatalf("unexpected metadata: %+v", meta)
   612  					}
   613  					if row != nil {
   614  						count++
   615  					} else if meta == nil {
   616  						break
   617  					}
   618  				}
   619  				if count != expectedCount {
   620  					b.Fatalf("found %d rows, expected %d", count, expectedCount)
   621  				}
   622  			}
   623  
   624  			flowCtxTableReader := execinfra.FlowCtx{
   625  				EvalCtx: &evalCtx,
   626  				Cfg:     &execinfra.ServerConfig{Settings: s.ClusterSettings()},
   627  				Txn:     kv.NewTxn(ctx, s.DB(), s.NodeID()),
   628  				NodeID:  evalCtx.NodeID,
   629  			}
   630  
   631  			b.Run(fmt.Sprintf("TableReader+Distinct-rows=%d-ratio=%d", numRows, valueRatio), func(b *testing.B) {
   632  				spec := execinfrapb.TableReaderSpec{
   633  					Table: *tableDesc,
   634  					Spans: []execinfrapb.TableReaderSpan{{Span: tableDesc.PrimaryIndexSpan(keys.SystemSQLCodec)}},
   635  				}
   636  				post := execinfrapb.PostProcessSpec{
   637  					Projection:    true,
   638  					OutputColumns: []uint32{0},
   639  				}
   640  
   641  				specDistinct := execinfrapb.DistinctSpec{
   642  					OrderedColumns:  []uint32{0},
   643  					DistinctColumns: []uint32{0},
   644  				}
   645  				postDistinct := execinfrapb.PostProcessSpec{}
   646  
   647  				b.ResetTimer()
   648  				for i := 0; i < b.N; i++ {
   649  					tr, err := newTableReader(&flowCtxTableReader, 0, &spec, &post, nil)
   650  					if err != nil {
   651  						b.Fatal(err)
   652  					}
   653  					dist, err := newDistinct(&flowCtxTableReader, 0, &specDistinct, tr, &postDistinct, nil)
   654  					if err != nil {
   655  						b.Fatal(err)
   656  					}
   657  					runner(dist, b)
   658  				}
   659  			})
   660  
   661  			flowCtxIndexSkipTableReader := execinfra.FlowCtx{
   662  				EvalCtx: &evalCtx,
   663  				Cfg:     &execinfra.ServerConfig{Settings: s.ClusterSettings()},
   664  				Txn:     kv.NewTxn(ctx, s.DB(), s.NodeID()),
   665  				NodeID:  evalCtx.NodeID,
   666  			}
   667  
   668  			// run the index skip table reader
   669  			b.Run(fmt.Sprintf("IndexSkipTableReader-rows=%d-ratio=%d", numRows, valueRatio), func(b *testing.B) {
   670  				spec := execinfrapb.IndexSkipTableReaderSpec{
   671  					Table: *tableDesc,
   672  					Spans: []execinfrapb.TableReaderSpan{{Span: tableDesc.PrimaryIndexSpan(keys.SystemSQLCodec)}},
   673  				}
   674  				post := execinfrapb.PostProcessSpec{
   675  					OutputColumns: []uint32{0},
   676  					Projection:    true,
   677  				}
   678  				b.ResetTimer()
   679  				for i := 0; i < b.N; i++ {
   680  					it, err := newIndexSkipTableReader(&flowCtxIndexSkipTableReader, 0, &spec, &post, nil)
   681  					if err != nil {
   682  						b.Fatal(err)
   683  					}
   684  					runner(it, b)
   685  				}
   686  			})
   687  		}
   688  	}
   689  }