github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/rowexec/zigzagjoiner_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 rowexec
    12  
    13  import (
    14  	"context"
    15  	"testing"
    16  
    17  	"github.com/cockroachdb/cockroach/pkg/base"
    18  	"github.com/cockroachdb/cockroach/pkg/keys"
    19  	"github.com/cockroachdb/cockroach/pkg/kv"
    20  	"github.com/cockroachdb/cockroach/pkg/settings/cluster"
    21  	"github.com/cockroachdb/cockroach/pkg/sql/execinfra"
    22  	"github.com/cockroachdb/cockroach/pkg/sql/execinfrapb"
    23  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    24  	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
    25  	"github.com/cockroachdb/cockroach/pkg/sql/types"
    26  	"github.com/cockroachdb/cockroach/pkg/testutils/distsqlutils"
    27  	"github.com/cockroachdb/cockroach/pkg/testutils/serverutils"
    28  	"github.com/cockroachdb/cockroach/pkg/testutils/sqlutils"
    29  	"github.com/cockroachdb/cockroach/pkg/util/leaktest"
    30  	"github.com/stretchr/testify/require"
    31  )
    32  
    33  type zigzagJoinerTestCase struct {
    34  	desc          string
    35  	spec          execinfrapb.ZigzagJoinerSpec
    36  	outCols       []uint32
    37  	fixedValues   []sqlbase.EncDatumRow
    38  	expectedTypes []*types.T
    39  	expected      string
    40  }
    41  
    42  func intCols(numCols int) []*types.T {
    43  	cols := make([]*types.T, numCols)
    44  	for i := range cols {
    45  		cols[i] = types.Int
    46  	}
    47  	return cols
    48  }
    49  
    50  func encInt(i int) sqlbase.EncDatum {
    51  	return sqlbase.DatumToEncDatum(types.Int, tree.NewDInt(tree.DInt(i)))
    52  }
    53  
    54  func TestZigzagJoiner(t *testing.T) {
    55  	defer leaktest.AfterTest(t)()
    56  	ctx := context.Background()
    57  
    58  	s, sqlDB, kvDB := serverutils.StartServer(t, base.TestServerArgs{})
    59  	defer s.Stopper().Stop(ctx)
    60  
    61  	null := tree.DNull
    62  
    63  	identity := func(row int) tree.Datum {
    64  		return tree.NewDInt(tree.DInt(row))
    65  	}
    66  	aFn := func(row int) tree.Datum {
    67  		return tree.NewDInt(tree.DInt(row / 5))
    68  	}
    69  	bFn := func(row int) tree.Datum {
    70  		return tree.NewDInt(tree.DInt(row % 5))
    71  	}
    72  	cFn := func(row int) tree.Datum {
    73  		return tree.NewDInt(tree.DInt(row%3 + 3))
    74  	}
    75  	dFn := func(row int) tree.Datum {
    76  		return tree.NewDInt(tree.DInt((row+1)%4 + 4))
    77  	}
    78  	eFn := func(row int) tree.Datum {
    79  		if row%5 == 0 {
    80  			return null
    81  		}
    82  		return tree.NewDInt(tree.DInt((row+1)%4 + 4))
    83  	}
    84  
    85  	offsetFn := func(oldFunc func(int) tree.Datum, offset int) func(int) tree.Datum {
    86  		offsetFunc := func(row int) tree.Datum {
    87  			return oldFunc(row + offset)
    88  		}
    89  		return offsetFunc
    90  	}
    91  
    92  	sqlutils.CreateTableDebug(t, sqlDB, "empty",
    93  		"a INT, b INT, x INT, c INT, d INT, PRIMARY KEY (a,b), INDEX c (c), INDEX d (d)",
    94  		0,
    95  		sqlutils.ToRowFn(aFn, bFn, cFn, dFn),
    96  		true, /* shouldPrint */
    97  	)
    98  
    99  	// Drop a column to test https://github.com/cockroachdb/cockroach/issues/37196
   100  	_, err := sqlDB.Exec("ALTER TABLE test.empty DROP COLUMN x")
   101  	require.NoError(t, err)
   102  
   103  	// Drop and add an index to test
   104  	// https://github.com/cockroachdb/cockroach/issues/42164.
   105  	_, err = sqlDB.Exec("DROP INDEX test.empty@d")
   106  	require.NoError(t, err)
   107  	_, err = sqlDB.Exec("CREATE INDEX d ON test.empty(d)")
   108  	require.NoError(t, err)
   109  
   110  	sqlutils.CreateTableDebug(t, sqlDB, "single",
   111  		"a INT, b INT, c INT, d INT, PRIMARY KEY (a,b), INDEX c (c), INDEX d (d)",
   112  		1,
   113  		sqlutils.ToRowFn(aFn, bFn, cFn, dFn),
   114  		true, /* shouldPrint */
   115  	)
   116  
   117  	sqlutils.CreateTableDebug(t, sqlDB, "small",
   118  		"a INT, b INT, c INT, d INT, PRIMARY KEY (a,b), INDEX c (c), INDEX d (d)",
   119  		10,
   120  		sqlutils.ToRowFn(aFn, bFn, cFn, dFn),
   121  		true, /* shouldPrint */
   122  	)
   123  
   124  	sqlutils.CreateTableDebug(t, sqlDB, "med",
   125  		"a INT, b INT, c INT, d INT, PRIMARY KEY (a,b), INDEX c (c), INDEX d (d)",
   126  		22,
   127  		sqlutils.ToRowFn(aFn, bFn, cFn, dFn),
   128  		true, /* shouldPrint */
   129  	)
   130  
   131  	sqlutils.CreateTableDebug(t, sqlDB, "overlapping",
   132  		"a INT, b INT, c INT, d INT, PRIMARY KEY (a, b), INDEX ac (a, c), INDEX d (d)",
   133  		22,
   134  		sqlutils.ToRowFn(aFn, bFn, cFn, dFn),
   135  		true, /* shouldPrint */
   136  	)
   137  
   138  	sqlutils.CreateTableDebug(t, sqlDB, "comp",
   139  		"a INT, b INT, c INT, d INT, PRIMARY KEY (a, b), INDEX cab (c, a, b), INDEX d (d)",
   140  		22,
   141  		sqlutils.ToRowFn(aFn, bFn, cFn, dFn),
   142  		true, /* shouldPrint */
   143  	)
   144  
   145  	sqlutils.CreateTableDebug(t, sqlDB, "rev",
   146  		"a INT, b INT, c INT, d INT, PRIMARY KEY (a, b), INDEX cba (c, b, a), INDEX d (d)",
   147  		22,
   148  		sqlutils.ToRowFn(aFn, bFn, cFn, dFn),
   149  		true, /* shouldPrint */
   150  	)
   151  
   152  	offset := 44
   153  	sqlutils.CreateTableDebug(t, sqlDB, "offset",
   154  		"a INT, b INT, c INT, d INT, PRIMARY KEY (a,b), INDEX c (c), INDEX d (d)",
   155  		20,
   156  		sqlutils.ToRowFn(offsetFn(aFn, offset), offsetFn(bFn, offset), offsetFn(cFn, offset), offsetFn(dFn, offset)),
   157  		true, /* shouldPrint */
   158  	)
   159  
   160  	unqOffset := 20
   161  	sqlutils.CreateTableDebug(t, sqlDB, "unq",
   162  		"a INT, b INT, c INT UNIQUE, d INT, PRIMARY KEY (a, b), INDEX cb (c, b), INDEX d (d)",
   163  		20,
   164  		sqlutils.ToRowFn(
   165  			offsetFn(aFn, unqOffset),
   166  			offsetFn(bFn, unqOffset),
   167  			offsetFn(identity, unqOffset),
   168  			offsetFn(dFn, unqOffset),
   169  		),
   170  		true, /* shouldPrint */
   171  	)
   172  
   173  	sqlutils.CreateTableDebug(t, sqlDB, "t2",
   174  		"b INT, a INT, PRIMARY KEY (b, a)",
   175  		10,
   176  		sqlutils.ToRowFn(bFn, aFn),
   177  		true, /* shouldPrint */
   178  	)
   179  
   180  	sqlutils.CreateTableDebug(t, sqlDB, "nullable",
   181  		"a INT, b INT, e INT, d INT, PRIMARY KEY (a, b), INDEX e (e), INDEX d (d)",
   182  		10,
   183  		sqlutils.ToRowFn(aFn, bFn, eFn, dFn),
   184  		true, /* shouldPrint */
   185  	)
   186  
   187  	empty := sqlbase.GetTableDescriptor(kvDB, keys.SystemSQLCodec, "test", "empty")
   188  	single := sqlbase.GetTableDescriptor(kvDB, keys.SystemSQLCodec, "test", "single")
   189  	smallDesc := sqlbase.GetTableDescriptor(kvDB, keys.SystemSQLCodec, "test", "small")
   190  	medDesc := sqlbase.GetTableDescriptor(kvDB, keys.SystemSQLCodec, "test", "med")
   191  	highRangeDesc := sqlbase.GetTableDescriptor(kvDB, keys.SystemSQLCodec, "test", "offset")
   192  	overlappingDesc := sqlbase.GetTableDescriptor(kvDB, keys.SystemSQLCodec, "test", "overlapping")
   193  	compDesc := sqlbase.GetTableDescriptor(kvDB, keys.SystemSQLCodec, "test", "comp")
   194  	revCompDesc := sqlbase.GetTableDescriptor(kvDB, keys.SystemSQLCodec, "test", "rev")
   195  	compUnqDesc := sqlbase.GetTableDescriptor(kvDB, keys.SystemSQLCodec, "test", "unq")
   196  	t2Desc := sqlbase.GetTableDescriptor(kvDB, keys.SystemSQLCodec, "test", "t2")
   197  	nullableDesc := sqlbase.GetTableDescriptor(kvDB, keys.SystemSQLCodec, "test", "nullable")
   198  
   199  	testCases := []zigzagJoinerTestCase{
   200  		{
   201  			desc: "join on an empty table with itself on its primary key",
   202  			spec: execinfrapb.ZigzagJoinerSpec{
   203  				Tables:        []sqlbase.TableDescriptor{*empty, *empty},
   204  				EqColumns:     []execinfrapb.Columns{{Columns: []uint32{0, 1}}, {Columns: []uint32{0, 1}}},
   205  				Type:          sqlbase.InnerJoin,
   206  				IndexOrdinals: []uint32{1 /* (c) */, 2 /* (d) */},
   207  			},
   208  			fixedValues:   []sqlbase.EncDatumRow{{encInt(3)}, {encInt(7)}},
   209  			outCols:       []uint32{0, 1, 2, 7},
   210  			expectedTypes: intCols(4),
   211  			expected:      "[]",
   212  		},
   213  		{
   214  			desc: "join an empty table on the left with a populated table on its primary key",
   215  			spec: execinfrapb.ZigzagJoinerSpec{
   216  				Tables:        []sqlbase.TableDescriptor{*empty, *highRangeDesc},
   217  				EqColumns:     []execinfrapb.Columns{{Columns: []uint32{0, 1}}, {Columns: []uint32{0, 1}}},
   218  				Type:          sqlbase.InnerJoin,
   219  				IndexOrdinals: []uint32{1 /* (c) */, 2 /* (d) */},
   220  			},
   221  			fixedValues:   []sqlbase.EncDatumRow{{encInt(3)}, {encInt(7)}},
   222  			outCols:       []uint32{0, 1, 2, 7},
   223  			expectedTypes: intCols(4),
   224  			expected:      "[]",
   225  		},
   226  		{
   227  			desc: "join a populated table on the left with an empty table on its primary key",
   228  			spec: execinfrapb.ZigzagJoinerSpec{
   229  				Tables:        []sqlbase.TableDescriptor{*highRangeDesc, *empty},
   230  				EqColumns:     []execinfrapb.Columns{{Columns: []uint32{0, 1}}, {Columns: []uint32{0, 1}}},
   231  				Type:          sqlbase.InnerJoin,
   232  				IndexOrdinals: []uint32{1 /* (c) */, 2 /* (d) */},
   233  			},
   234  			fixedValues:   []sqlbase.EncDatumRow{{encInt(3)}, {encInt(7)}},
   235  			outCols:       []uint32{0, 1, 2, 7},
   236  			expectedTypes: intCols(4),
   237  			expected:      "[]",
   238  		},
   239  		{
   240  			desc: "join a table with a single row with itself on its primary key",
   241  			spec: execinfrapb.ZigzagJoinerSpec{
   242  				Tables:        []sqlbase.TableDescriptor{*single, *single},
   243  				EqColumns:     []execinfrapb.Columns{{Columns: []uint32{0, 1}}, {Columns: []uint32{0, 1}}},
   244  				Type:          sqlbase.InnerJoin,
   245  				IndexOrdinals: []uint32{1 /* (c) */, 2 /* (d) */},
   246  			},
   247  			fixedValues:   []sqlbase.EncDatumRow{{encInt(3)}, {encInt(7)}},
   248  			outCols:       []uint32{0, 1, 2, 7},
   249  			expectedTypes: intCols(4),
   250  			expected:      "[]",
   251  		},
   252  		{
   253  			desc: "join a table with a few rows with itself on its primary key",
   254  			spec: execinfrapb.ZigzagJoinerSpec{
   255  				Tables:        []sqlbase.TableDescriptor{*smallDesc, *smallDesc},
   256  				EqColumns:     []execinfrapb.Columns{{Columns: []uint32{0, 1}}, {Columns: []uint32{0, 1}}},
   257  				Type:          sqlbase.InnerJoin,
   258  				IndexOrdinals: []uint32{1 /* (c) */, 2 /* (d) */},
   259  			},
   260  			fixedValues:   []sqlbase.EncDatumRow{{encInt(3)}, {encInt(7)}},
   261  			outCols:       []uint32{0, 1, 2, 7},
   262  			expectedTypes: intCols(4),
   263  			expected:      "[[1 1 3 7]]",
   264  		},
   265  		{
   266  			desc: "join a populated table that has a match in the last row with itself",
   267  			spec: execinfrapb.ZigzagJoinerSpec{
   268  				Tables:        []sqlbase.TableDescriptor{*medDesc, *medDesc},
   269  				EqColumns:     []execinfrapb.Columns{{Columns: []uint32{0, 1}}, {Columns: []uint32{0, 1}}},
   270  				Type:          sqlbase.InnerJoin,
   271  				IndexOrdinals: []uint32{1 /* (c) */, 2 /* (d) */},
   272  			},
   273  			fixedValues:   []sqlbase.EncDatumRow{{encInt(3)}, {encInt(7)}},
   274  			outCols:       []uint32{0, 1, 2, 7},
   275  			expectedTypes: intCols(4),
   276  			expected:      "[[1 1 3 7] [3 3 3 7]]",
   277  		},
   278  		{
   279  			desc: "(a) is free, and outputs cartesian product",
   280  			spec: execinfrapb.ZigzagJoinerSpec{
   281  				Tables:        []sqlbase.TableDescriptor{*medDesc, *medDesc},
   282  				EqColumns:     []execinfrapb.Columns{{Columns: []uint32{0}}, {Columns: []uint32{0}}},
   283  				Type:          sqlbase.InnerJoin,
   284  				IndexOrdinals: []uint32{1 /* (c) */, 2 /* (d) */},
   285  			},
   286  			fixedValues:   []sqlbase.EncDatumRow{{encInt(3)}, {encInt(7)}},
   287  			outCols:       []uint32{0, 1, 2, 4, 5, 7},
   288  			expectedTypes: intCols(6),
   289  			expected: "[[0 3 3 0 2 7] [1 4 3 1 1 7] [1 1 3 1 1 7] [2 2 3 2 4 7] [2 2 3 2 0 7] [3 3 3 3 3 7] " +
   290  				"[3 0 3 3 3 7] [4 1 3 4 2 7]]",
   291  		},
   292  		{
   293  			desc: "set the fixed columns to be a part of the primary key",
   294  			spec: execinfrapb.ZigzagJoinerSpec{
   295  				Tables:        []sqlbase.TableDescriptor{*medDesc, *medDesc},
   296  				EqColumns:     []execinfrapb.Columns{{Columns: []uint32{1}}, {Columns: []uint32{1}}},
   297  				Type:          sqlbase.InnerJoin,
   298  				IndexOrdinals: []uint32{1 /* (c) */, 2 /* (d) */},
   299  			},
   300  			fixedValues:   []sqlbase.EncDatumRow{{encInt(3), encInt(1)}, {encInt(7), encInt(1)}},
   301  			outCols:       []uint32{0, 1, 2, 7},
   302  			expectedTypes: intCols(4),
   303  			expected:      "[[1 1 3 7]]",
   304  		},
   305  		{
   306  			desc: "join should work when there is a block of matches",
   307  			spec: execinfrapb.ZigzagJoinerSpec{
   308  				Tables:        []sqlbase.TableDescriptor{*highRangeDesc, *highRangeDesc},
   309  				EqColumns:     []execinfrapb.Columns{{Columns: []uint32{0}}, {Columns: []uint32{0}}},
   310  				Type:          sqlbase.InnerJoin,
   311  				IndexOrdinals: []uint32{1 /* (c) */, 2 /* (d) */},
   312  			},
   313  			fixedValues:   []sqlbase.EncDatumRow{{encInt(3)}, {encInt(7)}},
   314  			outCols:       []uint32{0, 1, 2, 4, 5, 7},
   315  			expectedTypes: intCols(6),
   316  			expected: "[[9 3 3 9 1 7] [9 0 3 9 1 7] [10 4 3 10 4 7] [10 4 3 10 0 7] [10 1 3 10 4 7] " +
   317  				"[10 1 3 10 0 7] [11 2 3 11 3 7] [12 3 3 12 2 7] [12 0 3 12 2 7]]",
   318  		},
   319  		{
   320  			desc: "join two different tables where first one is larger",
   321  			spec: execinfrapb.ZigzagJoinerSpec{
   322  				Tables:        []sqlbase.TableDescriptor{*medDesc, *smallDesc},
   323  				EqColumns:     []execinfrapb.Columns{{Columns: []uint32{0, 1}}, {Columns: []uint32{0, 1}}},
   324  				Type:          sqlbase.InnerJoin,
   325  				IndexOrdinals: []uint32{1 /* (c) */, 2 /* (d) */},
   326  			},
   327  			fixedValues:   []sqlbase.EncDatumRow{{encInt(3)}, {encInt(7)}},
   328  			outCols:       []uint32{0, 1, 2, 4, 5, 7},
   329  			expectedTypes: intCols(6),
   330  			expected:      "[[1 1 3 1 1 7]]",
   331  		},
   332  		{
   333  			desc: "join two different tables where second is larger",
   334  			spec: execinfrapb.ZigzagJoinerSpec{
   335  				Tables:        []sqlbase.TableDescriptor{*smallDesc, *medDesc},
   336  				EqColumns:     []execinfrapb.Columns{{Columns: []uint32{0, 1}}, {Columns: []uint32{0, 1}}},
   337  				Type:          sqlbase.InnerJoin,
   338  				IndexOrdinals: []uint32{1 /* (c) */, 2 /* (d) */},
   339  			},
   340  			fixedValues:   []sqlbase.EncDatumRow{{encInt(3)}, {encInt(7)}},
   341  			outCols:       []uint32{0, 1, 2, 4, 5, 7},
   342  			expectedTypes: intCols(6),
   343  			expected:      "[[1 1 3 1 1 7]]",
   344  		},
   345  		{
   346  			desc: "join on an index containing primary key columns explicitly",
   347  			spec: execinfrapb.ZigzagJoinerSpec{
   348  				Tables:        []sqlbase.TableDescriptor{*overlappingDesc, *overlappingDesc},
   349  				EqColumns:     []execinfrapb.Columns{{Columns: []uint32{1}}, {Columns: []uint32{1}}},
   350  				Type:          sqlbase.InnerJoin,
   351  				IndexOrdinals: []uint32{1 /* (a, c) */, 2 /* (d) */},
   352  			},
   353  			fixedValues:   []sqlbase.EncDatumRow{{encInt(3) /*a*/, encInt(3) /*c*/}, {encInt(7) /*d*/, encInt(3) /*a*/}},
   354  			outCols:       []uint32{0, 1, 2, 7},
   355  			expectedTypes: intCols(4),
   356  			expected:      "[[3 3 3 7]]",
   357  		},
   358  		{
   359  			desc: "join two tables with different schemas",
   360  			spec: execinfrapb.ZigzagJoinerSpec{
   361  				Tables:        []sqlbase.TableDescriptor{*smallDesc, *t2Desc},
   362  				EqColumns:     []execinfrapb.Columns{{Columns: []uint32{0, 1}}, {Columns: []uint32{0, 1}}},
   363  				Type:          sqlbase.InnerJoin,
   364  				IndexOrdinals: []uint32{0 /* (a, b) */, 0 /* (a, b) */},
   365  			},
   366  			outCols:       []uint32{0, 1},
   367  			expectedTypes: intCols(2),
   368  			expected:      "[[0 1] [0 2] [1 0] [1 1] [2 0]]",
   369  		},
   370  		{
   371  			desc: "join two tables with different schemas flipped",
   372  			spec: execinfrapb.ZigzagJoinerSpec{
   373  				Tables:        []sqlbase.TableDescriptor{*t2Desc, *smallDesc},
   374  				EqColumns:     []execinfrapb.Columns{{Columns: []uint32{0, 1}}, {Columns: []uint32{0, 1}}},
   375  				Type:          sqlbase.InnerJoin,
   376  				IndexOrdinals: []uint32{0 /* (a, b) */, 0 /* (a, b) */},
   377  			},
   378  			outCols:       []uint32{0, 1},
   379  			expectedTypes: intCols(2),
   380  			expected:      "[[0 1] [0 2] [1 0] [1 1] [2 0]]",
   381  		},
   382  		{
   383  			desc: "join on a populated table with no fixed columns",
   384  			spec: execinfrapb.ZigzagJoinerSpec{
   385  				Tables:        []sqlbase.TableDescriptor{*smallDesc, *smallDesc},
   386  				EqColumns:     []execinfrapb.Columns{{Columns: []uint32{0, 1}}, {Columns: []uint32{0, 1}}},
   387  				Type:          sqlbase.InnerJoin,
   388  				IndexOrdinals: []uint32{0 /* (a, b) */, 0 /* (a, b) */},
   389  			},
   390  			outCols:       []uint32{0, 1},
   391  			expectedTypes: intCols(2),
   392  			expected:      "[[0 1] [0 2] [0 3] [0 4] [1 0] [1 1] [1 2] [1 3] [1 4] [2 0]]",
   393  		},
   394  		{
   395  			desc: "join tables with different schemas with no locked columns",
   396  			spec: execinfrapb.ZigzagJoinerSpec{
   397  				Tables:        []sqlbase.TableDescriptor{*smallDesc, *t2Desc},
   398  				EqColumns:     []execinfrapb.Columns{{Columns: []uint32{1}}, {Columns: []uint32{1}}},
   399  				Type:          sqlbase.InnerJoin,
   400  				IndexOrdinals: []uint32{0 /* (a, b) */, 0 /* (a, b) */},
   401  			},
   402  			fixedValues:   []sqlbase.EncDatumRow{{encInt(1)}, {encInt(1)}},
   403  			outCols:       []uint32{0, 1},
   404  			expectedTypes: intCols(2),
   405  			expected:      "[[1 0] [1 1]]",
   406  		},
   407  		{
   408  			desc: "join a composite index with itself",
   409  			spec: execinfrapb.ZigzagJoinerSpec{
   410  				Tables:        []sqlbase.TableDescriptor{*compDesc, *compDesc},
   411  				EqColumns:     []execinfrapb.Columns{{Columns: []uint32{0, 1}}, {Columns: []uint32{0, 1}}},
   412  				Type:          sqlbase.InnerJoin,
   413  				IndexOrdinals: []uint32{1 /* (c, a, b) */, 2 /* (d) */},
   414  			},
   415  			fixedValues:   []sqlbase.EncDatumRow{{encInt(3)}, {encInt(7)}},
   416  			outCols:       []uint32{0, 1, 2, 7},
   417  			expectedTypes: intCols(4),
   418  			expected:      "[[1 1 3 7] [3 3 3 7]]",
   419  		},
   420  		{
   421  			desc: "join a composite index with the primary key reversed with itself",
   422  			spec: execinfrapb.ZigzagJoinerSpec{
   423  				Tables:        []sqlbase.TableDescriptor{*revCompDesc, *revCompDesc},
   424  				EqColumns:     []execinfrapb.Columns{{Columns: []uint32{0}}, {Columns: []uint32{0}}}, // join on a
   425  				Type:          sqlbase.InnerJoin,
   426  				IndexOrdinals: []uint32{1 /* (c, b, a) */, 2 /* (d) */},
   427  			},
   428  			fixedValues:   []sqlbase.EncDatumRow{{encInt(3), encInt(1)}, {encInt(7)}},
   429  			outCols:       []uint32{0, 1, 2, 7},
   430  			expectedTypes: intCols(4),
   431  			expected:      "[[1 1 3 7] [4 1 3 7]]",
   432  		},
   433  		{
   434  			desc: "join a composite index with the primary key reversed with itself with onExpr on value on one side",
   435  			spec: execinfrapb.ZigzagJoinerSpec{
   436  				Tables:        []sqlbase.TableDescriptor{*revCompDesc, *revCompDesc},
   437  				EqColumns:     []execinfrapb.Columns{{Columns: []uint32{0}}, {Columns: []uint32{0}}},
   438  				Type:          sqlbase.InnerJoin,
   439  				IndexOrdinals: []uint32{1 /* (c, b, a) */, 2 /* (d) */},
   440  				OnExpr:        execinfrapb.Expression{Expr: "@1 > 1"},
   441  			},
   442  			fixedValues:   []sqlbase.EncDatumRow{{encInt(3), encInt(1)}, {encInt(7)}},
   443  			outCols:       []uint32{0, 1, 2, 7},
   444  			expectedTypes: intCols(4),
   445  			expected:      "[[4 1 3 7]]",
   446  		},
   447  		{
   448  			desc: "join a composite index with the primary key reversed with itself and with onExpr comparing both sides",
   449  			spec: execinfrapb.ZigzagJoinerSpec{
   450  				Tables:        []sqlbase.TableDescriptor{*revCompDesc, *revCompDesc},
   451  				EqColumns:     []execinfrapb.Columns{{Columns: []uint32{0}}, {Columns: []uint32{0}}},
   452  				Type:          sqlbase.InnerJoin,
   453  				IndexOrdinals: []uint32{1 /* (c, b, a) */, 2 /* (d) */},
   454  				OnExpr:        execinfrapb.Expression{Expr: "@8 < 2 * @1"},
   455  			},
   456  			fixedValues:   []sqlbase.EncDatumRow{{encInt(3), encInt(1)}, {encInt(7)}},
   457  			outCols:       []uint32{0, 1, 2, 7},
   458  			expectedTypes: intCols(4),
   459  			expected:      "[[4 1 3 7]]",
   460  		},
   461  		{
   462  			desc: "join a composite index that doesn't contain the full primary key with itself",
   463  			spec: execinfrapb.ZigzagJoinerSpec{
   464  				Tables:        []sqlbase.TableDescriptor{*compUnqDesc, *compUnqDesc},
   465  				EqColumns:     []execinfrapb.Columns{{Columns: []uint32{1}}, {Columns: []uint32{1}}},
   466  				Type:          sqlbase.InnerJoin,
   467  				IndexOrdinals: []uint32{2 /* (c, b) */, 3 /* (d) */},
   468  			},
   469  			fixedValues:   []sqlbase.EncDatumRow{{encInt(21) /* c */}, {encInt(6), encInt(4) /* d, a */}},
   470  			outCols:       []uint32{0, 1, 2, 7},
   471  			expectedTypes: intCols(4),
   472  			expected:      "[[4 1 21 6]]",
   473  		},
   474  		{
   475  			desc: "test when equality columns may be null",
   476  			spec: execinfrapb.ZigzagJoinerSpec{
   477  				Tables:        []sqlbase.TableDescriptor{*nullableDesc, *nullableDesc},
   478  				EqColumns:     []execinfrapb.Columns{{Columns: []uint32{2}}, {Columns: []uint32{3}}},
   479  				Type:          sqlbase.InnerJoin,
   480  				IndexOrdinals: []uint32{1 /* (e) */, 2 /* (d) */},
   481  			},
   482  			outCols:       []uint32{0, 1, 2, 4, 5, 7},
   483  			expectedTypes: intCols(6),
   484  			expected: "[[1 2 4 1 2 4] [1 2 4 0 3 4] [0 3 4 1 2 4] [0 3 4 0 3 4] [1 3 5 1 3 5] " +
   485  				"[1 3 5 0 4 5] [0 4 5 1 3 5] [0 4 5 0 4 5] [1 4 6 1 4 6] [1 4 6 1 0 6] [1 4 6 0 1 6] " +
   486  				"[0 1 6 1 4 6] [0 1 6 1 0 6] [0 1 6 0 1 6] [1 1 7 2 0 7] [1 1 7 1 1 7] [1 1 7 0 2 7] " +
   487  				"[0 2 7 2 0 7] [0 2 7 1 1 7] [0 2 7 0 2 7]]",
   488  		},
   489  		{
   490  			desc: "test joining with primary key",
   491  			spec: execinfrapb.ZigzagJoinerSpec{
   492  				Tables:        []sqlbase.TableDescriptor{*medDesc, *medDesc},
   493  				EqColumns:     []execinfrapb.Columns{{Columns: []uint32{0}}, {Columns: []uint32{3}}},
   494  				Type:          sqlbase.InnerJoin,
   495  				IndexOrdinals: []uint32{0 /* primary (a, b) */, 2 /* (d) */},
   496  			},
   497  			outCols:       []uint32{0, 1, 2, 3, 4, 5, 7},
   498  			expectedTypes: intCols(7),
   499  			expected: "[[4 2 4 7 3 4 4] [4 1 3 6 3 4 4] [4 0 5 5 3 4 4] [4 2 4 7 3 0 4] [4 1 3 6 3 0 4] " +
   500  				"[4 0 5 5 3 0 4] [4 2 4 7 2 1 4] [4 1 3 6 2 1 4] [4 0 5 5 2 1 4] [4 2 4 7 1 2 4] " +
   501  				"[4 1 3 6 1 2 4] [4 0 5 5 1 2 4] [4 2 4 7 0 3 4] [4 1 3 6 0 3 4] [4 0 5 5 0 3 4]]",
   502  		},
   503  	}
   504  
   505  	for _, c := range testCases {
   506  		t.Run(c.desc, func(t *testing.T) {
   507  			st := cluster.MakeTestingClusterSettings()
   508  			evalCtx := tree.MakeTestingEvalContext(st)
   509  			defer evalCtx.Stop(ctx)
   510  			flowCtx := execinfra.FlowCtx{
   511  				EvalCtx: &evalCtx,
   512  				Cfg:     &execinfra.ServerConfig{Settings: st},
   513  				Txn:     kv.NewTxn(ctx, s.DB(), s.NodeID()),
   514  			}
   515  
   516  			out := &distsqlutils.RowBuffer{}
   517  			post := execinfrapb.PostProcessSpec{Projection: true, OutputColumns: c.outCols}
   518  			z, err := newZigzagJoiner(&flowCtx, 0 /* processorID */, &c.spec, c.fixedValues, &post, out)
   519  			if err != nil {
   520  				t.Fatal(err)
   521  			}
   522  
   523  			z.Run(ctx)
   524  
   525  			if !out.ProducerClosed() {
   526  				t.Fatalf("output RowReceiver not closed")
   527  			}
   528  
   529  			var res sqlbase.EncDatumRows
   530  			for {
   531  				row := out.NextNoMeta(t)
   532  				if row == nil {
   533  					break
   534  				}
   535  				res = append(res, row)
   536  			}
   537  
   538  			if result := res.String(c.expectedTypes); result != c.expected {
   539  				t.Errorf("invalid results for test '%s': %s, expected %s'", c.desc, result, c.expected)
   540  			}
   541  		})
   542  	}
   543  }
   544  
   545  // TestJoinReaderDrain tests various scenarios in which a zigzagJoiner's consumer
   546  // is closed.
   547  func TestZigzagJoinerDrain(t *testing.T) {
   548  	defer leaktest.AfterTest(t)()
   549  	ctx := context.Background()
   550  
   551  	s, sqlDB, kvDB := serverutils.StartServer(t, base.TestServerArgs{})
   552  	defer s.Stopper().Stop(ctx)
   553  
   554  	v := [10]tree.Datum{}
   555  	for i := range v {
   556  		v[i] = tree.NewDInt(tree.DInt(i))
   557  	}
   558  	encThree := sqlbase.DatumToEncDatum(types.Int, v[3])
   559  	encSeven := sqlbase.DatumToEncDatum(types.Int, v[7])
   560  
   561  	sqlutils.CreateTable(
   562  		t,
   563  		sqlDB,
   564  		"t",
   565  		"a INT, b INT, c INT, d INT, PRIMARY KEY (a,b), INDEX c (c), INDEX d (d)",
   566  		1, /* numRows */
   567  		sqlutils.ToRowFn(sqlutils.RowIdxFn, sqlutils.RowIdxFn, sqlutils.RowIdxFn, sqlutils.RowIdxFn),
   568  	)
   569  	td := sqlbase.GetTableDescriptor(kvDB, keys.SystemSQLCodec, "test", "t")
   570  
   571  	evalCtx := tree.MakeTestingEvalContext(s.ClusterSettings())
   572  	defer evalCtx.Stop(ctx)
   573  	flowCtx := execinfra.FlowCtx{
   574  		EvalCtx: &evalCtx,
   575  		Cfg:     &execinfra.ServerConfig{Settings: s.ClusterSettings()},
   576  		Txn:     kv.NewTxn(ctx, s.DB(), s.NodeID()),
   577  	}
   578  
   579  	encRow := make(sqlbase.EncDatumRow, 1)
   580  	encRow[0] = sqlbase.DatumToEncDatum(types.Int, tree.NewDInt(1))
   581  
   582  	// ConsumerClosed verifies that when a joinReader's consumer is closed, the
   583  	// joinReader finishes gracefully.
   584  	t.Run("ConsumerClosed", func(t *testing.T) {
   585  		out := &distsqlutils.RowBuffer{}
   586  		out.ConsumerClosed()
   587  		zz, err := newZigzagJoiner(
   588  			&flowCtx,
   589  			0, /* processorID */
   590  			&execinfrapb.ZigzagJoinerSpec{
   591  				Tables:        []sqlbase.TableDescriptor{*td, *td},
   592  				EqColumns:     []execinfrapb.Columns{{Columns: []uint32{0, 1}}, {Columns: []uint32{0, 1}}},
   593  				Type:          sqlbase.InnerJoin,
   594  				IndexOrdinals: []uint32{0, 1},
   595  			},
   596  			[]sqlbase.EncDatumRow{{encThree}, {encSeven}},
   597  			&execinfrapb.PostProcessSpec{Projection: true, OutputColumns: []uint32{0, 1}},
   598  			out,
   599  		)
   600  		if err != nil {
   601  			t.Fatal(err)
   602  		}
   603  		zz.Run(ctx)
   604  	})
   605  
   606  	//TODO(pbardea): When RowSource inputs are added, ensure that meta is
   607  	// propagated.
   608  }