github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/colexec/mergejoiner_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 colexec
    12  
    13  import (
    14  	"context"
    15  	"fmt"
    16  	"testing"
    17  
    18  	"github.com/cockroachdb/cockroach/pkg/col/coldata"
    19  	"github.com/cockroachdb/cockroach/pkg/col/coldatatestutils"
    20  	"github.com/cockroachdb/cockroach/pkg/settings/cluster"
    21  	"github.com/cockroachdb/cockroach/pkg/sql/colexecbase"
    22  	"github.com/cockroachdb/cockroach/pkg/sql/colmem"
    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/sql/types"
    28  	"github.com/cockroachdb/cockroach/pkg/testutils/colcontainerutils"
    29  	"github.com/cockroachdb/cockroach/pkg/util/humanizeutil"
    30  	"github.com/cockroachdb/cockroach/pkg/util/leaktest"
    31  	"github.com/cockroachdb/cockroach/pkg/util/mon"
    32  	"github.com/cockroachdb/cockroach/pkg/util/randutil"
    33  	"github.com/stretchr/testify/require"
    34  )
    35  
    36  func createSpecForMergeJoiner(tc *joinTestCase) *execinfrapb.ProcessorSpec {
    37  	leftOrdering := execinfrapb.Ordering{}
    38  	for i, eqCol := range tc.leftEqCols {
    39  		leftOrdering.Columns = append(
    40  			leftOrdering.Columns,
    41  			execinfrapb.Ordering_Column{
    42  				ColIdx:    eqCol,
    43  				Direction: tc.leftDirections[i],
    44  			},
    45  		)
    46  	}
    47  	rightOrdering := execinfrapb.Ordering{}
    48  	for i, eqCol := range tc.rightEqCols {
    49  		rightOrdering.Columns = append(
    50  			rightOrdering.Columns,
    51  			execinfrapb.Ordering_Column{
    52  				ColIdx:    eqCol,
    53  				Direction: tc.rightDirections[i],
    54  			},
    55  		)
    56  	}
    57  	mjSpec := &execinfrapb.MergeJoinerSpec{
    58  		LeftOrdering:  leftOrdering,
    59  		RightOrdering: rightOrdering,
    60  		OnExpr:        tc.onExpr,
    61  		Type:          tc.joinType,
    62  	}
    63  	projection := make([]uint32, 0, len(tc.leftOutCols)+len(tc.rightOutCols))
    64  	projection = append(projection, tc.leftOutCols...)
    65  	rColOffset := uint32(len(tc.leftTypes))
    66  	for _, outCol := range tc.rightOutCols {
    67  		projection = append(projection, rColOffset+outCol)
    68  	}
    69  	return &execinfrapb.ProcessorSpec{
    70  		Input: []execinfrapb.InputSyncSpec{
    71  			{ColumnTypes: tc.leftTypes},
    72  			{ColumnTypes: tc.rightTypes},
    73  		},
    74  		Core: execinfrapb.ProcessorCoreUnion{
    75  			MergeJoiner: mjSpec,
    76  		},
    77  		Post: execinfrapb.PostProcessSpec{
    78  			Projection:    true,
    79  			OutputColumns: projection,
    80  		},
    81  	}
    82  }
    83  
    84  var mjTestCases = []*joinTestCase{
    85  	{
    86  		description:  "basic test",
    87  		leftTypes:    []*types.T{types.Int},
    88  		rightTypes:   []*types.T{types.Int},
    89  		leftTuples:   tuples{{1}, {2}, {3}, {4}},
    90  		rightTuples:  tuples{{1}, {2}, {3}, {4}},
    91  		leftOutCols:  []uint32{0},
    92  		rightOutCols: []uint32{},
    93  		leftEqCols:   []uint32{0},
    94  		rightEqCols:  []uint32{0},
    95  		expected:     tuples{{1}, {2}, {3}, {4}},
    96  	},
    97  	{
    98  		description:  "basic test, no out cols",
    99  		leftTypes:    []*types.T{types.Int},
   100  		rightTypes:   []*types.T{types.Int},
   101  		leftTuples:   tuples{{1}, {2}, {3}, {4}},
   102  		rightTuples:  tuples{{1}, {2}, {3}, {4}},
   103  		leftOutCols:  []uint32{},
   104  		rightOutCols: []uint32{},
   105  		leftEqCols:   []uint32{0},
   106  		rightEqCols:  []uint32{0},
   107  		expected:     tuples{{}, {}, {}, {}},
   108  	},
   109  	{
   110  		description:  "basic test, out col on left",
   111  		leftTypes:    []*types.T{types.Int},
   112  		rightTypes:   []*types.T{types.Int},
   113  		leftTuples:   tuples{{1}, {2}, {3}, {4}},
   114  		rightTuples:  tuples{{1}, {2}, {3}, {4}},
   115  		leftOutCols:  []uint32{0},
   116  		rightOutCols: []uint32{},
   117  		leftEqCols:   []uint32{0},
   118  		rightEqCols:  []uint32{0},
   119  		expected:     tuples{{1}, {2}, {3}, {4}},
   120  	},
   121  	{
   122  		description:  "basic test, out col on right",
   123  		leftTypes:    []*types.T{types.Int},
   124  		rightTypes:   []*types.T{types.Int},
   125  		leftTuples:   tuples{{1}, {2}, {3}, {4}},
   126  		rightTuples:  tuples{{1}, {2}, {3}, {4}},
   127  		leftOutCols:  []uint32{},
   128  		rightOutCols: []uint32{0},
   129  		leftEqCols:   []uint32{0},
   130  		rightEqCols:  []uint32{0},
   131  		expected:     tuples{{1}, {2}, {3}, {4}},
   132  	},
   133  	{
   134  		description:  "basic test, L missing",
   135  		leftTypes:    []*types.T{types.Int},
   136  		rightTypes:   []*types.T{types.Int},
   137  		leftTuples:   tuples{{1}, {3}, {4}},
   138  		rightTuples:  tuples{{1}, {2}, {3}, {4}},
   139  		leftOutCols:  []uint32{0},
   140  		rightOutCols: []uint32{},
   141  		leftEqCols:   []uint32{0},
   142  		rightEqCols:  []uint32{0},
   143  		expected:     tuples{{1}, {3}, {4}},
   144  	},
   145  	{
   146  		description:  "basic test, R missing",
   147  		leftTypes:    []*types.T{types.Int},
   148  		rightTypes:   []*types.T{types.Int},
   149  		leftTuples:   tuples{{1}, {2}, {3}, {4}},
   150  		rightTuples:  tuples{{1}, {3}, {4}},
   151  		leftOutCols:  []uint32{},
   152  		rightOutCols: []uint32{0},
   153  		leftEqCols:   []uint32{0},
   154  		rightEqCols:  []uint32{0},
   155  		expected:     tuples{{1}, {3}, {4}},
   156  	},
   157  	{
   158  		description:  "basic test, L duplicate",
   159  		leftTypes:    []*types.T{types.Int},
   160  		rightTypes:   []*types.T{types.Int},
   161  		leftTuples:   tuples{{1}, {1}, {2}, {3}, {4}},
   162  		rightTuples:  tuples{{1}, {2}, {3}, {4}},
   163  		leftOutCols:  []uint32{0},
   164  		rightOutCols: []uint32{},
   165  		leftEqCols:   []uint32{0},
   166  		rightEqCols:  []uint32{0},
   167  		expected:     tuples{{1}, {1}, {2}, {3}, {4}},
   168  	},
   169  	{
   170  		description:  "basic test, R duplicate",
   171  		leftTypes:    []*types.T{types.Int},
   172  		rightTypes:   []*types.T{types.Int},
   173  		leftTuples:   tuples{{1}, {2}, {3}, {4}},
   174  		rightTuples:  tuples{{1}, {1}, {2}, {3}, {4}},
   175  		leftOutCols:  []uint32{},
   176  		rightOutCols: []uint32{0},
   177  		leftEqCols:   []uint32{0},
   178  		rightEqCols:  []uint32{0},
   179  		expected:     tuples{{1}, {1}, {2}, {3}, {4}},
   180  	},
   181  	{
   182  		description:  "basic test, R duplicate 2",
   183  		leftTypes:    []*types.T{types.Int},
   184  		rightTypes:   []*types.T{types.Int},
   185  		leftTuples:   tuples{{1}, {2}},
   186  		rightTuples:  tuples{{1}, {1}, {2}},
   187  		leftOutCols:  []uint32{0},
   188  		rightOutCols: []uint32{},
   189  		leftEqCols:   []uint32{0},
   190  		rightEqCols:  []uint32{0},
   191  		expected:     tuples{{1}, {1}, {2}},
   192  	},
   193  	{
   194  		description:  "basic test, L+R duplicates",
   195  		leftTypes:    []*types.T{types.Int},
   196  		rightTypes:   []*types.T{types.Int},
   197  		leftTuples:   tuples{{1}, {1}, {2}, {3}, {4}},
   198  		rightTuples:  tuples{{1}, {1}, {2}, {3}, {4}},
   199  		leftOutCols:  []uint32{},
   200  		rightOutCols: []uint32{0},
   201  		leftEqCols:   []uint32{0},
   202  		rightEqCols:  []uint32{0},
   203  		expected:     tuples{{1}, {1}, {1}, {1}, {2}, {3}, {4}},
   204  	},
   205  	{
   206  		description:  "basic test, L+R duplicate, multiple runs",
   207  		leftTypes:    []*types.T{types.Int},
   208  		rightTypes:   []*types.T{types.Int},
   209  		leftTuples:   tuples{{1}, {2}, {2}, {2}, {3}, {4}},
   210  		rightTuples:  tuples{{1}, {1}, {2}, {3}, {4}},
   211  		leftOutCols:  []uint32{},
   212  		rightOutCols: []uint32{0},
   213  		leftEqCols:   []uint32{0},
   214  		rightEqCols:  []uint32{0},
   215  		expected:     tuples{{1}, {1}, {2}, {2}, {2}, {3}, {4}},
   216  	},
   217  	{
   218  		description:  "cross product test, batch size = col.BatchSize()",
   219  		leftTypes:    []*types.T{types.Int},
   220  		rightTypes:   []*types.T{types.Int},
   221  		leftTuples:   tuples{{1}, {1}, {1}, {1}},
   222  		rightTuples:  tuples{{1}, {1}, {1}, {1}},
   223  		leftOutCols:  []uint32{0},
   224  		rightOutCols: []uint32{},
   225  		leftEqCols:   []uint32{0},
   226  		rightEqCols:  []uint32{0},
   227  		expected:     tuples{{1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}},
   228  	},
   229  	{
   230  		description:     "cross product test, batch size = 4 (small even)",
   231  		leftTypes:       []*types.T{types.Int},
   232  		rightTypes:      []*types.T{types.Int},
   233  		leftTuples:      tuples{{1}, {1}, {1}, {1}},
   234  		rightTuples:     tuples{{1}, {1}, {1}, {1}},
   235  		leftOutCols:     []uint32{0},
   236  		rightOutCols:    []uint32{},
   237  		leftEqCols:      []uint32{0},
   238  		rightEqCols:     []uint32{0},
   239  		expected:        tuples{{1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}},
   240  		outputBatchSize: 4,
   241  	},
   242  	{
   243  		description:     "cross product test, batch size = 3 (small odd)",
   244  		leftTypes:       []*types.T{types.Int},
   245  		rightTypes:      []*types.T{types.Int},
   246  		leftTuples:      tuples{{1}, {1}, {1}, {1}},
   247  		rightTuples:     tuples{{1}, {1}, {1}, {1}},
   248  		leftOutCols:     []uint32{},
   249  		rightOutCols:    []uint32{0},
   250  		leftEqCols:      []uint32{0},
   251  		rightEqCols:     []uint32{0},
   252  		expected:        tuples{{1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}},
   253  		outputBatchSize: 3,
   254  	},
   255  	{
   256  		description:     "cross product test, batch size = 1 (unit)",
   257  		leftTypes:       []*types.T{types.Int},
   258  		rightTypes:      []*types.T{types.Int},
   259  		leftTuples:      tuples{{1}, {1}, {1}, {1}},
   260  		rightTuples:     tuples{{1}, {1}, {1}, {1}},
   261  		leftOutCols:     []uint32{},
   262  		rightOutCols:    []uint32{0},
   263  		leftEqCols:      []uint32{0},
   264  		rightEqCols:     []uint32{0},
   265  		expected:        tuples{{1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}},
   266  		outputBatchSize: 1,
   267  	},
   268  	{
   269  		description:  "multi output column test, basic",
   270  		leftTypes:    []*types.T{types.Int, types.Int},
   271  		rightTypes:   []*types.T{types.Int, types.Int},
   272  		leftTuples:   tuples{{1, 10}, {2, 20}, {3, 30}, {4, 40}},
   273  		rightTuples:  tuples{{1, 11}, {2, 12}, {3, 13}, {4, 14}},
   274  		leftOutCols:  []uint32{0, 1},
   275  		rightOutCols: []uint32{0, 1},
   276  		leftEqCols:   []uint32{0},
   277  		rightEqCols:  []uint32{0},
   278  		expected:     tuples{{1, 10, 1, 11}, {2, 20, 2, 12}, {3, 30, 3, 13}, {4, 40, 4, 14}},
   279  	},
   280  	{
   281  		description:     "multi output column test, batch size = 1",
   282  		leftTypes:       []*types.T{types.Int, types.Int},
   283  		rightTypes:      []*types.T{types.Int, types.Int},
   284  		leftTuples:      tuples{{1, 10}, {2, 20}, {3, 30}, {4, 40}},
   285  		rightTuples:     tuples{{1, 11}, {2, 12}, {3, 13}, {4, 14}},
   286  		leftOutCols:     []uint32{0, 1},
   287  		rightOutCols:    []uint32{0, 1},
   288  		leftEqCols:      []uint32{0},
   289  		rightEqCols:     []uint32{0},
   290  		expected:        tuples{{1, 10, 1, 11}, {2, 20, 2, 12}, {3, 30, 3, 13}, {4, 40, 4, 14}},
   291  		outputBatchSize: 1,
   292  	},
   293  	{
   294  		description:  "multi output column test, test output coldata projection",
   295  		leftTypes:    []*types.T{types.Int, types.Int},
   296  		rightTypes:   []*types.T{types.Int, types.Int},
   297  		leftTuples:   tuples{{1, 10}, {2, 20}, {3, 30}, {4, 40}},
   298  		rightTuples:  tuples{{1, 11}, {2, 12}, {3, 13}, {4, 14}},
   299  		leftOutCols:  []uint32{0},
   300  		rightOutCols: []uint32{0},
   301  		leftEqCols:   []uint32{0},
   302  		rightEqCols:  []uint32{0},
   303  		expected:     tuples{{1, 1}, {2, 2}, {3, 3}, {4, 4}},
   304  	},
   305  	{
   306  		description:  "multi output column test, test output coldata projection",
   307  		leftTypes:    []*types.T{types.Int, types.Int},
   308  		rightTypes:   []*types.T{types.Int, types.Int},
   309  		leftTuples:   tuples{{1, 10}, {2, 20}, {3, 30}, {4, 40}},
   310  		rightTuples:  tuples{{1, 11}, {2, 12}, {3, 13}, {4, 14}},
   311  		leftOutCols:  []uint32{1},
   312  		rightOutCols: []uint32{1},
   313  		leftEqCols:   []uint32{0},
   314  		rightEqCols:  []uint32{0},
   315  		expected:     tuples{{10, 11}, {20, 12}, {30, 13}, {40, 14}},
   316  	},
   317  	{
   318  		description:  "multi output column test, L run",
   319  		leftTypes:    []*types.T{types.Int, types.Int},
   320  		rightTypes:   []*types.T{types.Int, types.Int},
   321  		leftTuples:   tuples{{1, 10}, {2, 20}, {2, 21}, {3, 30}, {4, 40}},
   322  		rightTuples:  tuples{{1, 11}, {2, 12}, {3, 13}, {4, 14}},
   323  		leftOutCols:  []uint32{0, 1},
   324  		rightOutCols: []uint32{0, 1},
   325  		leftEqCols:   []uint32{0},
   326  		rightEqCols:  []uint32{0},
   327  		expected:     tuples{{1, 10, 1, 11}, {2, 20, 2, 12}, {2, 21, 2, 12}, {3, 30, 3, 13}, {4, 40, 4, 14}},
   328  	},
   329  	{
   330  		description:     "multi output column test, L run, batch size = 1",
   331  		leftTypes:       []*types.T{types.Int, types.Int},
   332  		rightTypes:      []*types.T{types.Int, types.Int},
   333  		leftTuples:      tuples{{1, 10}, {2, 20}, {2, 21}, {3, 30}, {4, 40}},
   334  		rightTuples:     tuples{{1, 11}, {2, 12}, {3, 13}, {4, 14}},
   335  		leftOutCols:     []uint32{0, 1},
   336  		rightOutCols:    []uint32{0, 1},
   337  		leftEqCols:      []uint32{0},
   338  		rightEqCols:     []uint32{0},
   339  		expected:        tuples{{1, 10, 1, 11}, {2, 20, 2, 12}, {2, 21, 2, 12}, {3, 30, 3, 13}, {4, 40, 4, 14}},
   340  		outputBatchSize: 1,
   341  	},
   342  	{
   343  		description:  "multi output column test, R run",
   344  		leftTypes:    []*types.T{types.Int, types.Int},
   345  		rightTypes:   []*types.T{types.Int, types.Int},
   346  		leftTuples:   tuples{{1, 10}, {2, 20}, {3, 30}, {4, 40}},
   347  		rightTuples:  tuples{{1, 11}, {1, 111}, {2, 12}, {3, 13}, {4, 14}},
   348  		leftOutCols:  []uint32{0, 1},
   349  		rightOutCols: []uint32{0, 1},
   350  		leftEqCols:   []uint32{0},
   351  		rightEqCols:  []uint32{0},
   352  		expected:     tuples{{1, 10, 1, 11}, {1, 10, 1, 111}, {2, 20, 2, 12}, {3, 30, 3, 13}, {4, 40, 4, 14}},
   353  	},
   354  	{
   355  		description:     "multi output column test, R run, batch size = 1",
   356  		leftTypes:       []*types.T{types.Int, types.Int},
   357  		rightTypes:      []*types.T{types.Int, types.Int},
   358  		leftTuples:      tuples{{1, 10}, {2, 20}, {3, 30}, {4, 40}},
   359  		rightTuples:     tuples{{1, 11}, {1, 111}, {2, 12}, {3, 13}, {4, 14}},
   360  		leftOutCols:     []uint32{0, 1},
   361  		rightOutCols:    []uint32{0, 1},
   362  		leftEqCols:      []uint32{0},
   363  		rightEqCols:     []uint32{0},
   364  		expected:        tuples{{1, 10, 1, 11}, {1, 10, 1, 111}, {2, 20, 2, 12}, {3, 30, 3, 13}, {4, 40, 4, 14}},
   365  		outputBatchSize: 1,
   366  	},
   367  	{
   368  		description:  "logic test",
   369  		leftTypes:    []*types.T{types.Int, types.Int},
   370  		rightTypes:   []*types.T{types.Int, types.Int},
   371  		leftTuples:   tuples{{-1, -1}, {0, 4}, {2, 1}, {3, 4}, {5, 4}},
   372  		rightTuples:  tuples{{0, 5}, {1, 3}, {3, 2}, {4, 6}},
   373  		leftOutCols:  []uint32{1},
   374  		rightOutCols: []uint32{1},
   375  		leftEqCols:   []uint32{0},
   376  		rightEqCols:  []uint32{0},
   377  		expected:     tuples{{4, 5}, {4, 2}},
   378  	},
   379  	{
   380  		description:  "multi output column test, batch size = 1 and runs (to test saved output), reordered out columns",
   381  		leftTypes:    []*types.T{types.Int, types.Int},
   382  		rightTypes:   []*types.T{types.Int, types.Int},
   383  		leftTuples:   tuples{{1, 10}, {1, 10}, {1, 10}, {2, 20}, {3, 30}, {4, 40}},
   384  		rightTuples:  tuples{{1, 11}, {1, 11}, {2, 12}, {3, 13}, {4, 14}},
   385  		leftOutCols:  []uint32{1, 0},
   386  		rightOutCols: []uint32{1, 0},
   387  		leftEqCols:   []uint32{0},
   388  		rightEqCols:  []uint32{0},
   389  		expected: tuples{
   390  			{10, 1, 11, 1},
   391  			{10, 1, 11, 1},
   392  			{10, 1, 11, 1},
   393  			{10, 1, 11, 1},
   394  			{10, 1, 11, 1},
   395  			{10, 1, 11, 1},
   396  			{20, 2, 12, 2},
   397  			{30, 3, 13, 3},
   398  			{40, 4, 14, 4},
   399  		},
   400  		outputBatchSize: 1,
   401  	},
   402  	{
   403  		description:  "multi output column test, batch size = 1 and runs (to test saved output), reordered out columns that dont start at 0",
   404  		leftTypes:    []*types.T{types.Int, types.Int},
   405  		rightTypes:   []*types.T{types.Int, types.Int},
   406  		leftTuples:   tuples{{1, 10}, {1, 10}, {1, 10}, {2, 20}, {3, 30}, {4, 40}},
   407  		rightTuples:  tuples{{1, 11}, {1, 11}, {2, 12}, {3, 13}, {4, 14}},
   408  		leftOutCols:  []uint32{1, 0},
   409  		rightOutCols: []uint32{1},
   410  		leftEqCols:   []uint32{0},
   411  		rightEqCols:  []uint32{0},
   412  		expected: tuples{
   413  			{10, 1, 11},
   414  			{10, 1, 11},
   415  			{10, 1, 11},
   416  			{10, 1, 11},
   417  			{10, 1, 11},
   418  			{10, 1, 11},
   419  			{20, 2, 12},
   420  			{30, 3, 13},
   421  			{40, 4, 14},
   422  		},
   423  		outputBatchSize: 1,
   424  	},
   425  	{
   426  		description:  "equality column is correctly indexed",
   427  		leftTypes:    []*types.T{types.Int, types.Int},
   428  		rightTypes:   []*types.T{types.Int, types.Int},
   429  		leftTuples:   tuples{{10, 1}, {10, 1}, {10, 1}, {20, 2}, {30, 3}, {40, 4}},
   430  		rightTuples:  tuples{{1, 11}, {1, 11}, {2, 12}, {3, 13}, {4, 14}},
   431  		leftOutCols:  []uint32{1, 0},
   432  		rightOutCols: []uint32{1},
   433  		leftEqCols:   []uint32{1},
   434  		rightEqCols:  []uint32{0},
   435  		expected: tuples{
   436  			{1, 10, 11},
   437  			{1, 10, 11},
   438  			{1, 10, 11},
   439  			{1, 10, 11},
   440  			{1, 10, 11},
   441  			{1, 10, 11},
   442  			{2, 20, 12},
   443  			{3, 30, 13},
   444  			{4, 40, 14},
   445  		},
   446  	},
   447  	{
   448  		description:  "multi column equality basic test",
   449  		leftTypes:    []*types.T{types.Int, types.Int},
   450  		rightTypes:   []*types.T{types.Int, types.Int},
   451  		leftTuples:   tuples{{1, 10}, {2, 20}, {3, 30}, {4, 40}},
   452  		rightTuples:  tuples{{1, 10}, {2, 20}, {3, 13}, {4, 14}},
   453  		leftOutCols:  []uint32{0, 1},
   454  		rightOutCols: []uint32{0, 1},
   455  		leftEqCols:   []uint32{0, 1},
   456  		rightEqCols:  []uint32{0, 1},
   457  		expected: tuples{
   458  			{1, 10, 1, 10},
   459  			{2, 20, 2, 20},
   460  		},
   461  	},
   462  	{
   463  		description:  "multi column equality runs",
   464  		leftTypes:    []*types.T{types.Int, types.Int},
   465  		rightTypes:   []*types.T{types.Int, types.Int},
   466  		leftTuples:   tuples{{1, 10}, {1, 10}, {1, 10}, {2, 20}, {3, 30}, {4, 40}},
   467  		rightTuples:  tuples{{1, 10}, {1, 10}, {2, 20}, {3, 13}, {4, 14}},
   468  		leftOutCols:  []uint32{0, 1},
   469  		rightOutCols: []uint32{0, 1},
   470  		leftEqCols:   []uint32{0, 1},
   471  		rightEqCols:  []uint32{0, 1},
   472  		expected: tuples{
   473  			{1, 10, 1, 10},
   474  			{1, 10, 1, 10},
   475  			{1, 10, 1, 10},
   476  			{1, 10, 1, 10},
   477  			{1, 10, 1, 10},
   478  			{1, 10, 1, 10},
   479  			{2, 20, 2, 20},
   480  		},
   481  	},
   482  	{
   483  		description:  "multi column non-consecutive equality cols",
   484  		leftTypes:    []*types.T{types.Int, types.Int, types.Int},
   485  		rightTypes:   []*types.T{types.Int, types.Int, types.Int},
   486  		leftTuples:   tuples{{1, 123, 1}, {1, 234, 10}},
   487  		rightTuples:  tuples{{1, 1, 345}, {1, 10, 456}},
   488  		leftOutCols:  []uint32{0, 2, 1},
   489  		rightOutCols: []uint32{0, 2, 1},
   490  		leftEqCols:   []uint32{0, 2},
   491  		rightEqCols:  []uint32{0, 1},
   492  		expected: tuples{
   493  			{1, 1, 123, 1, 345, 1},
   494  			{1, 10, 234, 1, 456, 10},
   495  		},
   496  	},
   497  	{
   498  		description:  "multi column equality: new batch ends run",
   499  		leftTypes:    []*types.T{types.Int, types.Int},
   500  		rightTypes:   []*types.T{types.Int, types.Int},
   501  		leftTuples:   tuples{{1, 1}, {1, 1}, {3, 3}, {4, 3}},
   502  		rightTuples:  tuples{{1, 1}, {1, 2}, {3, 3}, {3, 3}},
   503  		leftOutCols:  []uint32{0, 1},
   504  		rightOutCols: []uint32{0, 1},
   505  		leftEqCols:   []uint32{0, 1},
   506  		rightEqCols:  []uint32{0, 1},
   507  		expected: tuples{
   508  			{1, 1, 1, 1},
   509  			{1, 1, 1, 1},
   510  			{3, 3, 3, 3},
   511  			{3, 3, 3, 3},
   512  		},
   513  	},
   514  	{
   515  		description:  "multi column equality: reordered eq columns",
   516  		leftTypes:    []*types.T{types.Int, types.Int},
   517  		rightTypes:   []*types.T{types.Int, types.Int},
   518  		leftTuples:   tuples{{1, 1}, {1, 1}, {3, 3}, {4, 3}},
   519  		rightTuples:  tuples{{1, 1}, {1, 2}, {3, 3}, {3, 3}},
   520  		leftOutCols:  []uint32{0, 1},
   521  		rightOutCols: []uint32{0, 1},
   522  		leftEqCols:   []uint32{0, 1},
   523  		rightEqCols:  []uint32{1, 0},
   524  		expected: tuples{
   525  			{1, 1, 1, 1},
   526  			{1, 1, 1, 1},
   527  			{3, 3, 3, 3},
   528  			{3, 3, 3, 3},
   529  		},
   530  	},
   531  	{
   532  		description:  "cross batch, distinct group",
   533  		leftTypes:    []*types.T{types.Int, types.Int},
   534  		rightTypes:   []*types.T{types.Int, types.Int},
   535  		leftTuples:   tuples{{1, 2}, {1, 2}, {1, 2}, {2, 2}},
   536  		rightTuples:  tuples{{1, 2}},
   537  		leftOutCols:  []uint32{0, 1},
   538  		rightOutCols: []uint32{0, 1},
   539  		leftEqCols:   []uint32{0, 1},
   540  		rightEqCols:  []uint32{0, 1},
   541  		expected: tuples{
   542  			{1, 2, 1, 2},
   543  			{1, 2, 1, 2},
   544  			{1, 2, 1, 2},
   545  		},
   546  	},
   547  	{
   548  		description:  "templating basic test",
   549  		leftTypes:    []*types.T{types.Bool, types.Int2, types.Float},
   550  		rightTypes:   []*types.T{types.Bool, types.Int2, types.Float},
   551  		leftTuples:   tuples{{true, int16(10), 1.2}, {true, int16(20), 2.2}, {true, int16(30), 3.2}},
   552  		rightTuples:  tuples{{true, int16(10), 1.2}, {false, int16(20), 2.2}, {true, int16(30), 3.9}},
   553  		leftOutCols:  []uint32{0, 1, 2},
   554  		rightOutCols: []uint32{0, 1, 2},
   555  		leftEqCols:   []uint32{0, 1, 2},
   556  		rightEqCols:  []uint32{0, 1, 2},
   557  		expected: tuples{
   558  			{true, 10, 1.2, true, 10, 1.2},
   559  		},
   560  	},
   561  	{
   562  		description:  "templating cross product test",
   563  		leftTypes:    []*types.T{types.Bool, types.Int2, types.Float},
   564  		rightTypes:   []*types.T{types.Bool, types.Int2, types.Float},
   565  		leftTuples:   tuples{{false, int16(10), 1.2}, {true, int16(20), 2.2}, {true, int16(30), 3.2}},
   566  		rightTuples:  tuples{{false, int16(10), 1.2}, {true, int16(20), 2.3}, {true, int16(20), 2.4}, {true, int16(31), 3.9}},
   567  		leftOutCols:  []uint32{0, 1, 2},
   568  		rightOutCols: []uint32{0, 1, 2},
   569  		leftEqCols:   []uint32{0, 1},
   570  		rightEqCols:  []uint32{0, 1},
   571  		expected: tuples{
   572  			{false, 10, 1.2, false, 10, 1.2},
   573  			{true, 20, 2.2, true, 20, 2.3},
   574  			{true, 20, 2.2, true, 20, 2.4},
   575  		},
   576  	},
   577  	{
   578  		description:  "templating cross product test, output batch size 1",
   579  		leftTypes:    []*types.T{types.Bool, types.Int2, types.Float},
   580  		rightTypes:   []*types.T{types.Bool, types.Int2, types.Float},
   581  		leftTuples:   tuples{{false, int16(10), 1.2}, {true, int16(20), 2.2}, {true, int16(30), 3.2}},
   582  		rightTuples:  tuples{{false, int16(10), 1.2}, {true, int16(20), 2.3}, {true, int16(20), 2.4}, {true, int16(31), 3.9}},
   583  		leftOutCols:  []uint32{0, 1, 2},
   584  		rightOutCols: []uint32{0, 1, 2},
   585  		leftEqCols:   []uint32{0, 1},
   586  		rightEqCols:  []uint32{0, 1},
   587  		expected: tuples{
   588  			{false, 10, 1.2, false, 10, 1.2},
   589  			{true, 20, 2.2, true, 20, 2.3},
   590  			{true, 20, 2.2, true, 20, 2.4},
   591  		},
   592  		outputBatchSize: 1,
   593  	},
   594  	{
   595  		description:  "templating cross product test, output batch size 2",
   596  		leftTypes:    []*types.T{types.Bool, types.Int2, types.Float},
   597  		rightTypes:   []*types.T{types.Bool, types.Int2, types.Float},
   598  		leftTuples:   tuples{{false, int16(10), 1.2}, {true, int16(20), 2.2}, {true, int16(30), 3.2}},
   599  		rightTuples:  tuples{{false, int16(10), 1.2}, {true, int16(20), 2.3}, {true, int16(20), 2.4}, {true, int16(31), 3.9}},
   600  		leftOutCols:  []uint32{0, 1, 2},
   601  		rightOutCols: []uint32{0, 1, 2},
   602  		leftEqCols:   []uint32{0, 1},
   603  		rightEqCols:  []uint32{0, 1},
   604  		expected: tuples{
   605  			{false, 10, 1.2, false, 10, 1.2},
   606  			{true, 20, 2.2, true, 20, 2.3},
   607  			{true, 20, 2.2, true, 20, 2.4},
   608  		},
   609  		outputBatchSize: 2,
   610  	},
   611  	{
   612  		description:  "templating reordered eq columns",
   613  		leftTypes:    []*types.T{types.Bool, types.Int2, types.Float},
   614  		rightTypes:   []*types.T{types.Bool, types.Int2, types.Float},
   615  		leftTuples:   tuples{{false, int16(10), 1.2}, {true, int16(20), 2.2}, {true, int16(30), 3.2}},
   616  		rightTuples:  tuples{{false, int16(10), 1.2}, {true, int16(20), 2.3}, {true, int16(20), 2.4}, {true, int16(31), 3.9}},
   617  		leftOutCols:  []uint32{0, 1, 2},
   618  		rightOutCols: []uint32{0, 1, 2},
   619  		leftEqCols:   []uint32{1, 0},
   620  		rightEqCols:  []uint32{1, 0},
   621  		expected: tuples{
   622  			{false, 10, 1.2, false, 10, 1.2},
   623  			{true, 20, 2.2, true, 20, 2.3},
   624  			{true, 20, 2.2, true, 20, 2.4},
   625  		},
   626  	},
   627  	{
   628  		description:  "templating reordered eq columns non symmetrical",
   629  		leftTypes:    []*types.T{types.Bool, types.Int2, types.Float},
   630  		rightTypes:   []*types.T{types.Int2, types.Float, types.Bool},
   631  		leftTuples:   tuples{{false, int16(10), 1.2}, {true, int16(20), 2.2}, {true, int16(30), 3.2}},
   632  		rightTuples:  tuples{{int16(10), 1.2, false}, {int16(20), 2.2, true}, {int16(21), 2.2, true}, {int16(30), 3.2, false}},
   633  		leftOutCols:  []uint32{0, 1, 2},
   634  		rightOutCols: []uint32{0, 1, 2},
   635  		leftEqCols:   []uint32{2, 0},
   636  		rightEqCols:  []uint32{1, 2},
   637  		expected: tuples{
   638  			{false, 10, 1.2, 10, 1.2, false},
   639  			{true, 20, 2.2, 20, 2.2, true},
   640  			{true, 20, 2.2, 21, 2.2, true},
   641  		},
   642  	},
   643  	{
   644  		description:  "null handling",
   645  		leftTypes:    []*types.T{types.Int},
   646  		rightTypes:   []*types.T{types.Int},
   647  		leftTuples:   tuples{{nil}, {0}},
   648  		rightTuples:  tuples{{nil}, {0}},
   649  		leftOutCols:  []uint32{0},
   650  		rightOutCols: []uint32{0},
   651  		leftEqCols:   []uint32{0},
   652  		rightEqCols:  []uint32{0},
   653  		expected: tuples{
   654  			{0, 0},
   655  		},
   656  	},
   657  	{
   658  		description:  "null handling multi column, nulls on left",
   659  		leftTypes:    []*types.T{types.Int, types.Int},
   660  		rightTypes:   []*types.T{types.Int, types.Int},
   661  		leftTuples:   tuples{{nil, 0}, {0, nil}},
   662  		rightTuples:  tuples{{nil, nil}, {0, 1}},
   663  		leftOutCols:  []uint32{0, 1},
   664  		rightOutCols: []uint32{0, 1},
   665  		leftEqCols:   []uint32{0},
   666  		rightEqCols:  []uint32{0},
   667  		expected: tuples{
   668  			{0, nil, 0, 1},
   669  		},
   670  	},
   671  	{
   672  		description:  "null handling multi column, nulls on right",
   673  		leftTypes:    []*types.T{types.Int, types.Int},
   674  		rightTypes:   []*types.T{types.Int, types.Int},
   675  		leftTuples:   tuples{{nil, 0}, {0, 1}},
   676  		rightTuples:  tuples{{nil, nil}, {0, nil}},
   677  		leftOutCols:  []uint32{0, 1},
   678  		rightOutCols: []uint32{0, 1},
   679  		leftEqCols:   []uint32{0},
   680  		rightEqCols:  []uint32{0},
   681  		expected: tuples{
   682  			{0, 1, 0, nil},
   683  		},
   684  	},
   685  	{
   686  		description:  "desc test",
   687  		leftTypes:    []*types.T{types.Int},
   688  		rightTypes:   []*types.T{types.Int},
   689  		leftTuples:   tuples{{4}, {3}, {2}, {1}},
   690  		rightTuples:  tuples{{4}, {2}, {1}},
   691  		leftOutCols:  []uint32{0},
   692  		rightOutCols: []uint32{0},
   693  		leftEqCols:   []uint32{0},
   694  		rightEqCols:  []uint32{0},
   695  		expected:     tuples{{4, 4}, {2, 2}, {1, 1}},
   696  
   697  		leftDirections:  []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_DESC},
   698  		rightDirections: []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_DESC},
   699  	},
   700  	{
   701  		description:  "desc nulls test",
   702  		leftTypes:    []*types.T{types.Int},
   703  		rightTypes:   []*types.T{types.Int},
   704  		leftTuples:   tuples{{4}, {3}, {nil}, {1}},
   705  		rightTuples:  tuples{{4}, {nil}, {2}, {1}},
   706  		leftOutCols:  []uint32{0},
   707  		rightOutCols: []uint32{0},
   708  		leftEqCols:   []uint32{0},
   709  		rightEqCols:  []uint32{0},
   710  		expected:     tuples{{4, 4}, {1, 1}},
   711  
   712  		leftDirections:  []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_DESC},
   713  		rightDirections: []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_DESC},
   714  	},
   715  	{
   716  		description:  "desc nulls test end on 0",
   717  		leftTypes:    []*types.T{types.Int},
   718  		rightTypes:   []*types.T{types.Int},
   719  		leftTuples:   tuples{{9}, {9}, {8}, {0}, {nil}},
   720  		rightTuples:  tuples{{9}, {9}, {8}, {0}, {nil}},
   721  		leftOutCols:  []uint32{0},
   722  		rightOutCols: []uint32{0},
   723  		leftEqCols:   []uint32{0},
   724  		rightEqCols:  []uint32{0},
   725  		expected:     tuples{{9, 9}, {9, 9}, {9, 9}, {9, 9}, {8, 8}, {0, 0}},
   726  
   727  		leftDirections:  []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_DESC},
   728  		rightDirections: []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_DESC},
   729  	},
   730  	{
   731  		description:  "non-equality columns with nulls",
   732  		leftTypes:    []*types.T{types.Int, types.Int},
   733  		rightTypes:   []*types.T{types.Int, types.Int},
   734  		leftTuples:   tuples{{1, nil}, {2, 2}, {2, 2}, {3, nil}, {4, nil}},
   735  		rightTuples:  tuples{{1, 1}, {2, nil}, {2, nil}, {3, nil}, {4, 4}, {4, 4}},
   736  		leftOutCols:  []uint32{0, 1},
   737  		rightOutCols: []uint32{0, 1},
   738  		leftEqCols:   []uint32{0},
   739  		rightEqCols:  []uint32{0},
   740  		expected:     tuples{{1, nil, 1, 1}, {2, 2, 2, nil}, {2, 2, 2, nil}, {2, 2, 2, nil}, {2, 2, 2, nil}, {3, nil, 3, nil}, {4, nil, 4, 4}, {4, nil, 4, 4}},
   741  	},
   742  	{
   743  		description:  "basic LEFT OUTER JOIN test, L and R exhausted at the same time",
   744  		joinType:     sqlbase.LeftOuterJoin,
   745  		leftTypes:    []*types.T{types.Int},
   746  		rightTypes:   []*types.T{types.Int},
   747  		leftTuples:   tuples{{1}, {2}, {3}, {4}, {4}},
   748  		rightTuples:  tuples{{0}, {2}, {3}, {4}, {4}},
   749  		leftOutCols:  []uint32{0},
   750  		rightOutCols: []uint32{0},
   751  		leftEqCols:   []uint32{0},
   752  		rightEqCols:  []uint32{0},
   753  		expected:     tuples{{1, nil}, {2, 2}, {3, 3}, {4, 4}, {4, 4}, {4, 4}, {4, 4}},
   754  	},
   755  	{
   756  		description:  "basic LEFT OUTER JOIN test, R exhausted first",
   757  		joinType:     sqlbase.LeftOuterJoin,
   758  		leftTypes:    []*types.T{types.Int},
   759  		rightTypes:   []*types.T{types.Int},
   760  		leftTuples:   tuples{{1}, {1}, {3}, {5}, {6}, {7}},
   761  		rightTuples:  tuples{{2}, {3}, {4}},
   762  		leftOutCols:  []uint32{0},
   763  		rightOutCols: []uint32{0},
   764  		leftEqCols:   []uint32{0},
   765  		rightEqCols:  []uint32{0},
   766  		expected:     tuples{{1, nil}, {1, nil}, {3, 3}, {5, nil}, {6, nil}, {7, nil}},
   767  	},
   768  	{
   769  		description:  "basic LEFT OUTER JOIN test, L exhausted first",
   770  		joinType:     sqlbase.LeftOuterJoin,
   771  		leftTypes:    []*types.T{types.Int},
   772  		rightTypes:   []*types.T{types.Int},
   773  		leftTuples:   tuples{{3}, {5}, {6}, {7}},
   774  		rightTuples:  tuples{{2}, {3}, {4}, {6}, {8}, {9}},
   775  		leftOutCols:  []uint32{0},
   776  		rightOutCols: []uint32{0},
   777  		leftEqCols:   []uint32{0},
   778  		rightEqCols:  []uint32{0},
   779  		expected:     tuples{{3, 3}, {5, nil}, {6, 6}, {7, nil}},
   780  	},
   781  	{
   782  		description:  "multi output column LEFT OUTER JOIN test with nulls",
   783  		joinType:     sqlbase.LeftOuterJoin,
   784  		leftTypes:    []*types.T{types.Int, types.Int},
   785  		rightTypes:   []*types.T{types.Int, types.Int},
   786  		leftTuples:   tuples{{1, 10}, {2, 20}, {3, nil}, {4, 40}},
   787  		rightTuples:  tuples{{1, nil}, {3, 13}, {4, 14}},
   788  		leftOutCols:  []uint32{0, 1},
   789  		rightOutCols: []uint32{0, 1},
   790  		leftEqCols:   []uint32{0},
   791  		rightEqCols:  []uint32{0},
   792  		expected:     tuples{{1, 10, 1, nil}, {2, 20, nil, nil}, {3, nil, 3, 13}, {4, 40, 4, 14}},
   793  	},
   794  	{
   795  		description:  "null in equality column LEFT OUTER JOIN",
   796  		joinType:     sqlbase.LeftOuterJoin,
   797  		leftTypes:    []*types.T{types.Int},
   798  		rightTypes:   []*types.T{types.Int, types.Int},
   799  		leftTuples:   tuples{{nil}, {nil}, {1}, {3}},
   800  		rightTuples:  tuples{{nil, 1}, {1, 1}, {2, 2}, {3, 3}},
   801  		leftOutCols:  []uint32{0},
   802  		rightOutCols: []uint32{0, 1},
   803  		leftEqCols:   []uint32{0},
   804  		rightEqCols:  []uint32{0},
   805  		expected:     tuples{{nil, nil, nil}, {nil, nil, nil}, {1, 1, 1}, {3, 3, 3}},
   806  	},
   807  	{
   808  		description:  "multi equality column LEFT OUTER JOIN test with nulls",
   809  		joinType:     sqlbase.LeftOuterJoin,
   810  		leftTypes:    []*types.T{types.Int, types.Int},
   811  		rightTypes:   []*types.T{types.Int, types.Int},
   812  		leftTuples:   tuples{{nil, nil}, {nil, 10}, {1, nil}, {1, 10}, {2, 20}, {4, 40}},
   813  		rightTuples:  tuples{{nil, nil}, {nil, 10}, {1, nil}, {1, nil}, {2, 20}, {3, 30}},
   814  		leftOutCols:  []uint32{0, 1},
   815  		rightOutCols: []uint32{0, 1},
   816  		leftEqCols:   []uint32{0, 1},
   817  		rightEqCols:  []uint32{0, 1},
   818  		expected:     tuples{{nil, nil, nil, nil}, {nil, 10, nil, nil}, {1, nil, nil, nil}, {1, 10, nil, nil}, {2, 20, 2, 20}, {4, 40, nil, nil}},
   819  	},
   820  	{
   821  		description:  "multi equality column (long runs on left) LEFT OUTER JOIN test with nulls",
   822  		joinType:     sqlbase.LeftOuterJoin,
   823  		leftTypes:    []*types.T{types.Int, types.Int},
   824  		rightTypes:   []*types.T{types.Int, types.Int},
   825  		leftTuples:   tuples{{1, 9}, {1, 10}, {1, 10}, {1, 11}, {2, 20}, {2, 20}, {2, 21}, {2, 22}, {2, 22}},
   826  		rightTuples:  tuples{{1, 8}, {1, 11}, {1, 11}, {2, 21}, {2, 23}},
   827  		leftOutCols:  []uint32{0, 1},
   828  		rightOutCols: []uint32{0, 1},
   829  		leftEqCols:   []uint32{0, 1},
   830  		rightEqCols:  []uint32{0, 1},
   831  		expected:     tuples{{1, 9, nil, nil}, {1, 10, nil, nil}, {1, 10, nil, nil}, {1, 11, 1, 11}, {1, 11, 1, 11}, {2, 20, nil, nil}, {2, 20, nil, nil}, {2, 21, 2, 21}, {2, 22, nil, nil}, {2, 22, nil, nil}},
   832  	},
   833  	{
   834  		description:     "3 equality column LEFT OUTER JOIN test with nulls DESC ordering",
   835  		joinType:        sqlbase.LeftOuterJoin,
   836  		leftTypes:       []*types.T{types.Int, types.Int, types.Int},
   837  		rightTypes:      []*types.T{types.Int, types.Int, types.Int},
   838  		leftDirections:  []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_DESC},
   839  		rightDirections: []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_DESC},
   840  		leftTuples:      tuples{{2, 3, 1}, {2, nil, 1}, {nil, 1, 3}},
   841  		rightTuples:     tuples{{4, 3, 3}, {nil, 2, nil}, {nil, 1, 3}},
   842  		leftOutCols:     []uint32{0, 1, 2},
   843  		rightOutCols:    []uint32{0, 1, 2},
   844  		leftEqCols:      []uint32{0, 1, 2},
   845  		rightEqCols:     []uint32{0, 1, 2},
   846  		expected:        tuples{{2, 3, 1, nil, nil, nil}, {2, nil, 1, nil, nil, nil}, {nil, 1, 3, nil, nil, nil}},
   847  	},
   848  	{
   849  		description:     "3 equality column LEFT OUTER JOIN test with nulls mixed ordering",
   850  		joinType:        sqlbase.LeftOuterJoin,
   851  		leftTypes:       []*types.T{types.Int, types.Int, types.Int},
   852  		rightTypes:      []*types.T{types.Int, types.Int, types.Int},
   853  		leftDirections:  []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_ASC},
   854  		rightDirections: []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_ASC, execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_DESC},
   855  		leftTuples:      tuples{{2, 3, 1}, {2, nil, 1}, {nil, 1, 3}},
   856  		rightTuples:     tuples{{4, 3, 3}, {nil, 2, nil}, {nil, 1, 3}},
   857  		leftOutCols:     []uint32{0, 1, 2},
   858  		rightOutCols:    []uint32{0, 1, 2},
   859  		leftEqCols:      []uint32{0, 1, 2},
   860  		rightEqCols:     []uint32{1, 2, 0},
   861  		expected:        tuples{{2, 3, 1, nil, nil, nil}, {2, nil, 1, nil, nil, nil}, {nil, 1, 3, nil, nil, nil}},
   862  	},
   863  	{
   864  		description:     "single column DESC with nulls on the left LEFT OUTER JOIN",
   865  		joinType:        sqlbase.LeftOuterJoin,
   866  		leftTypes:       []*types.T{types.Int},
   867  		rightTypes:      []*types.T{types.Int},
   868  		leftDirections:  []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_DESC},
   869  		rightDirections: []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_DESC},
   870  		leftTuples:      tuples{{1}, {1}, {1}, {nil}, {nil}, {nil}},
   871  		rightTuples:     tuples{{1}},
   872  		leftOutCols:     []uint32{0},
   873  		rightOutCols:    []uint32{0},
   874  		leftEqCols:      []uint32{0},
   875  		rightEqCols:     []uint32{0},
   876  		expected:        tuples{{1, 1}, {1, 1}, {1, 1}, {nil, nil}, {nil, nil}, {nil, nil}},
   877  	},
   878  	{
   879  		description:  "basic RIGHT OUTER JOIN test, L and R exhausted at the same time",
   880  		joinType:     sqlbase.RightOuterJoin,
   881  		leftTypes:    []*types.T{types.Int},
   882  		rightTypes:   []*types.T{types.Int},
   883  		leftTuples:   tuples{{-1}, {2}, {3}, {4}, {4}},
   884  		rightTuples:  tuples{{1}, {2}, {3}, {4}, {4}},
   885  		leftOutCols:  []uint32{0},
   886  		rightOutCols: []uint32{0},
   887  		leftEqCols:   []uint32{0},
   888  		rightEqCols:  []uint32{0},
   889  		expected:     tuples{{nil, 1}, {2, 2}, {3, 3}, {4, 4}, {4, 4}, {4, 4}, {4, 4}},
   890  	},
   891  	{
   892  		description:  "basic RIGHT OUTER JOIN test, R exhausted first",
   893  		joinType:     sqlbase.RightOuterJoin,
   894  		leftTypes:    []*types.T{types.Int},
   895  		rightTypes:   []*types.T{types.Int},
   896  		leftTuples:   tuples{{1}, {1}, {3}, {5}, {6}, {7}},
   897  		rightTuples:  tuples{{2}, {3}, {4}},
   898  		leftOutCols:  []uint32{0},
   899  		rightOutCols: []uint32{0},
   900  		leftEqCols:   []uint32{0},
   901  		rightEqCols:  []uint32{0},
   902  		expected:     tuples{{nil, 2}, {3, 3}, {nil, 4}},
   903  	},
   904  	{
   905  		description:  "basic RIGHT OUTER JOIN test, L exhausted first",
   906  		joinType:     sqlbase.RightOuterJoin,
   907  		leftTypes:    []*types.T{types.Int},
   908  		rightTypes:   []*types.T{types.Int},
   909  		leftTuples:   tuples{{3}, {5}, {6}, {7}},
   910  		rightTuples:  tuples{{2}, {3}, {4}, {6}, {8}, {9}},
   911  		leftOutCols:  []uint32{0},
   912  		rightOutCols: []uint32{0},
   913  		leftEqCols:   []uint32{0},
   914  		rightEqCols:  []uint32{0},
   915  		expected:     tuples{{nil, 2}, {3, 3}, {nil, 4}, {6, 6}, {nil, 8}, {nil, 9}},
   916  	},
   917  	{
   918  		description:  "multi output column RIGHT OUTER JOIN test with nulls",
   919  		joinType:     sqlbase.RightOuterJoin,
   920  		leftTypes:    []*types.T{types.Int, types.Int},
   921  		rightTypes:   []*types.T{types.Int, types.Int},
   922  		leftTuples:   tuples{{1, nil}, {3, 13}, {4, 14}},
   923  		rightTuples:  tuples{{1, 10}, {2, 20}, {3, nil}, {4, 40}},
   924  		leftOutCols:  []uint32{0, 1},
   925  		rightOutCols: []uint32{0, 1},
   926  		leftEqCols:   []uint32{0},
   927  		rightEqCols:  []uint32{0},
   928  		expected:     tuples{{1, nil, 1, 10}, {nil, nil, 2, 20}, {3, 13, 3, nil}, {4, 14, 4, 40}},
   929  	},
   930  	{
   931  		description:  "null in equality column RIGHT OUTER JOIN",
   932  		joinType:     sqlbase.RightOuterJoin,
   933  		leftTypes:    []*types.T{types.Int, types.Int},
   934  		rightTypes:   []*types.T{types.Int},
   935  		leftTuples:   tuples{{nil, 1}, {1, 1}, {2, 2}, {3, 3}},
   936  		rightTuples:  tuples{{nil}, {nil}, {1}, {3}},
   937  		leftOutCols:  []uint32{0, 1},
   938  		rightOutCols: []uint32{0},
   939  		leftEqCols:   []uint32{0},
   940  		rightEqCols:  []uint32{0},
   941  		expected:     tuples{{nil, nil, nil}, {nil, nil, nil}, {1, 1, 1}, {3, 3, 3}},
   942  	},
   943  	{
   944  		description:  "multi equality column RIGHT OUTER JOIN test with nulls",
   945  		joinType:     sqlbase.RightOuterJoin,
   946  		leftTypes:    []*types.T{types.Int, types.Int},
   947  		rightTypes:   []*types.T{types.Int, types.Int},
   948  		leftTuples:   tuples{{nil, nil}, {nil, 10}, {1, nil}, {1, nil}, {2, 20}, {3, 30}},
   949  		rightTuples:  tuples{{nil, nil}, {nil, 10}, {1, nil}, {1, 10}, {2, 20}, {4, 40}},
   950  		leftOutCols:  []uint32{0, 1},
   951  		rightOutCols: []uint32{0, 1},
   952  		leftEqCols:   []uint32{0, 1},
   953  		rightEqCols:  []uint32{0, 1},
   954  		expected:     tuples{{nil, nil, nil, nil}, {nil, nil, nil, 10}, {nil, nil, 1, nil}, {nil, nil, 1, 10}, {2, 20, 2, 20}, {nil, nil, 4, 40}},
   955  	},
   956  	{
   957  		description:  "multi equality column (long runs on right) RIGHT OUTER JOIN test with nulls",
   958  		joinType:     sqlbase.RightOuterJoin,
   959  		leftTypes:    []*types.T{types.Int, types.Int},
   960  		rightTypes:   []*types.T{types.Int, types.Int},
   961  		leftTuples:   tuples{{1, 8}, {1, 11}, {1, 11}, {2, 21}, {2, 23}},
   962  		rightTuples:  tuples{{1, 9}, {1, 10}, {1, 10}, {1, 11}, {2, 20}, {2, 20}, {2, 21}, {2, 22}, {2, 22}},
   963  		leftOutCols:  []uint32{0, 1},
   964  		rightOutCols: []uint32{0, 1},
   965  		leftEqCols:   []uint32{0, 1},
   966  		rightEqCols:  []uint32{0, 1},
   967  		expected:     tuples{{nil, nil, 1, 9}, {nil, nil, 1, 10}, {nil, nil, 1, 10}, {1, 11, 1, 11}, {1, 11, 1, 11}, {nil, nil, 2, 20}, {nil, nil, 2, 20}, {2, 21, 2, 21}, {nil, nil, 2, 22}, {nil, nil, 2, 22}},
   968  	},
   969  	{
   970  		description:     "3 equality column RIGHT OUTER JOIN test with nulls DESC ordering",
   971  		joinType:        sqlbase.RightOuterJoin,
   972  		leftTypes:       []*types.T{types.Int, types.Int, types.Int},
   973  		rightTypes:      []*types.T{types.Int, types.Int, types.Int},
   974  		leftDirections:  []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_DESC},
   975  		rightDirections: []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_DESC},
   976  		leftTuples:      tuples{{4, 3, 3}, {nil, 2, nil}, {nil, 1, 3}},
   977  		rightTuples:     tuples{{2, 3, 1}, {2, nil, 1}, {nil, 1, 3}},
   978  		leftOutCols:     []uint32{0, 1, 2},
   979  		rightOutCols:    []uint32{0, 1, 2},
   980  		leftEqCols:      []uint32{0, 1, 2},
   981  		rightEqCols:     []uint32{0, 1, 2},
   982  		expected:        tuples{{nil, nil, nil, 2, 3, 1}, {nil, nil, nil, 2, nil, 1}, {nil, nil, nil, nil, 1, 3}},
   983  	},
   984  	{
   985  		description:     "3 equality column RIGHT OUTER JOIN test with nulls mixed ordering",
   986  		joinType:        sqlbase.RightOuterJoin,
   987  		leftTypes:       []*types.T{types.Int, types.Int, types.Int},
   988  		rightTypes:      []*types.T{types.Int, types.Int, types.Int},
   989  		leftDirections:  []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_ASC},
   990  		rightDirections: []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_ASC, execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_DESC},
   991  		leftTuples:      tuples{{4, 3, 3}, {nil, 2, nil}, {nil, 1, 3}},
   992  		rightTuples:     tuples{{2, 3, 1}, {2, nil, 1}, {nil, 1, 3}},
   993  		leftOutCols:     []uint32{0, 1, 2},
   994  		rightOutCols:    []uint32{0, 1, 2},
   995  		leftEqCols:      []uint32{0, 1, 2},
   996  		rightEqCols:     []uint32{1, 2, 0},
   997  		expected:        tuples{{nil, nil, nil, 2, 3, 1}, {nil, nil, nil, 2, nil, 1}, {nil, nil, nil, nil, 1, 3}},
   998  	},
   999  	{
  1000  		description:     "single column DESC with nulls on the right RIGHT OUTER JOIN",
  1001  		joinType:        sqlbase.RightOuterJoin,
  1002  		leftTypes:       []*types.T{types.Int},
  1003  		rightTypes:      []*types.T{types.Int},
  1004  		leftDirections:  []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_DESC},
  1005  		rightDirections: []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_DESC},
  1006  		leftTuples:      tuples{{1}},
  1007  		rightTuples:     tuples{{1}, {1}, {1}, {nil}, {nil}, {nil}},
  1008  		leftOutCols:     []uint32{0},
  1009  		rightOutCols:    []uint32{0},
  1010  		leftEqCols:      []uint32{0},
  1011  		rightEqCols:     []uint32{0},
  1012  		expected:        tuples{{1, 1}, {1, 1}, {1, 1}, {nil, nil}, {nil, nil}, {nil, nil}},
  1013  	},
  1014  	{
  1015  		description:  "basic FULL OUTER JOIN test, L and R exhausted at the same time",
  1016  		joinType:     sqlbase.FullOuterJoin,
  1017  		leftTypes:    []*types.T{types.Int},
  1018  		rightTypes:   []*types.T{types.Int},
  1019  		leftTuples:   tuples{{-1}, {2}, {3}, {4}, {4}},
  1020  		rightTuples:  tuples{{1}, {2}, {3}, {4}, {4}},
  1021  		leftOutCols:  []uint32{0},
  1022  		rightOutCols: []uint32{0},
  1023  		leftEqCols:   []uint32{0},
  1024  		rightEqCols:  []uint32{0},
  1025  		expected:     tuples{{-1, nil}, {nil, 1}, {2, 2}, {3, 3}, {4, 4}, {4, 4}, {4, 4}, {4, 4}},
  1026  	},
  1027  	{
  1028  		description:  "basic FULL OUTER JOIN test, R exhausted first",
  1029  		joinType:     sqlbase.FullOuterJoin,
  1030  		leftTypes:    []*types.T{types.Int},
  1031  		rightTypes:   []*types.T{types.Int},
  1032  		leftTuples:   tuples{{1}, {1}, {3}, {5}, {6}, {7}},
  1033  		rightTuples:  tuples{{2}, {3}, {4}},
  1034  		leftOutCols:  []uint32{0},
  1035  		rightOutCols: []uint32{0},
  1036  		leftEqCols:   []uint32{0},
  1037  		rightEqCols:  []uint32{0},
  1038  		expected:     tuples{{1, nil}, {1, nil}, {nil, 2}, {3, 3}, {nil, 4}, {5, nil}, {6, nil}, {7, nil}},
  1039  	},
  1040  	{
  1041  		description:  "basic FULL OUTER JOIN test, L exhausted first",
  1042  		joinType:     sqlbase.FullOuterJoin,
  1043  		leftTypes:    []*types.T{types.Int},
  1044  		rightTypes:   []*types.T{types.Int},
  1045  		leftTuples:   tuples{{3}, {5}, {6}, {7}},
  1046  		rightTuples:  tuples{{2}, {3}, {4}, {6}, {8}, {9}},
  1047  		leftOutCols:  []uint32{0},
  1048  		rightOutCols: []uint32{0},
  1049  		leftEqCols:   []uint32{0},
  1050  		rightEqCols:  []uint32{0},
  1051  		expected:     tuples{{nil, 2}, {3, 3}, {nil, 4}, {5, nil}, {6, 6}, {7, nil}, {nil, 8}, {nil, 9}},
  1052  	},
  1053  	{
  1054  		description:  "multi output column FULL OUTER JOIN test with nulls",
  1055  		joinType:     sqlbase.FullOuterJoin,
  1056  		leftTypes:    []*types.T{types.Int, types.Int},
  1057  		rightTypes:   []*types.T{types.Int, types.Int},
  1058  		leftTuples:   tuples{{1, nil}, {3, 13}, {4, 14}},
  1059  		rightTuples:  tuples{{1, 10}, {2, 20}, {3, nil}, {4, 40}},
  1060  		leftOutCols:  []uint32{0, 1},
  1061  		rightOutCols: []uint32{0, 1},
  1062  		leftEqCols:   []uint32{0},
  1063  		rightEqCols:  []uint32{0},
  1064  		expected:     tuples{{1, nil, 1, 10}, {nil, nil, 2, 20}, {3, 13, 3, nil}, {4, 14, 4, 40}},
  1065  	},
  1066  	{
  1067  		description:  "null in equality column FULL OUTER JOIN",
  1068  		joinType:     sqlbase.FullOuterJoin,
  1069  		leftTypes:    []*types.T{types.Int, types.Int},
  1070  		rightTypes:   []*types.T{types.Int},
  1071  		leftTuples:   tuples{{nil, 1}, {1, 1}, {2, 2}, {3, 3}},
  1072  		rightTuples:  tuples{{nil}, {nil}, {1}, {3}},
  1073  		leftOutCols:  []uint32{0, 1},
  1074  		rightOutCols: []uint32{0},
  1075  		leftEqCols:   []uint32{0},
  1076  		rightEqCols:  []uint32{0},
  1077  		expected:     tuples{{nil, 1, nil}, {nil, nil, nil}, {nil, nil, nil}, {1, 1, 1}, {2, 2, nil}, {3, 3, 3}},
  1078  	},
  1079  	{
  1080  		description:  "multi equality column FULL OUTER JOIN test with nulls",
  1081  		joinType:     sqlbase.FullOuterJoin,
  1082  		leftTypes:    []*types.T{types.Int, types.Int},
  1083  		rightTypes:   []*types.T{types.Int, types.Int},
  1084  		leftTuples:   tuples{{nil, nil}, {nil, 10}, {1, nil}, {1, nil}, {2, 20}, {3, 30}},
  1085  		rightTuples:  tuples{{nil, nil}, {nil, 10}, {1, nil}, {1, 10}, {2, 20}, {4, 40}},
  1086  		leftOutCols:  []uint32{0, 1},
  1087  		rightOutCols: []uint32{0, 1},
  1088  		leftEqCols:   []uint32{0, 1},
  1089  		rightEqCols:  []uint32{0, 1},
  1090  		expected:     tuples{{nil, nil, nil, nil}, {nil, nil, nil, nil}, {nil, 10, nil, nil}, {nil, nil, nil, 10}, {1, nil, nil, nil}, {1, nil, nil, nil}, {nil, nil, 1, nil}, {nil, nil, 1, 10}, {2, 20, 2, 20}, {3, 30, nil, nil}, {nil, nil, 4, 40}},
  1091  	},
  1092  	{
  1093  		description:  "multi equality column (long runs on right) FULL OUTER JOIN test with nulls",
  1094  		joinType:     sqlbase.FullOuterJoin,
  1095  		leftTypes:    []*types.T{types.Int, types.Int},
  1096  		rightTypes:   []*types.T{types.Int, types.Int},
  1097  		leftTuples:   tuples{{1, 8}, {1, 11}, {1, 11}, {2, 21}, {2, 23}},
  1098  		rightTuples:  tuples{{1, 9}, {1, 10}, {1, 10}, {1, 11}, {2, 20}, {2, 20}, {2, 21}, {2, 22}, {2, 22}},
  1099  		leftOutCols:  []uint32{0, 1},
  1100  		rightOutCols: []uint32{0, 1},
  1101  		leftEqCols:   []uint32{0, 1},
  1102  		rightEqCols:  []uint32{0, 1},
  1103  		expected:     tuples{{1, 8, nil, nil}, {nil, nil, 1, 9}, {nil, nil, 1, 10}, {nil, nil, 1, 10}, {1, 11, 1, 11}, {1, 11, 1, 11}, {nil, nil, 2, 20}, {nil, nil, 2, 20}, {2, 21, 2, 21}, {nil, nil, 2, 22}, {nil, nil, 2, 22}, {2, 23, nil, nil}},
  1104  	},
  1105  	{
  1106  		description:     "3 equality column FULL OUTER JOIN test with nulls DESC ordering",
  1107  		joinType:        sqlbase.FullOuterJoin,
  1108  		leftTypes:       []*types.T{types.Int, types.Int, types.Int},
  1109  		rightTypes:      []*types.T{types.Int, types.Int, types.Int},
  1110  		leftDirections:  []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_DESC},
  1111  		rightDirections: []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_DESC},
  1112  		leftTuples:      tuples{{4, 3, 3}, {nil, 2, nil}, {nil, 1, 3}},
  1113  		rightTuples:     tuples{{2, 3, 1}, {2, nil, 1}, {nil, 1, 3}},
  1114  		leftOutCols:     []uint32{0, 1, 2},
  1115  		rightOutCols:    []uint32{0, 1, 2},
  1116  		leftEqCols:      []uint32{0, 1, 2},
  1117  		rightEqCols:     []uint32{0, 1, 2},
  1118  		expected:        tuples{{4, 3, 3, nil, nil, nil}, {nil, nil, nil, 2, 3, 1}, {nil, nil, nil, 2, nil, 1}, {nil, 2, nil, nil, nil, nil}, {nil, 1, 3, nil, nil, nil}, {nil, nil, nil, nil, 1, 3}},
  1119  	},
  1120  	{
  1121  		description:     "3 equality column FULL OUTER JOIN test with nulls mixed ordering",
  1122  		joinType:        sqlbase.FullOuterJoin,
  1123  		leftTypes:       []*types.T{types.Int, types.Int, types.Int},
  1124  		rightTypes:      []*types.T{types.Int, types.Int, types.Int},
  1125  		leftDirections:  []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_ASC},
  1126  		rightDirections: []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_ASC, execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_DESC},
  1127  		leftTuples:      tuples{{4, 3, 3}, {nil, 2, nil}, {nil, 1, 3}},
  1128  		rightTuples:     tuples{{2, 3, 1}, {2, nil, 1}, {nil, 1, 3}},
  1129  		leftOutCols:     []uint32{0, 1, 2},
  1130  		rightOutCols:    []uint32{0, 1, 2},
  1131  		leftEqCols:      []uint32{0, 1, 2},
  1132  		rightEqCols:     []uint32{1, 2, 0},
  1133  		expected:        tuples{{4, 3, 3, nil, nil, nil}, {nil, nil, nil, 2, 3, 1}, {nil, nil, nil, 2, nil, 1}, {nil, 2, nil, nil, nil, nil}, {nil, 1, 3, nil, nil, nil}, {nil, nil, nil, nil, 1, 3}},
  1134  	},
  1135  	{
  1136  		description:     "single column DESC with nulls on the right FULL OUTER JOIN",
  1137  		joinType:        sqlbase.FullOuterJoin,
  1138  		leftTypes:       []*types.T{types.Int},
  1139  		rightTypes:      []*types.T{types.Int},
  1140  		leftDirections:  []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_DESC},
  1141  		rightDirections: []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_DESC},
  1142  		leftTuples:      tuples{{1}},
  1143  		rightTuples:     tuples{{1}, {1}, {1}, {nil}, {nil}, {nil}},
  1144  		leftOutCols:     []uint32{0},
  1145  		rightOutCols:    []uint32{0},
  1146  		leftEqCols:      []uint32{0},
  1147  		rightEqCols:     []uint32{0},
  1148  		expected:        tuples{{1, 1}, {1, 1}, {1, 1}, {nil, nil}, {nil, nil}, {nil, nil}},
  1149  	},
  1150  	{
  1151  		description:  "FULL OUTER JOIN test with nulls and Bytes",
  1152  		joinType:     sqlbase.FullOuterJoin,
  1153  		leftTypes:    []*types.T{types.Int, types.Bytes},
  1154  		rightTypes:   []*types.T{types.Int, types.Bytes},
  1155  		leftTuples:   tuples{{nil, "0"}, {1, "10"}, {2, "20"}, {3, nil}, {4, "40"}},
  1156  		rightTuples:  tuples{{1, nil}, {3, "13"}, {4, nil}},
  1157  		leftOutCols:  []uint32{0, 1},
  1158  		rightOutCols: []uint32{1},
  1159  		leftEqCols:   []uint32{0},
  1160  		rightEqCols:  []uint32{0},
  1161  		expected:     tuples{{nil, "0", nil}, {1, "10", nil}, {2, "20", nil}, {3, nil, "13"}, {4, "40", nil}},
  1162  	},
  1163  	{
  1164  		description:  "basic LEFT SEMI JOIN test, L and R exhausted at the same time",
  1165  		joinType:     sqlbase.LeftSemiJoin,
  1166  		leftTypes:    []*types.T{types.Int},
  1167  		rightTypes:   []*types.T{types.Int},
  1168  		leftTuples:   tuples{{1}, {2}, {3}, {4}, {4}},
  1169  		rightTuples:  tuples{{-1}, {2}, {3}, {4}, {4}},
  1170  		leftOutCols:  []uint32{0},
  1171  		rightOutCols: []uint32{},
  1172  		leftEqCols:   []uint32{0},
  1173  		rightEqCols:  []uint32{0},
  1174  		expected:     tuples{{2}, {3}, {4}, {4}},
  1175  	},
  1176  	{
  1177  		description:  "basic LEFT SEMI JOIN test, R exhausted first",
  1178  		joinType:     sqlbase.LeftSemiJoin,
  1179  		leftTypes:    []*types.T{types.Int},
  1180  		rightTypes:   []*types.T{types.Int},
  1181  		leftTuples:   tuples{{1}, {1}, {3}, {5}, {6}, {7}},
  1182  		rightTuples:  tuples{{2}, {3}, {4}},
  1183  		leftOutCols:  []uint32{0},
  1184  		rightOutCols: []uint32{},
  1185  		leftEqCols:   []uint32{0},
  1186  		rightEqCols:  []uint32{0},
  1187  		expected:     tuples{{3}},
  1188  	},
  1189  	{
  1190  		description:  "basic LEFT SEMI JOIN test, L exhausted first",
  1191  		joinType:     sqlbase.LeftSemiJoin,
  1192  		leftTypes:    []*types.T{types.Int},
  1193  		rightTypes:   []*types.T{types.Int},
  1194  		leftTuples:   tuples{{3}, {5}, {6}, {7}},
  1195  		rightTuples:  tuples{{2}, {3}, {3}, {3}, {4}, {6}, {8}, {9}},
  1196  		leftOutCols:  []uint32{0},
  1197  		rightOutCols: []uint32{},
  1198  		leftEqCols:   []uint32{0},
  1199  		rightEqCols:  []uint32{0},
  1200  		expected:     tuples{{3}, {6}},
  1201  	},
  1202  	{
  1203  		description:  "multi output column LEFT SEMI JOIN test with nulls",
  1204  		joinType:     sqlbase.LeftSemiJoin,
  1205  		leftTypes:    []*types.T{types.Int, types.Int},
  1206  		rightTypes:   []*types.T{types.Int, types.Int},
  1207  		leftTuples:   tuples{{1, 10}, {2, 20}, {3, nil}, {4, 40}},
  1208  		rightTuples:  tuples{{1, nil}, {3, 13}, {4, 14}},
  1209  		leftOutCols:  []uint32{0, 1},
  1210  		rightOutCols: []uint32{},
  1211  		leftEqCols:   []uint32{0},
  1212  		rightEqCols:  []uint32{0},
  1213  		expected:     tuples{{1, 10}, {3, nil}, {4, 40}},
  1214  	},
  1215  	{
  1216  		description:  "null in equality column LEFT SEMI JOIN",
  1217  		joinType:     sqlbase.LeftSemiJoin,
  1218  		leftTypes:    []*types.T{types.Int},
  1219  		rightTypes:   []*types.T{types.Int, types.Int},
  1220  		leftTuples:   tuples{{nil}, {nil}, {1}, {3}},
  1221  		rightTuples:  tuples{{nil, 1}, {1, 1}, {2, 2}, {3, 3}},
  1222  		leftOutCols:  []uint32{0},
  1223  		rightOutCols: []uint32{},
  1224  		leftEqCols:   []uint32{0},
  1225  		rightEqCols:  []uint32{0},
  1226  		expected:     tuples{{1}, {3}},
  1227  	},
  1228  	{
  1229  		description:  "multi equality column LEFT SEMI JOIN test with nulls",
  1230  		joinType:     sqlbase.LeftSemiJoin,
  1231  		leftTypes:    []*types.T{types.Int, types.Int},
  1232  		rightTypes:   []*types.T{types.Int, types.Int},
  1233  		leftTuples:   tuples{{nil, nil}, {nil, 10}, {1, nil}, {1, 10}, {2, 20}, {4, 40}},
  1234  		rightTuples:  tuples{{nil, nil}, {nil, 10}, {1, nil}, {1, nil}, {2, 20}, {3, 30}},
  1235  		leftOutCols:  []uint32{0, 1},
  1236  		rightOutCols: []uint32{},
  1237  		leftEqCols:   []uint32{0, 1},
  1238  		rightEqCols:  []uint32{0, 1},
  1239  		expected:     tuples{{2, 20}},
  1240  	},
  1241  	{
  1242  		description:  "multi equality column (long runs on left) LEFT SEMI JOIN test with nulls",
  1243  		joinType:     sqlbase.LeftSemiJoin,
  1244  		leftTypes:    []*types.T{types.Int, types.Int},
  1245  		rightTypes:   []*types.T{types.Int, types.Int},
  1246  		leftTuples:   tuples{{1, 9}, {1, 10}, {1, 10}, {1, 11}, {1, 11}, {1, 11}, {2, 20}, {2, 20}, {2, 21}, {2, 22}, {2, 22}},
  1247  		rightTuples:  tuples{{1, 8}, {1, 11}, {1, 11}, {2, 21}, {2, 23}},
  1248  		leftOutCols:  []uint32{0, 1},
  1249  		rightOutCols: []uint32{},
  1250  		leftEqCols:   []uint32{0, 1},
  1251  		rightEqCols:  []uint32{0, 1},
  1252  		expected:     tuples{{1, 11}, {1, 11}, {1, 11}, {2, 21}},
  1253  	},
  1254  	{
  1255  		description:     "3 equality column LEFT SEMI JOIN test with nulls DESC ordering",
  1256  		joinType:        sqlbase.LeftSemiJoin,
  1257  		leftTypes:       []*types.T{types.Int, types.Int, types.Int},
  1258  		rightTypes:      []*types.T{types.Int, types.Int, types.Int},
  1259  		leftDirections:  []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_DESC},
  1260  		rightDirections: []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_DESC},
  1261  		leftTuples:      tuples{{2, 3, 1}, {2, nil, 1}, {nil, 1, 3}},
  1262  		rightTuples:     tuples{{4, 3, 3}, {nil, 2, nil}, {nil, 1, 3}},
  1263  		leftOutCols:     []uint32{0, 1, 2},
  1264  		rightOutCols:    []uint32{},
  1265  		leftEqCols:      []uint32{0, 1, 2},
  1266  		rightEqCols:     []uint32{0, 1, 2},
  1267  		expected:        tuples{},
  1268  		// The expected output here is empty, so will it be during the all nulls
  1269  		// injection, so we want to skip that.
  1270  		skipAllNullsInjection: true,
  1271  	},
  1272  	{
  1273  		description:     "3 equality column LEFT SEMI JOIN test with nulls mixed ordering",
  1274  		joinType:        sqlbase.LeftSemiJoin,
  1275  		leftTypes:       []*types.T{types.Int, types.Int, types.Int},
  1276  		rightTypes:      []*types.T{types.Int, types.Int, types.Int},
  1277  		leftDirections:  []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_ASC},
  1278  		rightDirections: []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_ASC, execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_DESC},
  1279  		leftTuples:      tuples{{2, 3, 1}, {2, nil, 1}, {nil, 1, 3}},
  1280  		rightTuples:     tuples{{4, 3, 3}, {nil, 2, nil}, {nil, 1, 3}},
  1281  		leftOutCols:     []uint32{0, 1, 2},
  1282  		rightOutCols:    []uint32{},
  1283  		leftEqCols:      []uint32{0, 1, 2},
  1284  		rightEqCols:     []uint32{1, 2, 0},
  1285  		expected:        tuples{},
  1286  		// The expected output here is empty, so will it be during the all nulls
  1287  		// injection, so we want to skip that.
  1288  		skipAllNullsInjection: true,
  1289  	},
  1290  	{
  1291  		description:     "single column DESC with nulls on the left LEFT SEMI JOIN",
  1292  		joinType:        sqlbase.LeftSemiJoin,
  1293  		leftTypes:       []*types.T{types.Int},
  1294  		rightTypes:      []*types.T{types.Int},
  1295  		leftDirections:  []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_DESC},
  1296  		rightDirections: []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_DESC},
  1297  		leftTuples:      tuples{{1}, {1}, {1}, {nil}, {nil}, {nil}},
  1298  		rightTuples:     tuples{{1}},
  1299  		leftOutCols:     []uint32{0},
  1300  		rightOutCols:    []uint32{},
  1301  		leftEqCols:      []uint32{0},
  1302  		rightEqCols:     []uint32{0},
  1303  		expected:        tuples{{1}, {1}, {1}},
  1304  	},
  1305  	{
  1306  		description:  "basic LEFT ANTI JOIN test, L and R exhausted at the same time",
  1307  		joinType:     sqlbase.LeftAntiJoin,
  1308  		leftTypes:    []*types.T{types.Int},
  1309  		rightTypes:   []*types.T{types.Int},
  1310  		leftTuples:   tuples{{1}, {2}, {3}, {4}, {4}},
  1311  		rightTuples:  tuples{{-1}, {2}, {4}, {4}},
  1312  		leftOutCols:  []uint32{0},
  1313  		rightOutCols: []uint32{},
  1314  		leftEqCols:   []uint32{0},
  1315  		rightEqCols:  []uint32{0},
  1316  		expected:     tuples{{1}, {3}},
  1317  	},
  1318  	{
  1319  		description:  "basic LEFT ANTI JOIN test, R exhausted first",
  1320  		joinType:     sqlbase.LeftAntiJoin,
  1321  		leftTypes:    []*types.T{types.Int},
  1322  		rightTypes:   []*types.T{types.Int},
  1323  		leftTuples:   tuples{{1}, {1}, {3}, {5}, {6}, {7}},
  1324  		rightTuples:  tuples{{2}, {3}, {4}},
  1325  		leftOutCols:  []uint32{0},
  1326  		rightOutCols: []uint32{},
  1327  		leftEqCols:   []uint32{0},
  1328  		rightEqCols:  []uint32{0},
  1329  		expected:     tuples{{1}, {1}, {5}, {6}, {7}},
  1330  	},
  1331  	{
  1332  		description:  "basic LEFT ANTI JOIN test, L exhausted first",
  1333  		joinType:     sqlbase.LeftAntiJoin,
  1334  		leftTypes:    []*types.T{types.Int},
  1335  		rightTypes:   []*types.T{types.Int},
  1336  		leftTuples:   tuples{{3}, {5}, {6}, {7}},
  1337  		rightTuples:  tuples{{2}, {3}, {3}, {3}, {4}, {6}, {8}, {9}},
  1338  		leftOutCols:  []uint32{0},
  1339  		rightOutCols: []uint32{},
  1340  		leftEqCols:   []uint32{0},
  1341  		rightEqCols:  []uint32{0},
  1342  		expected:     tuples{{5}, {7}},
  1343  	},
  1344  	{
  1345  		description:  "multi output column LEFT ANTI JOIN test with nulls",
  1346  		joinType:     sqlbase.LeftAntiJoin,
  1347  		leftTypes:    []*types.T{types.Int, types.Int},
  1348  		rightTypes:   []*types.T{types.Int, types.Int},
  1349  		leftTuples:   tuples{{1, 10}, {2, 20}, {3, nil}, {4, 40}},
  1350  		rightTuples:  tuples{{1, nil}, {3, 13}, {4, 14}},
  1351  		leftOutCols:  []uint32{0, 1},
  1352  		rightOutCols: []uint32{},
  1353  		leftEqCols:   []uint32{0},
  1354  		rightEqCols:  []uint32{0},
  1355  		expected:     tuples{{2, 20}},
  1356  	},
  1357  	{
  1358  		description:  "null in equality column LEFT ANTI JOIN",
  1359  		joinType:     sqlbase.LeftAntiJoin,
  1360  		leftTypes:    []*types.T{types.Int},
  1361  		rightTypes:   []*types.T{types.Int, types.Int},
  1362  		leftTuples:   tuples{{nil}, {nil}, {1}, {3}},
  1363  		rightTuples:  tuples{{nil, 1}, {1, 1}, {2, 2}, {3, 3}},
  1364  		leftOutCols:  []uint32{0},
  1365  		rightOutCols: []uint32{},
  1366  		leftEqCols:   []uint32{0},
  1367  		rightEqCols:  []uint32{0},
  1368  		expected:     tuples{{nil}, {nil}},
  1369  	},
  1370  	{
  1371  		description:  "multi equality column LEFT ANTI JOIN test with nulls",
  1372  		joinType:     sqlbase.LeftAntiJoin,
  1373  		leftTypes:    []*types.T{types.Int, types.Int},
  1374  		rightTypes:   []*types.T{types.Int, types.Int},
  1375  		leftTuples:   tuples{{nil, nil}, {nil, 10}, {1, nil}, {1, 10}, {2, 20}, {4, 40}},
  1376  		rightTuples:  tuples{{nil, nil}, {nil, 10}, {1, nil}, {1, nil}, {2, 20}, {3, 30}},
  1377  		leftOutCols:  []uint32{0, 1},
  1378  		rightOutCols: []uint32{},
  1379  		leftEqCols:   []uint32{0, 1},
  1380  		rightEqCols:  []uint32{0, 1},
  1381  		expected:     tuples{{nil, nil}, {nil, 10}, {1, nil}, {1, 10}, {4, 40}},
  1382  	},
  1383  	{
  1384  		description:  "multi equality column (long runs on left) LEFT ANTI JOIN test with nulls",
  1385  		joinType:     sqlbase.LeftAntiJoin,
  1386  		leftTypes:    []*types.T{types.Int, types.Int},
  1387  		rightTypes:   []*types.T{types.Int, types.Int},
  1388  		leftTuples:   tuples{{1, 9}, {1, 10}, {1, 10}, {1, 11}, {1, 11}, {1, 11}, {2, 20}, {2, 20}, {2, 21}, {2, 22}, {2, 22}},
  1389  		rightTuples:  tuples{{1, 8}, {1, 11}, {1, 11}, {2, 21}, {2, 23}},
  1390  		leftOutCols:  []uint32{0, 1},
  1391  		rightOutCols: []uint32{},
  1392  		leftEqCols:   []uint32{0, 1},
  1393  		rightEqCols:  []uint32{0, 1},
  1394  		expected:     tuples{{1, 9}, {1, 10}, {1, 10}, {2, 20}, {2, 20}, {2, 22}, {2, 22}},
  1395  	},
  1396  	{
  1397  		description:     "3 equality column LEFT ANTI JOIN test with nulls DESC ordering",
  1398  		joinType:        sqlbase.LeftAntiJoin,
  1399  		leftTypes:       []*types.T{types.Int, types.Int, types.Int},
  1400  		rightTypes:      []*types.T{types.Int, types.Int, types.Int},
  1401  		leftDirections:  []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_DESC},
  1402  		rightDirections: []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_DESC},
  1403  		leftTuples:      tuples{{2, 3, 1}, {2, nil, 1}, {nil, 1, 3}},
  1404  		rightTuples:     tuples{{4, 3, 3}, {nil, 2, nil}, {nil, 1, 3}},
  1405  		leftOutCols:     []uint32{0, 1, 2},
  1406  		rightOutCols:    []uint32{},
  1407  		leftEqCols:      []uint32{0, 1, 2},
  1408  		rightEqCols:     []uint32{0, 1, 2},
  1409  		expected:        tuples{{2, 3, 1}, {2, nil, 1}, {nil, 1, 3}},
  1410  	},
  1411  	{
  1412  		description:     "3 equality column LEFT ANTI JOIN test with nulls mixed ordering",
  1413  		joinType:        sqlbase.LeftAntiJoin,
  1414  		leftTypes:       []*types.T{types.Int, types.Int, types.Int},
  1415  		rightTypes:      []*types.T{types.Int, types.Int, types.Int},
  1416  		leftDirections:  []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_ASC},
  1417  		rightDirections: []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_ASC, execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_DESC},
  1418  		leftTuples:      tuples{{2, 3, 1}, {2, nil, 1}, {nil, 1, 3}},
  1419  		rightTuples:     tuples{{4, 3, 3}, {nil, 2, nil}, {nil, 1, 3}},
  1420  		leftOutCols:     []uint32{0, 1, 2},
  1421  		rightOutCols:    []uint32{},
  1422  		leftEqCols:      []uint32{0, 1, 2},
  1423  		rightEqCols:     []uint32{1, 2, 0},
  1424  		expected:        tuples{{2, 3, 1}, {2, nil, 1}, {nil, 1, 3}},
  1425  	},
  1426  	{
  1427  		description:     "single column DESC with nulls on the left LEFT ANTI JOIN",
  1428  		joinType:        sqlbase.LeftAntiJoin,
  1429  		leftTypes:       []*types.T{types.Int},
  1430  		rightTypes:      []*types.T{types.Int},
  1431  		leftDirections:  []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_DESC},
  1432  		rightDirections: []execinfrapb.Ordering_Column_Direction{execinfrapb.Ordering_Column_DESC},
  1433  		leftTuples:      tuples{{1}, {1}, {1}, {nil}, {nil}, {nil}},
  1434  		rightTuples:     tuples{{1}, {nil}},
  1435  		leftOutCols:     []uint32{0},
  1436  		rightOutCols:    []uint32{},
  1437  		leftEqCols:      []uint32{0},
  1438  		rightEqCols:     []uint32{0},
  1439  		expected:        tuples{{nil}, {nil}, {nil}},
  1440  	},
  1441  	{
  1442  		description:  "INNER JOIN test with ON expression (filter only on left)",
  1443  		joinType:     sqlbase.InnerJoin,
  1444  		leftTypes:    []*types.T{types.Int, types.Int},
  1445  		rightTypes:   []*types.T{types.Int, types.Int},
  1446  		leftTuples:   tuples{{nil, 0}, {1, 10}, {2, 20}, {3, nil}, {4, 40}},
  1447  		rightTuples:  tuples{{1, nil}, {3, 13}, {4, 14}},
  1448  		leftOutCols:  []uint32{0, 1},
  1449  		rightOutCols: []uint32{},
  1450  		leftEqCols:   []uint32{0},
  1451  		rightEqCols:  []uint32{0},
  1452  		onExpr:       execinfrapb.Expression{Expr: "@1 < 4"},
  1453  		expected:     tuples{{1, 10}, {3, nil}},
  1454  	},
  1455  	{
  1456  		description:  "INNER JOIN test with ON expression (filter only on right)",
  1457  		joinType:     sqlbase.InnerJoin,
  1458  		leftTypes:    []*types.T{types.Int, types.Int},
  1459  		rightTypes:   []*types.T{types.Int, types.Int},
  1460  		leftTuples:   tuples{{nil, 0}, {1, 10}, {2, 20}, {3, nil}, {4, 40}},
  1461  		rightTuples:  tuples{{1, nil}, {3, 13}, {4, 14}},
  1462  		leftOutCols:  []uint32{0, 1},
  1463  		rightOutCols: []uint32{},
  1464  		leftEqCols:   []uint32{0},
  1465  		rightEqCols:  []uint32{0},
  1466  		onExpr:       execinfrapb.Expression{Expr: "@4 < 14"},
  1467  		expected:     tuples{{3, nil}},
  1468  	},
  1469  	{
  1470  		description:  "INNER JOIN test with ON expression (filter on both)",
  1471  		joinType:     sqlbase.InnerJoin,
  1472  		leftTypes:    []*types.T{types.Int, types.Int},
  1473  		rightTypes:   []*types.T{types.Int, types.Int},
  1474  		leftTuples:   tuples{{nil, 0}, {1, 10}, {2, 20}, {3, nil}, {4, 40}},
  1475  		rightTuples:  tuples{{1, nil}, {3, 13}, {4, 14}},
  1476  		leftOutCols:  []uint32{0, 1},
  1477  		rightOutCols: []uint32{},
  1478  		leftEqCols:   []uint32{0},
  1479  		rightEqCols:  []uint32{0},
  1480  		onExpr:       execinfrapb.Expression{Expr: "@2 + @3 < 50"},
  1481  		expected:     tuples{{1, 10}, {4, 40}},
  1482  	},
  1483  	{
  1484  		description:  "INTERSECT ALL join basic",
  1485  		joinType:     sqlbase.IntersectAllJoin,
  1486  		leftTypes:    []*types.T{types.Int},
  1487  		rightTypes:   []*types.T{types.Int},
  1488  		leftTuples:   tuples{{1}, {1}, {2}, {2}, {3}},
  1489  		rightTuples:  tuples{{1}, {2}, {2}, {3}, {3}, {3}},
  1490  		leftOutCols:  []uint32{0},
  1491  		rightOutCols: []uint32{},
  1492  		leftEqCols:   []uint32{0},
  1493  		rightEqCols:  []uint32{0},
  1494  		expected:     tuples{{1}, {2}, {2}, {3}},
  1495  	},
  1496  	{
  1497  		description:  "INTERSECT ALL join with mixed ordering with NULLs",
  1498  		joinType:     sqlbase.IntersectAllJoin,
  1499  		leftTypes:    []*types.T{types.Int, types.Int},
  1500  		rightTypes:   []*types.T{types.Int, types.Int},
  1501  		leftTuples:   tuples{{4, nil}, {4, 1}, {1, nil}, {1, 2}, {0, 2}, {0, 3}, {nil, 1}, {nil, 2}, {nil, 2}, {nil, 3}},
  1502  		rightTuples:  tuples{{3, 2}, {2, 1}, {2, 2}, {2, 3}, {1, nil}, {1, 1}, {1, 1}, {0, 1}, {0, 2}, {nil, 2}},
  1503  		leftOutCols:  []uint32{0, 1},
  1504  		rightOutCols: []uint32{},
  1505  		leftEqCols:   []uint32{0, 1},
  1506  		leftDirections: []execinfrapb.Ordering_Column_Direction{
  1507  			execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_ASC,
  1508  		},
  1509  		rightEqCols: []uint32{0, 1},
  1510  		rightDirections: []execinfrapb.Ordering_Column_Direction{
  1511  			execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_ASC,
  1512  		},
  1513  		expected: tuples{{1, nil}, {0, 2}, {nil, 2}},
  1514  	},
  1515  	{
  1516  		description:  "INTERSECT ALL join with mixed ordering with NULLs on the left",
  1517  		joinType:     sqlbase.IntersectAllJoin,
  1518  		leftTypes:    []*types.T{types.Int, types.Int},
  1519  		rightTypes:   []*types.T{types.Int, types.Int},
  1520  		leftTuples:   tuples{{nil, 3}, {nil, nil}, {9, 6}, {9, 0}, {9, nil}},
  1521  		rightTuples:  tuples{{0, 5}, {0, 4}, {8, 8}, {8, 6}, {9, 0}},
  1522  		leftOutCols:  []uint32{0, 1},
  1523  		rightOutCols: []uint32{},
  1524  		leftEqCols:   []uint32{0, 1},
  1525  		leftDirections: []execinfrapb.Ordering_Column_Direction{
  1526  			execinfrapb.Ordering_Column_ASC, execinfrapb.Ordering_Column_DESC,
  1527  		},
  1528  		rightEqCols: []uint32{0, 1},
  1529  		rightDirections: []execinfrapb.Ordering_Column_Direction{
  1530  			execinfrapb.Ordering_Column_ASC, execinfrapb.Ordering_Column_DESC,
  1531  		},
  1532  		expected: tuples{{9, 0}},
  1533  	},
  1534  	{
  1535  		description:  "INTERSECT ALL join on booleans",
  1536  		joinType:     sqlbase.IntersectAllJoin,
  1537  		leftTypes:    []*types.T{types.Bool},
  1538  		rightTypes:   []*types.T{types.Bool},
  1539  		leftTuples:   tuples{{nil}, {nil}, {false}, {false}, {true}, {true}, {true}},
  1540  		rightTuples:  tuples{{nil}, {false}, {false}, {false}, {false}, {true}},
  1541  		leftOutCols:  []uint32{0},
  1542  		rightOutCols: []uint32{},
  1543  		leftEqCols:   []uint32{0},
  1544  		rightEqCols:  []uint32{0},
  1545  		expected:     tuples{{nil}, {false}, {false}, {true}},
  1546  	},
  1547  	{
  1548  		description:  "EXCEPT ALL join basic",
  1549  		joinType:     sqlbase.ExceptAllJoin,
  1550  		leftTypes:    []*types.T{types.Int},
  1551  		rightTypes:   []*types.T{types.Int},
  1552  		leftTuples:   tuples{{1}, {1}, {2}, {2}, {2}, {3}, {3}},
  1553  		rightTuples:  tuples{{1}, {2}, {3}, {3}, {3}},
  1554  		leftOutCols:  []uint32{0},
  1555  		rightOutCols: []uint32{},
  1556  		leftEqCols:   []uint32{0},
  1557  		rightEqCols:  []uint32{0},
  1558  		expected:     tuples{{1}, {2}, {2}},
  1559  	},
  1560  	{
  1561  		description:  "EXCEPT ALL join with mixed ordering with NULLs",
  1562  		joinType:     sqlbase.ExceptAllJoin,
  1563  		leftTypes:    []*types.T{types.Int, types.Int},
  1564  		rightTypes:   []*types.T{types.Int, types.Int},
  1565  		leftTuples:   tuples{{4, nil}, {4, 1}, {1, nil}, {1, 2}, {0, 2}, {0, 3}, {nil, 1}, {nil, 2}, {nil, 2}, {nil, 3}},
  1566  		rightTuples:  tuples{{3, 2}, {2, 1}, {2, 2}, {2, 3}, {1, nil}, {1, 1}, {1, 1}, {0, 1}, {0, 2}, {nil, 2}},
  1567  		leftOutCols:  []uint32{0, 1},
  1568  		rightOutCols: []uint32{},
  1569  		leftEqCols:   []uint32{0, 1},
  1570  		leftDirections: []execinfrapb.Ordering_Column_Direction{
  1571  			execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_ASC,
  1572  		},
  1573  		rightEqCols: []uint32{0, 1},
  1574  		rightDirections: []execinfrapb.Ordering_Column_Direction{
  1575  			execinfrapb.Ordering_Column_DESC, execinfrapb.Ordering_Column_ASC,
  1576  		},
  1577  		expected: tuples{{4, nil}, {4, 1}, {1, 2}, {0, 3}, {nil, 1}, {nil, 2}, {nil, 3}},
  1578  	},
  1579  	{
  1580  		description:  "EXCEPT ALL join with mixed ordering with NULLs on the left",
  1581  		joinType:     sqlbase.ExceptAllJoin,
  1582  		leftTypes:    []*types.T{types.Int, types.Int},
  1583  		rightTypes:   []*types.T{types.Int, types.Int},
  1584  		leftTuples:   tuples{{nil, 3}, {nil, nil}, {9, 6}, {9, 0}, {9, nil}},
  1585  		rightTuples:  tuples{{0, 5}, {0, 4}, {8, 8}, {8, 6}, {9, 0}},
  1586  		leftOutCols:  []uint32{0, 1},
  1587  		rightOutCols: []uint32{},
  1588  		leftEqCols:   []uint32{0, 1},
  1589  		leftDirections: []execinfrapb.Ordering_Column_Direction{
  1590  			execinfrapb.Ordering_Column_ASC, execinfrapb.Ordering_Column_DESC,
  1591  		},
  1592  		rightEqCols: []uint32{0, 1},
  1593  		rightDirections: []execinfrapb.Ordering_Column_Direction{
  1594  			execinfrapb.Ordering_Column_ASC, execinfrapb.Ordering_Column_DESC,
  1595  		},
  1596  		expected: tuples{{nil, 3}, {nil, nil}, {9, 6}, {9, nil}},
  1597  	},
  1598  	{
  1599  		description:  "EXCEPT ALL join on booleans",
  1600  		joinType:     sqlbase.ExceptAllJoin,
  1601  		leftTypes:    []*types.T{types.Bool},
  1602  		rightTypes:   []*types.T{types.Bool},
  1603  		leftTuples:   tuples{{nil}, {nil}, {false}, {false}, {true}, {true}, {true}},
  1604  		rightTuples:  tuples{{nil}, {false}, {false}, {false}, {false}, {true}},
  1605  		leftOutCols:  []uint32{0},
  1606  		rightOutCols: []uint32{},
  1607  		leftEqCols:   []uint32{0},
  1608  		rightEqCols:  []uint32{0},
  1609  		expected:     tuples{{nil}, {true}, {true}},
  1610  	},
  1611  }
  1612  
  1613  func TestMergeJoiner(t *testing.T) {
  1614  	defer leaktest.AfterTest(t)()
  1615  	ctx := context.Background()
  1616  	st := cluster.MakeTestingClusterSettings()
  1617  	evalCtx := tree.MakeTestingEvalContext(st)
  1618  	defer evalCtx.Stop(ctx)
  1619  	flowCtx := &execinfra.FlowCtx{
  1620  		EvalCtx: &evalCtx,
  1621  		Cfg: &execinfra.ServerConfig{
  1622  			Settings:    st,
  1623  			DiskMonitor: testDiskMonitor,
  1624  		},
  1625  	}
  1626  	queueCfg, cleanup := colcontainerutils.NewTestingDiskQueueCfg(t, true /* inMem */)
  1627  	defer cleanup()
  1628  	var (
  1629  		accounts []*mon.BoundAccount
  1630  		monitors []*mon.BytesMonitor
  1631  	)
  1632  	for _, tc := range mjTestCases {
  1633  		for _, tc := range tc.mutateTypes() {
  1634  			tc.init()
  1635  
  1636  			// We use a custom verifier function so that we can get the merge join op
  1637  			// to use a custom output batch size per test, to exercise more cases.
  1638  			var mergeJoinVerifier verifierFn = func(output *opTestOutput) error {
  1639  				if mj, ok := output.input.(variableOutputBatchSizeInitializer); ok {
  1640  					mj.initWithOutputBatchSize(tc.outputBatchSize)
  1641  				} else {
  1642  					// When we have an inner join with ON expression, a filter operator
  1643  					// will be put on top of the merge join, so to make life easier, we'll
  1644  					// just ignore the requested output batch size.
  1645  					output.input.Init()
  1646  				}
  1647  				verify := output.Verify
  1648  				if _, isFullOuter := output.input.(*mergeJoinFullOuterOp); isFullOuter {
  1649  					// FULL OUTER JOIN doesn't guarantee any ordering on its output (since
  1650  					// it is ambiguous), so we're comparing the outputs as sets.
  1651  					verify = output.VerifyAnyOrder
  1652  				}
  1653  
  1654  				return verify()
  1655  			}
  1656  
  1657  			var runner testRunner
  1658  			if tc.skipAllNullsInjection {
  1659  				// We're omitting all nulls injection test. See comments for each such
  1660  				// test case.
  1661  				runner = runTestsWithoutAllNullsInjection
  1662  			} else {
  1663  				runner = runTestsWithTyps
  1664  			}
  1665  			// We test all cases with the default memory limit (regular scenario) and a
  1666  			// limit of 1 byte (to force the buffered groups to spill to disk).
  1667  			for _, memoryLimit := range []int64{1, defaultMemoryLimit} {
  1668  				t.Run(fmt.Sprintf("MemoryLimit=%s/%s", humanizeutil.IBytes(memoryLimit), tc.description), func(t *testing.T) {
  1669  					runner(t, []tuples{tc.leftTuples, tc.rightTuples},
  1670  						[][]*types.T{tc.leftTypes, tc.rightTypes},
  1671  						tc.expected, mergeJoinVerifier,
  1672  						func(input []colexecbase.Operator) (colexecbase.Operator, error) {
  1673  							spec := createSpecForMergeJoiner(tc)
  1674  							args := NewColOperatorArgs{
  1675  								Spec:                spec,
  1676  								Inputs:              input,
  1677  								StreamingMemAccount: testMemAcc,
  1678  								DiskQueueCfg:        queueCfg,
  1679  								FDSemaphore:         colexecbase.NewTestingSemaphore(mjFDLimit),
  1680  							}
  1681  							args.TestingKnobs.UseStreamingMemAccountForBuffering = true
  1682  							flowCtx.Cfg.TestingKnobs.MemoryLimitBytes = memoryLimit
  1683  							result, err := NewColOperator(ctx, flowCtx, args)
  1684  							if err != nil {
  1685  								return nil, err
  1686  							}
  1687  							accounts = append(accounts, result.OpAccounts...)
  1688  							monitors = append(monitors, result.OpMonitors...)
  1689  							return result.Op, nil
  1690  						})
  1691  				})
  1692  			}
  1693  		}
  1694  	}
  1695  	for _, acc := range accounts {
  1696  		acc.Close(ctx)
  1697  	}
  1698  	for _, mon := range monitors {
  1699  		mon.Stop(ctx)
  1700  	}
  1701  }
  1702  
  1703  // Merge joiner will be using two spillingQueues, and each of them will use
  1704  // 2 file descriptors.
  1705  const mjFDLimit = 4
  1706  
  1707  // TestFullOuterMergeJoinWithMaximumNumberOfGroups will create two input
  1708  // sources such that the left one contains rows with even numbers 0, 2, 4, ...
  1709  // while the right contains one rows with odd numbers 1, 3, 5, ... The test
  1710  // will perform FULL OUTER JOIN. Such setup will create the maximum number of
  1711  // groups per batch.
  1712  func TestFullOuterMergeJoinWithMaximumNumberOfGroups(t *testing.T) {
  1713  	defer leaktest.AfterTest(t)()
  1714  	ctx := context.Background()
  1715  	nTuples := coldata.BatchSize() * 4
  1716  	queueCfg, cleanup := colcontainerutils.NewTestingDiskQueueCfg(t, true /* inMem */)
  1717  	defer cleanup()
  1718  	for _, outBatchSize := range []int{1, 16, coldata.BatchSize() - 1, coldata.BatchSize(), coldata.BatchSize() + 1} {
  1719  		t.Run(fmt.Sprintf("outBatchSize=%d", outBatchSize),
  1720  			func(t *testing.T) {
  1721  				typs := []*types.T{types.Int}
  1722  				colsLeft := []coldata.Vec{testAllocator.NewMemColumn(typs[0], nTuples)}
  1723  				colsRight := []coldata.Vec{testAllocator.NewMemColumn(typs[0], nTuples)}
  1724  				groupsLeft := colsLeft[0].Int64()
  1725  				groupsRight := colsRight[0].Int64()
  1726  				for i := range groupsLeft {
  1727  					groupsLeft[i] = int64(i * 2)
  1728  					groupsRight[i] = int64(i*2 + 1)
  1729  				}
  1730  				leftSource := newChunkingBatchSource(typs, colsLeft, nTuples)
  1731  				rightSource := newChunkingBatchSource(typs, colsRight, nTuples)
  1732  				a, err := newMergeJoinOp(
  1733  					testAllocator, defaultMemoryLimit, queueCfg,
  1734  					colexecbase.NewTestingSemaphore(mjFDLimit), sqlbase.FullOuterJoin,
  1735  					leftSource, rightSource, typs, typs,
  1736  					[]execinfrapb.Ordering_Column{{ColIdx: 0, Direction: execinfrapb.Ordering_Column_ASC}},
  1737  					[]execinfrapb.Ordering_Column{{ColIdx: 0, Direction: execinfrapb.Ordering_Column_ASC}},
  1738  					testDiskAcc,
  1739  				)
  1740  				if err != nil {
  1741  					t.Fatal("error in merge join op constructor", err)
  1742  				}
  1743  				a.(*mergeJoinFullOuterOp).initWithOutputBatchSize(outBatchSize)
  1744  				i, count, expVal := 0, 0, int64(0)
  1745  				for b := a.Next(ctx); b.Length() != 0; b = a.Next(ctx) {
  1746  					count += b.Length()
  1747  					leftOutCol := b.ColVec(0).Int64()
  1748  					leftNulls := b.ColVec(0).Nulls()
  1749  					rightOutCol := b.ColVec(1).Int64()
  1750  					rightNulls := b.ColVec(1).Nulls()
  1751  					for j := 0; j < b.Length(); j++ {
  1752  						leftVal := leftOutCol[j]
  1753  						leftNull := leftNulls.NullAt(j)
  1754  						rightVal := rightOutCol[j]
  1755  						rightNull := rightNulls.NullAt(j)
  1756  						if expVal%2 == 0 {
  1757  							// It is an even-numbered row, so the left value should contain
  1758  							// expVal and the right value should be NULL.
  1759  							if leftVal != expVal || leftNull || !rightNull {
  1760  								t.Fatalf("found left = %d, left NULL? = %t, right NULL? = %t, "+
  1761  									"expected left = %d, left NULL? = false, right NULL? = true, idx %d of batch %d",
  1762  									leftVal, leftNull, rightNull, expVal, j, i)
  1763  							}
  1764  						} else {
  1765  							// It is an odd-numbered row, so the right value should contain
  1766  							// expVal and the left value should be NULL.
  1767  							if rightVal != expVal || rightNull || !leftNull {
  1768  								t.Fatalf("found right = %d, right NULL? = %t, left NULL? = %t, "+
  1769  									"expected right = %d, right NULL? = false, left NULL? = true, idx %d of batch %d",
  1770  									rightVal, rightNull, leftNull, expVal, j, i)
  1771  							}
  1772  						}
  1773  						expVal++
  1774  					}
  1775  					i++
  1776  				}
  1777  				if count != 2*nTuples {
  1778  					t.Fatalf("found count %d, expected count %d", count, 2*nTuples)
  1779  				}
  1780  			})
  1781  	}
  1782  }
  1783  
  1784  // TestMergeJoinCrossProduct verifies that the merge joiner produces the same
  1785  // output as the hash joiner. The test aims at stressing randomly the building
  1786  // of cross product (from the buffered groups) in the merge joiner and does it
  1787  // by creating input sources such that they contain very big groups (each group
  1788  // is about coldata.BatchSize() in size) which will force the merge joiner to
  1789  // mostly build from the buffered groups. Join of such input sources results in
  1790  // an output quadratic in size, so the test is skipped unless coldata.BatchSize
  1791  // is set to relatively small number, but it is ok since we randomize this
  1792  // value.
  1793  func TestMergeJoinCrossProduct(t *testing.T) {
  1794  	defer leaktest.AfterTest(t)()
  1795  	if coldata.BatchSize() > 200 {
  1796  		t.Skipf("this test is too slow with relatively big batch size")
  1797  	}
  1798  	ctx := context.Background()
  1799  	nTuples := 2*coldata.BatchSize() + 1
  1800  	queueCfg, cleanup := colcontainerutils.NewTestingDiskQueueCfg(t, true /* inMem */)
  1801  	defer cleanup()
  1802  	rng, _ := randutil.NewPseudoRand()
  1803  	for _, outBatchSize := range []int{1, 17, coldata.BatchSize() - 1, coldata.BatchSize(), coldata.BatchSize() + 1} {
  1804  		t.Run(fmt.Sprintf("outBatchSize=%d", outBatchSize),
  1805  			func(t *testing.T) {
  1806  				typs := []*types.T{types.Int, types.Bytes, types.Decimal}
  1807  				colsLeft := make([]coldata.Vec, len(typs))
  1808  				colsRight := make([]coldata.Vec, len(typs))
  1809  				for i, typ := range typs {
  1810  					colsLeft[i] = testAllocator.NewMemColumn(typ, nTuples)
  1811  					colsRight[i] = testAllocator.NewMemColumn(typ, nTuples)
  1812  				}
  1813  				groupsLeft := colsLeft[0].Int64()
  1814  				groupsRight := colsRight[0].Int64()
  1815  				leftGroupIdx, rightGroupIdx := 0, 0
  1816  				for i := range groupsLeft {
  1817  					if rng.Float64() < 1.0/float64(coldata.BatchSize()) {
  1818  						leftGroupIdx++
  1819  					}
  1820  					if rng.Float64() < 1.0/float64(coldata.BatchSize()) {
  1821  						rightGroupIdx++
  1822  					}
  1823  					groupsLeft[i] = int64(leftGroupIdx)
  1824  					groupsRight[i] = int64(rightGroupIdx)
  1825  				}
  1826  				for i := range typs[1:] {
  1827  					for _, vecs := range [][]coldata.Vec{colsLeft, colsRight} {
  1828  						coldatatestutils.RandomVec(coldatatestutils.RandomVecArgs{
  1829  							Rand:            rng,
  1830  							Vec:             vecs[i+1],
  1831  							N:               nTuples,
  1832  							NullProbability: nullProbability,
  1833  						})
  1834  					}
  1835  				}
  1836  				leftMJSource := newChunkingBatchSource(typs, colsLeft, nTuples)
  1837  				rightMJSource := newChunkingBatchSource(typs, colsRight, nTuples)
  1838  				leftHJSource := newChunkingBatchSource(typs, colsLeft, nTuples)
  1839  				rightHJSource := newChunkingBatchSource(typs, colsRight, nTuples)
  1840  				mj, err := newMergeJoinOp(
  1841  					testAllocator, defaultMemoryLimit, queueCfg,
  1842  					colexecbase.NewTestingSemaphore(mjFDLimit), sqlbase.InnerJoin,
  1843  					leftMJSource, rightMJSource, typs, typs,
  1844  					[]execinfrapb.Ordering_Column{{ColIdx: 0, Direction: execinfrapb.Ordering_Column_ASC}},
  1845  					[]execinfrapb.Ordering_Column{{ColIdx: 0, Direction: execinfrapb.Ordering_Column_ASC}},
  1846  					testDiskAcc,
  1847  				)
  1848  				if err != nil {
  1849  					t.Fatal("error in merge join op constructor", err)
  1850  				}
  1851  				mj.(*mergeJoinInnerOp).initWithOutputBatchSize(outBatchSize)
  1852  				hj := newHashJoiner(
  1853  					testAllocator, hashJoinerSpec{
  1854  						joinType: sqlbase.InnerJoin,
  1855  						left: hashJoinerSourceSpec{
  1856  							eqCols: []uint32{0}, sourceTypes: typs,
  1857  						},
  1858  						right: hashJoinerSourceSpec{
  1859  							eqCols: []uint32{0}, sourceTypes: typs,
  1860  						},
  1861  					}, leftHJSource, rightHJSource)
  1862  				hj.Init()
  1863  
  1864  				var mjOutputTuples, hjOutputTuples tuples
  1865  				for b := mj.Next(ctx); b.Length() != 0; b = mj.Next(ctx) {
  1866  					for i := 0; i < b.Length(); i++ {
  1867  						mjOutputTuples = append(mjOutputTuples, getTupleFromBatch(b, i))
  1868  					}
  1869  				}
  1870  				for b := hj.Next(ctx); b.Length() != 0; b = hj.Next(ctx) {
  1871  					for i := 0; i < b.Length(); i++ {
  1872  						hjOutputTuples = append(hjOutputTuples, getTupleFromBatch(b, i))
  1873  					}
  1874  				}
  1875  				err = assertTuplesSetsEqual(hjOutputTuples, mjOutputTuples)
  1876  				// Note that the error message can be extremely verbose (it
  1877  				// might contain all output tuples), so we manually check that
  1878  				// comparing err to nil returns true (if we were to use
  1879  				// require.NoError, then the error message would be output).
  1880  				require.True(t, err == nil)
  1881  			})
  1882  
  1883  	}
  1884  }
  1885  
  1886  // TestMergeJoinerMultiBatch creates one long input of a 1:1 join, and keeps
  1887  // track of the expected output to make sure the join output is batched
  1888  // correctly.
  1889  func TestMergeJoinerMultiBatch(t *testing.T) {
  1890  	defer leaktest.AfterTest(t)()
  1891  	ctx := context.Background()
  1892  	queueCfg, cleanup := colcontainerutils.NewTestingDiskQueueCfg(t, true /* inMem */)
  1893  	defer cleanup()
  1894  	for _, numInputBatches := range []int{1, 2, 16} {
  1895  		for _, outBatchSize := range []int{1, 16, coldata.BatchSize()} {
  1896  			t.Run(fmt.Sprintf("numInputBatches=%d", numInputBatches),
  1897  				func(t *testing.T) {
  1898  					nTuples := coldata.BatchSize() * numInputBatches
  1899  					typs := []*types.T{types.Int}
  1900  					cols := []coldata.Vec{testAllocator.NewMemColumn(typs[0], nTuples)}
  1901  					groups := cols[0].Int64()
  1902  					for i := range groups {
  1903  						groups[i] = int64(i)
  1904  					}
  1905  
  1906  					leftSource := newChunkingBatchSource(typs, cols, nTuples)
  1907  					rightSource := newChunkingBatchSource(typs, cols, nTuples)
  1908  
  1909  					a, err := newMergeJoinOp(
  1910  						testAllocator, defaultMemoryLimit,
  1911  						queueCfg, colexecbase.NewTestingSemaphore(mjFDLimit), sqlbase.InnerJoin,
  1912  						leftSource, rightSource, typs, typs,
  1913  						[]execinfrapb.Ordering_Column{{ColIdx: 0, Direction: execinfrapb.Ordering_Column_ASC}},
  1914  						[]execinfrapb.Ordering_Column{{ColIdx: 0, Direction: execinfrapb.Ordering_Column_ASC}},
  1915  						testDiskAcc,
  1916  					)
  1917  					if err != nil {
  1918  						t.Fatal("error in merge join op constructor", err)
  1919  					}
  1920  
  1921  					a.(*mergeJoinInnerOp).initWithOutputBatchSize(outBatchSize)
  1922  
  1923  					i := 0
  1924  					count := 0
  1925  					// Keep track of the last comparison value.
  1926  					expVal := int64(0)
  1927  					for b := a.Next(ctx); b.Length() != 0; b = a.Next(ctx) {
  1928  						count += b.Length()
  1929  						outCol := b.ColVec(0).Int64()
  1930  						for j := int64(0); j < int64(b.Length()); j++ {
  1931  							outVal := outCol[j]
  1932  							if outVal != expVal {
  1933  								t.Fatalf("found val %d, expected %d, idx %d of batch %d",
  1934  									outVal, expVal, j, i)
  1935  							}
  1936  							expVal++
  1937  						}
  1938  						i++
  1939  					}
  1940  					if count != nTuples {
  1941  						t.Fatalf("found count %d, expected count %d", count, nTuples)
  1942  					}
  1943  				})
  1944  		}
  1945  	}
  1946  }
  1947  
  1948  // TestMergeJoinerMultiBatchRuns creates one long input of a n:n join, and
  1949  // keeps track of the expected count to make sure the join output is batched
  1950  // correctly.
  1951  func TestMergeJoinerMultiBatchRuns(t *testing.T) {
  1952  	defer leaktest.AfterTest(t)()
  1953  	ctx := context.Background()
  1954  	queueCfg, cleanup := colcontainerutils.NewTestingDiskQueueCfg(t, true /* inMem */)
  1955  	defer cleanup()
  1956  	for _, groupSize := range []int{coldata.BatchSize() / 8, coldata.BatchSize() / 4, coldata.BatchSize() / 2} {
  1957  		if groupSize == 0 {
  1958  			// We might be varying coldata.BatchSize() so that when it is divided by
  1959  			// 4, groupSize is 0. We want to skip such configuration.
  1960  			continue
  1961  		}
  1962  		for _, numInputBatches := range []int{1, 2, 16} {
  1963  			t.Run(fmt.Sprintf("groupSize=%d/numInputBatches=%d", groupSize, numInputBatches),
  1964  				func(t *testing.T) {
  1965  					nTuples := coldata.BatchSize() * numInputBatches
  1966  					// There will be nTuples/groupSize "full" groups - i.e. groups of
  1967  					// groupSize. Each of these "full" groups will produce groupSize^2
  1968  					// tuples. The last group might be not full and will consist of
  1969  					// nTuples % groupSize tuples. That group will produce
  1970  					// lastGroupSize^2 tuples.
  1971  					// Note that the math will still be correct in case when nTuples is
  1972  					// divisible by groupSize - all the groups will be full and "last"
  1973  					// group will be of size 0.
  1974  					lastGroupSize := nTuples % groupSize
  1975  					expCount := nTuples/groupSize*(groupSize*groupSize) + lastGroupSize*lastGroupSize
  1976  					typs := []*types.T{types.Int, types.Int}
  1977  					cols := []coldata.Vec{
  1978  						testAllocator.NewMemColumn(typs[0], nTuples),
  1979  						testAllocator.NewMemColumn(typs[1], nTuples),
  1980  					}
  1981  					for i := range cols[0].Int64() {
  1982  						cols[0].Int64()[i] = int64(i / groupSize)
  1983  						cols[1].Int64()[i] = int64(i / groupSize)
  1984  					}
  1985  
  1986  					leftSource := newChunkingBatchSource(typs, cols, nTuples)
  1987  					rightSource := newChunkingBatchSource(typs, cols, nTuples)
  1988  
  1989  					a, err := newMergeJoinOp(
  1990  						testAllocator, defaultMemoryLimit,
  1991  						queueCfg, colexecbase.NewTestingSemaphore(mjFDLimit), sqlbase.InnerJoin,
  1992  						leftSource, rightSource, typs, typs,
  1993  						[]execinfrapb.Ordering_Column{{ColIdx: 0, Direction: execinfrapb.Ordering_Column_ASC}, {ColIdx: 1, Direction: execinfrapb.Ordering_Column_ASC}},
  1994  						[]execinfrapb.Ordering_Column{{ColIdx: 0, Direction: execinfrapb.Ordering_Column_ASC}, {ColIdx: 1, Direction: execinfrapb.Ordering_Column_ASC}},
  1995  						testDiskAcc,
  1996  					)
  1997  					if err != nil {
  1998  						t.Fatal("error in merge join op constructor", err)
  1999  					}
  2000  
  2001  					a.(*mergeJoinInnerOp).Init()
  2002  
  2003  					i := 0
  2004  					count := 0
  2005  					// Keep track of the last comparison value.
  2006  					lastVal := int64(0)
  2007  					for b := a.Next(ctx); b.Length() != 0; b = a.Next(ctx) {
  2008  						count += b.Length()
  2009  						outCol := b.ColVec(0).Int64()
  2010  						for j := int64(0); j < int64(b.Length()); j++ {
  2011  							outVal := outCol[j]
  2012  							expVal := lastVal / int64(groupSize*groupSize)
  2013  							if outVal != expVal {
  2014  								t.Fatalf("found val %d, expected %d, idx %d of batch %d",
  2015  									outVal, expVal, j, i)
  2016  							}
  2017  							lastVal++
  2018  						}
  2019  						i++
  2020  					}
  2021  
  2022  					if count != expCount {
  2023  						t.Fatalf("found count %d, expected count %d",
  2024  							count, expCount)
  2025  					}
  2026  				})
  2027  		}
  2028  	}
  2029  }
  2030  
  2031  func min(a, b int) int {
  2032  	if a < b {
  2033  		return a
  2034  	}
  2035  	return b
  2036  }
  2037  
  2038  type expectedGroup struct {
  2039  	val         int64
  2040  	cardinality int
  2041  }
  2042  
  2043  func newBatchesOfRandIntRows(
  2044  	nTuples int, maxRunLength int64, skipValues bool, randomIncrement int64,
  2045  ) ([]coldata.Vec, []coldata.Vec, []expectedGroup) {
  2046  	rng, _ := randutil.NewPseudoRand()
  2047  	lCols := []coldata.Vec{testAllocator.NewMemColumn(types.Int, nTuples)}
  2048  	lCol := lCols[0].Int64()
  2049  	rCols := []coldata.Vec{testAllocator.NewMemColumn(types.Int, nTuples)}
  2050  	rCol := rCols[0].Int64()
  2051  	exp := make([]expectedGroup, nTuples)
  2052  	val := int64(0)
  2053  	lIdx, rIdx := 0, 0
  2054  	i := 0
  2055  	for lIdx < nTuples && rIdx < nTuples {
  2056  		// Randomly increment the value to write to the group.
  2057  		val += 1 + randomIncrement*rng.Int63n(256)
  2058  		// Determine whether or not to create a group.
  2059  		if skipValues && rng.Int63n(4) == 0 {
  2060  			lCol[lIdx] = val
  2061  			lIdx++
  2062  			// Randomly increment the value to write to the group.
  2063  			val += 1 + rng.Int63n(16)
  2064  			rCol[rIdx] = val
  2065  			rIdx++
  2066  		} else {
  2067  			lGroupSize := min(int(rng.Int63n(maxRunLength)+1), nTuples-lIdx)
  2068  			rGroupSize := min(int(rng.Int63n(maxRunLength)+1), nTuples-rIdx)
  2069  
  2070  			for j := 0; j < lGroupSize; j++ {
  2071  				lCol[lIdx] = val
  2072  				lIdx++
  2073  			}
  2074  
  2075  			for j := 0; j < rGroupSize; j++ {
  2076  				rCol[rIdx] = val
  2077  				rIdx++
  2078  			}
  2079  
  2080  			exp[i] = expectedGroup{val, lGroupSize * rGroupSize}
  2081  			i++
  2082  		}
  2083  	}
  2084  
  2085  	if lIdx < nTuples {
  2086  		for lIdx < nTuples {
  2087  			lCol[lIdx] = val + 1
  2088  			lIdx++
  2089  		}
  2090  	}
  2091  
  2092  	if rIdx < nTuples {
  2093  		for rIdx < nTuples {
  2094  			rCol[rIdx] = val + 1
  2095  			rIdx++
  2096  		}
  2097  	}
  2098  
  2099  	return lCols, rCols, exp
  2100  }
  2101  
  2102  func TestMergeJoinerRandomized(t *testing.T) {
  2103  	defer leaktest.AfterTest(t)()
  2104  	ctx := context.Background()
  2105  	queueCfg, cleanup := colcontainerutils.NewTestingDiskQueueCfg(t, true /* inMem */)
  2106  	defer cleanup()
  2107  	for _, numInputBatches := range []int{1, 2, 16, 256} {
  2108  		for _, maxRunLength := range []int64{2, 3, 100} {
  2109  			for _, skipValues := range []bool{false, true} {
  2110  				for _, randomIncrement := range []int64{0, 1} {
  2111  					t.Run(fmt.Sprintf("numInputBatches=%d/maxRunLength=%d/skipValues=%t/randomIncrement=%d", numInputBatches, maxRunLength, skipValues, randomIncrement),
  2112  						func(t *testing.T) {
  2113  							nTuples := coldata.BatchSize() * numInputBatches
  2114  							typs := []*types.T{types.Int}
  2115  							lCols, rCols, exp := newBatchesOfRandIntRows(nTuples, maxRunLength, skipValues, randomIncrement)
  2116  							leftSource := newChunkingBatchSource(typs, lCols, nTuples)
  2117  							rightSource := newChunkingBatchSource(typs, rCols, nTuples)
  2118  
  2119  							a, err := newMergeJoinOp(
  2120  								testAllocator, defaultMemoryLimit,
  2121  								queueCfg, colexecbase.NewTestingSemaphore(mjFDLimit), sqlbase.InnerJoin,
  2122  								leftSource, rightSource, typs, typs,
  2123  								[]execinfrapb.Ordering_Column{{ColIdx: 0, Direction: execinfrapb.Ordering_Column_ASC}},
  2124  								[]execinfrapb.Ordering_Column{{ColIdx: 0, Direction: execinfrapb.Ordering_Column_ASC}},
  2125  								testDiskAcc,
  2126  							)
  2127  
  2128  							if err != nil {
  2129  								t.Fatal("error in merge join op constructor", err)
  2130  							}
  2131  
  2132  							a.(*mergeJoinInnerOp).Init()
  2133  
  2134  							i := 0
  2135  							count := 0
  2136  							cpIdx := 0
  2137  							for b := a.Next(ctx); b.Length() != 0; b = a.Next(ctx) {
  2138  								count += b.Length()
  2139  								outCol := b.ColVec(0).Int64()
  2140  								for j := 0; j < b.Length(); j++ {
  2141  									outVal := outCol[j]
  2142  
  2143  									if exp[cpIdx].cardinality == 0 {
  2144  										cpIdx++
  2145  									}
  2146  									expVal := exp[cpIdx].val
  2147  									exp[cpIdx].cardinality--
  2148  									if expVal != outVal {
  2149  										t.Fatalf("found val %d, expected %d, idx %d of batch %d",
  2150  											outVal, expVal, j, i)
  2151  									}
  2152  								}
  2153  								i++
  2154  							}
  2155  						})
  2156  				}
  2157  			}
  2158  		}
  2159  	}
  2160  }
  2161  
  2162  func newBatchOfIntRows(nCols int, batch coldata.Batch) coldata.Batch {
  2163  	for colIdx := 0; colIdx < nCols; colIdx++ {
  2164  		col := batch.ColVec(colIdx).Int64()
  2165  		for i := 0; i < coldata.BatchSize(); i++ {
  2166  			col[i] = int64(i)
  2167  		}
  2168  	}
  2169  
  2170  	batch.SetLength(coldata.BatchSize())
  2171  
  2172  	for colIdx := 0; colIdx < nCols; colIdx++ {
  2173  		vec := batch.ColVec(colIdx)
  2174  		vec.Nulls().UnsetNulls()
  2175  	}
  2176  	return batch
  2177  }
  2178  
  2179  func newBatchOfRepeatedIntRows(nCols int, batch coldata.Batch, numRepeats int) coldata.Batch {
  2180  	for colIdx := 0; colIdx < nCols; colIdx++ {
  2181  		col := batch.ColVec(colIdx).Int64()
  2182  		for i := 0; i < coldata.BatchSize(); i++ {
  2183  			col[i] = int64((i + 1) / numRepeats)
  2184  		}
  2185  	}
  2186  
  2187  	batch.SetLength(coldata.BatchSize())
  2188  
  2189  	for colIdx := 0; colIdx < nCols; colIdx++ {
  2190  		vec := batch.ColVec(colIdx)
  2191  		vec.Nulls().UnsetNulls()
  2192  	}
  2193  	return batch
  2194  }
  2195  
  2196  func BenchmarkMergeJoiner(b *testing.B) {
  2197  	ctx := context.Background()
  2198  	nCols := 4
  2199  	sourceTypes := make([]*types.T, nCols)
  2200  
  2201  	for colIdx := 0; colIdx < nCols; colIdx++ {
  2202  		sourceTypes[colIdx] = types.Int
  2203  	}
  2204  
  2205  	batch := testAllocator.NewMemBatch(sourceTypes)
  2206  	queueCfg, cleanup := colcontainerutils.NewTestingDiskQueueCfg(b, false /* inMem */)
  2207  	defer cleanup()
  2208  	benchMemAccount := testMemMonitor.MakeBoundAccount()
  2209  	defer benchMemAccount.Close(ctx)
  2210  
  2211  	// 1:1 join.
  2212  	for _, nBatches := range []int{1, 4, 16, 1024} {
  2213  		b.Run(fmt.Sprintf("rows=%d", nBatches*coldata.BatchSize()), func(b *testing.B) {
  2214  			// 8 (bytes / int64) * nBatches (number of batches) * col.BatchSize() (rows /
  2215  			// batch) * nCols (number of columns / row) * 2 (number of sources).
  2216  			b.SetBytes(int64(8 * nBatches * coldata.BatchSize() * nCols * 2))
  2217  			b.ResetTimer()
  2218  			for i := 0; i < b.N; i++ {
  2219  				leftSource := newFiniteBatchSource(newBatchOfIntRows(nCols, batch), sourceTypes, nBatches)
  2220  				rightSource := newFiniteBatchSource(newBatchOfIntRows(nCols, batch), sourceTypes, nBatches)
  2221  
  2222  				benchMemAccount.Clear(ctx)
  2223  				base, err := newMergeJoinBase(
  2224  					colmem.NewAllocator(ctx, &benchMemAccount, testColumnFactory), defaultMemoryLimit, queueCfg, colexecbase.NewTestingSemaphore(mjFDLimit),
  2225  					sqlbase.InnerJoin, leftSource, rightSource, sourceTypes, sourceTypes,
  2226  					[]execinfrapb.Ordering_Column{{ColIdx: 0, Direction: execinfrapb.Ordering_Column_ASC}},
  2227  					[]execinfrapb.Ordering_Column{{ColIdx: 0, Direction: execinfrapb.Ordering_Column_ASC}},
  2228  					testDiskAcc,
  2229  				)
  2230  				require.NoError(b, err)
  2231  				s := mergeJoinInnerOp{mergeJoinBase: base}
  2232  				s.Init()
  2233  
  2234  				b.StartTimer()
  2235  				for b := s.Next(ctx); b.Length() != 0; b = s.Next(ctx) {
  2236  				}
  2237  				b.StopTimer()
  2238  			}
  2239  		})
  2240  	}
  2241  
  2242  	// Groups on left side.
  2243  	for _, nBatches := range []int{1, 4, 16, 1024} {
  2244  		b.Run(fmt.Sprintf("oneSideRepeat-rows=%d", nBatches*coldata.BatchSize()), func(b *testing.B) {
  2245  			// 8 (bytes / int64) * nBatches (number of batches) * col.BatchSize() (rows /
  2246  			// batch) * nCols (number of columns / row) * 2 (number of sources).
  2247  			b.SetBytes(int64(8 * nBatches * coldata.BatchSize() * nCols * 2))
  2248  			b.ResetTimer()
  2249  			for i := 0; i < b.N; i++ {
  2250  				leftSource := newFiniteBatchSource(newBatchOfRepeatedIntRows(nCols, batch, nBatches), sourceTypes, nBatches)
  2251  				rightSource := newFiniteBatchSource(newBatchOfIntRows(nCols, batch), sourceTypes, nBatches)
  2252  
  2253  				benchMemAccount.Clear(ctx)
  2254  				base, err := newMergeJoinBase(
  2255  					colmem.NewAllocator(ctx, &benchMemAccount, testColumnFactory), defaultMemoryLimit, queueCfg, colexecbase.NewTestingSemaphore(mjFDLimit),
  2256  					sqlbase.InnerJoin, leftSource, rightSource, sourceTypes, sourceTypes,
  2257  					[]execinfrapb.Ordering_Column{{ColIdx: 0, Direction: execinfrapb.Ordering_Column_ASC}},
  2258  					[]execinfrapb.Ordering_Column{{ColIdx: 0, Direction: execinfrapb.Ordering_Column_ASC}},
  2259  					testDiskAcc,
  2260  				)
  2261  				require.NoError(b, err)
  2262  				s := mergeJoinInnerOp{mergeJoinBase: base}
  2263  				s.Init()
  2264  
  2265  				b.StartTimer()
  2266  				for b := s.Next(ctx); b.Length() != 0; b = s.Next(ctx) {
  2267  				}
  2268  				b.StopTimer()
  2269  			}
  2270  		})
  2271  	}
  2272  
  2273  	// Groups on both sides.
  2274  	for _, nBatches := range []int{1, 4, 16, 32} {
  2275  		numRepeats := nBatches
  2276  		b.Run(fmt.Sprintf("bothSidesRepeat-rows=%d", nBatches*coldata.BatchSize()), func(b *testing.B) {
  2277  
  2278  			// 8 (bytes / int64) * nBatches (number of batches) * col.BatchSize() (rows /
  2279  			// batch) * nCols (number of columns / row) * 2 (number of sources).
  2280  			b.SetBytes(int64(8 * nBatches * coldata.BatchSize() * nCols * 2))
  2281  			b.ResetTimer()
  2282  			for i := 0; i < b.N; i++ {
  2283  				leftSource := newFiniteBatchSource(newBatchOfRepeatedIntRows(nCols, batch, numRepeats), sourceTypes, nBatches)
  2284  				rightSource := newFiniteBatchSource(newBatchOfRepeatedIntRows(nCols, batch, numRepeats), sourceTypes, nBatches)
  2285  
  2286  				benchMemAccount.Clear(ctx)
  2287  				base, err := newMergeJoinBase(
  2288  					colmem.NewAllocator(ctx, &benchMemAccount, testColumnFactory), defaultMemoryLimit, queueCfg, colexecbase.NewTestingSemaphore(mjFDLimit),
  2289  					sqlbase.InnerJoin, leftSource, rightSource, sourceTypes, sourceTypes,
  2290  					[]execinfrapb.Ordering_Column{{ColIdx: 0, Direction: execinfrapb.Ordering_Column_ASC}},
  2291  					[]execinfrapb.Ordering_Column{{ColIdx: 0, Direction: execinfrapb.Ordering_Column_ASC}},
  2292  					testDiskAcc,
  2293  				)
  2294  				require.NoError(b, err)
  2295  				s := mergeJoinInnerOp{mergeJoinBase: base}
  2296  				s.Init()
  2297  
  2298  				b.StartTimer()
  2299  				for b := s.Next(ctx); b.Length() != 0; b = s.Next(ctx) {
  2300  				}
  2301  				b.StopTimer()
  2302  			}
  2303  		})
  2304  	}
  2305  
  2306  }