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