github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/rowexec/interleaved_reader_joiner_test.go (about) 1 // Copyright 2017 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package rowexec 12 13 import ( 14 "context" 15 "fmt" 16 "strconv" 17 "testing" 18 19 "github.com/cockroachdb/cockroach/pkg/base" 20 "github.com/cockroachdb/cockroach/pkg/keys" 21 "github.com/cockroachdb/cockroach/pkg/kv" 22 "github.com/cockroachdb/cockroach/pkg/roachpb" 23 "github.com/cockroachdb/cockroach/pkg/sql/execinfra" 24 "github.com/cockroachdb/cockroach/pkg/sql/execinfrapb" 25 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" 26 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 27 "github.com/cockroachdb/cockroach/pkg/sql/sqlbase" 28 "github.com/cockroachdb/cockroach/pkg/testutils/distsqlutils" 29 "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" 30 "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" 31 "github.com/cockroachdb/cockroach/pkg/util/encoding" 32 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 33 "github.com/cockroachdb/cockroach/pkg/util/tracing" 34 ) 35 36 // min and max are inclusive bounds on the root table's ID. 37 // If min and/or max is -1, then no bound is used for that endpoint. 38 func makeSpanWithRootBound(desc *sqlbase.TableDescriptor, min int, max int) roachpb.Span { 39 keyPrefix := sqlbase.MakeIndexKeyPrefix(keys.SystemSQLCodec, desc, desc.PrimaryIndex.ID) 40 41 startKey := roachpb.Key(append([]byte(nil), keyPrefix...)) 42 if min != -1 { 43 startKey = encoding.EncodeUvarintAscending(startKey, uint64(min)) 44 } 45 endKey := roachpb.Key(append([]byte(nil), keyPrefix...)) 46 if max != -1 { 47 endKey = encoding.EncodeUvarintAscending(endKey, uint64(max)) 48 } 49 // endKey needs to be exclusive. 50 endKey = endKey.PrefixEnd() 51 52 return roachpb.Span{Key: startKey, EndKey: endKey} 53 } 54 55 func TestInterleavedReaderJoiner(t *testing.T) { 56 defer leaktest.AfterTest(t)() 57 ctx := context.Background() 58 59 s, sqlDB, kvDB := serverutils.StartServer(t, base.TestServerArgs{}) 60 defer s.Stopper().Stop(ctx) 61 62 aFn := func(row int) tree.Datum { 63 return tree.NewDInt(tree.DInt(row % 10)) 64 } 65 66 bFn := func(row int) tree.Datum { 67 return tree.NewDInt(tree.DInt(row / 10)) 68 } 69 70 sqlutils.CreateTable(t, sqlDB, "parent", 71 "id INT PRIMARY KEY, a INT", 72 30, 73 sqlutils.ToRowFn(sqlutils.RowIdxFn, aFn), 74 ) 75 // Child table has 3 rows for the first two parent rows and 2 rows for every 76 // other parent row. 77 sqlutils.CreateTableInterleaved(t, sqlDB, "child1", 78 "pid INT, id INT, b INT, PRIMARY KEY (pid, id)", 79 "parent (pid)", 80 62, 81 sqlutils.ToRowFn(sqlutils.RowModuloShiftedFn(30), sqlutils.RowIdxFn, bFn), 82 ) 83 // Create another table also interleaved into parent with 1 or 0 rows 84 // interleaved into each parent row. 85 sqlutils.CreateTableInterleaved(t, sqlDB, "child2", 86 "pid INT, id INT, s STRING, PRIMARY KEY (pid, id), INDEX (s)", 87 "parent (pid)", 88 15, 89 sqlutils.ToRowFn(sqlutils.RowModuloShiftedFn(30), sqlutils.RowIdxFn, sqlutils.RowEnglishFn), 90 ) 91 r := sqlutils.MakeSQLRunner(sqlDB) 92 // Insert additional rows into child1 not interleaved in parent. 93 r.Exec(t, fmt.Sprintf(`INSERT INTO %s.child1 VALUES 94 (-1, -1, 0), 95 (0, 0, 0), 96 (63, 63, 6), 97 (70, 70, 7) 98 `, sqlutils.TestDB)) 99 100 // Create child table that do not correspond to a parent row to 101 // exercise OUTER joins. 102 sqlutils.CreateTableInterleaved(t, sqlDB, "child3", 103 "pid INT, id INT, s STRING, PRIMARY KEY (pid, id), INDEX (s)", 104 "parent (pid)", 105 0, 106 sqlutils.ToRowFn(), 107 ) 108 r.Exec(t, fmt.Sprintf(`INSERT INTO %s.child3 VALUES 109 (-1, -1, '-1'), 110 (-1, -101, '-101'), 111 (0, 0, '0'), 112 (1, 1, '1'), 113 (3, 3, '3'), 114 (3, 103, '103'), 115 (5, 5, '5'), 116 (31, 31, '31'), 117 (31, 131, '131'), 118 (32, 32, '32') 119 `, sqlutils.TestDB)) 120 121 pd := sqlbase.GetTableDescriptor(kvDB, keys.SystemSQLCodec, sqlutils.TestDB, "parent") 122 cd1 := sqlbase.GetTableDescriptor(kvDB, keys.SystemSQLCodec, sqlutils.TestDB, "child1") 123 cd2 := sqlbase.GetTableDescriptor(kvDB, keys.SystemSQLCodec, sqlutils.TestDB, "child2") 124 cd3 := sqlbase.GetTableDescriptor(kvDB, keys.SystemSQLCodec, sqlutils.TestDB, "child3") 125 126 // InterleavedReaderJoiner specs for each parent-child combination used 127 // throughout the test cases. 128 // They are copied and/or modified via closures for each test case. 129 // For example, pdCd1Spec is the spec for full table INNER JOIN between 130 // parent and child1. 131 pdCd1Spec := execinfrapb.InterleavedReaderJoinerSpec{ 132 Tables: []execinfrapb.InterleavedReaderJoinerSpec_Table{ 133 { 134 Desc: *pd, 135 Ordering: execinfrapb.Ordering{Columns: []execinfrapb.Ordering_Column{{ColIdx: 0, Direction: execinfrapb.Ordering_Column_ASC}}}, 136 Spans: []execinfrapb.TableReaderSpan{{Span: pd.PrimaryIndexSpan(keys.SystemSQLCodec)}}, 137 }, 138 { 139 Desc: *cd1, 140 Ordering: execinfrapb.Ordering{Columns: []execinfrapb.Ordering_Column{{ColIdx: 0, Direction: execinfrapb.Ordering_Column_ASC}}}, 141 Spans: []execinfrapb.TableReaderSpan{{Span: cd1.PrimaryIndexSpan(keys.SystemSQLCodec)}}, 142 }, 143 }, 144 Type: sqlbase.InnerJoin, 145 } 146 147 copySpec := func(spec execinfrapb.InterleavedReaderJoinerSpec) execinfrapb.InterleavedReaderJoinerSpec { 148 spec.Tables = append([]execinfrapb.InterleavedReaderJoinerSpec_Table(nil), spec.Tables...) 149 return spec 150 } 151 152 pdCd2Spec := copySpec(pdCd1Spec) 153 pdCd2Spec.Tables[1].Desc = *cd2 154 pdCd2Spec.Tables[1].Spans = []execinfrapb.TableReaderSpan{{Span: cd2.PrimaryIndexSpan(keys.SystemSQLCodec)}} 155 pdCd3Spec := copySpec(pdCd1Spec) 156 pdCd3Spec.Tables[1].Desc = *cd3 157 pdCd3Spec.Tables[1].Spans = []execinfrapb.TableReaderSpan{{Span: cd3.PrimaryIndexSpan(keys.SystemSQLCodec)}} 158 159 testCases := []struct { 160 spec execinfrapb.InterleavedReaderJoinerSpec 161 post execinfrapb.PostProcessSpec 162 expected string 163 }{ 164 // Simple join with a post process filter and projection. 165 { 166 spec: pdCd1Spec, 167 post: execinfrapb.PostProcessSpec{ 168 Filter: execinfrapb.Expression{Expr: "@1 <= 4 OR @3 <= 4 OR @3 > 30"}, 169 Projection: true, 170 // pd.pid1, cid1, cid2 171 OutputColumns: []uint32{0, 3, 4}, 172 }, 173 expected: "[[1 1 0] [1 31 3] [1 61 6] [2 2 0] [2 32 3] [2 62 6] [3 3 0] [3 33 3] [4 4 0] [4 34 3]]", 174 }, 175 176 // Swapped parent-child tables. 177 { 178 spec: func() execinfrapb.InterleavedReaderJoinerSpec { 179 spec := copySpec(pdCd1Spec) 180 spec.Tables[0], spec.Tables[1] = spec.Tables[1], spec.Tables[0] 181 return spec 182 }(), 183 post: execinfrapb.PostProcessSpec{ 184 Filter: execinfrapb.Expression{Expr: "@1 <= 4 OR @1 > 30"}, 185 Projection: true, 186 // pd.pid1, cid1, cid2 187 OutputColumns: []uint32{4, 1, 2}, 188 }, 189 expected: "[[1 1 0] [1 31 3] [1 61 6] [2 2 0] [2 32 3] [2 62 6] [3 3 0] [3 33 3] [4 4 0] [4 34 3]]", 190 }, 191 192 // Not specifying spans on either table should return no joined rows. 193 { 194 spec: func() execinfrapb.InterleavedReaderJoinerSpec { 195 spec := copySpec(pdCd1Spec) 196 // No spans specified for cd1 should return no rows. 197 spec.Tables[1].Spans = nil 198 return spec 199 }(), 200 expected: "[]", 201 }, 202 203 { 204 spec: func() execinfrapb.InterleavedReaderJoinerSpec { 205 spec := copySpec(pdCd1Spec) 206 // No spans specified for pd should return no rows. 207 spec.Tables[0].Spans = nil 208 return spec 209 }(), 210 expected: "[]", 211 }, 212 213 // Intermediate filters that are logically disjoint returns no 214 // joined rows. 215 { 216 spec: func() execinfrapb.InterleavedReaderJoinerSpec { 217 spec := copySpec(pdCd1Spec) 218 spec.Tables[0].Post = execinfrapb.PostProcessSpec{ 219 Filter: execinfrapb.Expression{Expr: "@1 < 4"}, 220 } 221 spec.Tables[1].Post = execinfrapb.PostProcessSpec{ 222 Filter: execinfrapb.Expression{Expr: "@1 > 4"}, 223 } 224 return spec 225 }(), 226 expected: "[]", 227 }, 228 229 // Intermediate filters restrict range of joined rows. 230 { 231 spec: func() execinfrapb.InterleavedReaderJoinerSpec { 232 spec := copySpec(pdCd2Spec) 233 spec.Tables[0].Post = execinfrapb.PostProcessSpec{ 234 Filter: execinfrapb.Expression{Expr: "@1 <= 18"}, 235 } 236 spec.Tables[1].Post = execinfrapb.PostProcessSpec{ 237 Filter: execinfrapb.Expression{Expr: "@1 >= 12"}, 238 } 239 return spec 240 }(), 241 post: execinfrapb.PostProcessSpec{ 242 Projection: true, 243 // id column of parent and child table. 244 OutputColumns: []uint32{0, 3, 4}, 245 }, 246 expected: "[[12 12 'one-two'] [13 13 'one-three'] [14 14 'one-four'] [15 15 'one-five']]", 247 }, 248 249 // Filters that are converted to spans with index constraints. 250 { 251 spec: func() execinfrapb.InterleavedReaderJoinerSpec { 252 spec := copySpec(pdCd2Spec) 253 // Filter on id <= 18. 254 spec.Tables[0].Spans = []execinfrapb.TableReaderSpan{{Span: makeSpanWithRootBound(pd, -1, 18)}} 255 // Filter on pid >= 12. 256 spec.Tables[1].Spans = []execinfrapb.TableReaderSpan{{Span: makeSpanWithRootBound(pd, 12, -1)}} 257 return spec 258 }(), 259 post: execinfrapb.PostProcessSpec{ 260 Projection: true, 261 // id column of parent and child table. 262 OutputColumns: []uint32{0, 3, 4}, 263 }, 264 expected: "[[12 12 'one-two'] [13 13 'one-three'] [14 14 'one-four'] [15 15 'one-five']]", 265 }, 266 267 // Check that even though InterleavedReaderJoiner scans all 268 // children rows, only those that fit the filter on cd2Spans 269 // (pid >= 12) are not ignored and ultimately joined. 270 { 271 spec: func() execinfrapb.InterleavedReaderJoinerSpec { 272 spec := copySpec(pdCd2Spec) 273 // Filter on pid >= 12. 274 spec.Tables[1].Spans = []execinfrapb.TableReaderSpan{{Span: makeSpanWithRootBound(pd, 12, -1)}} 275 return spec 276 }(), 277 post: execinfrapb.PostProcessSpec{ 278 // Filter on id <= 18. 279 Filter: execinfrapb.Expression{Expr: "@1 <= 18"}, 280 Projection: true, 281 // id column of parent and child table. 282 OutputColumns: []uint32{0, 3, 4}, 283 }, 284 expected: "[[12 12 'one-two'] [13 13 'one-three'] [14 14 'one-four'] [15 15 'one-five']]", 285 }, 286 287 // Intermediate projections. 288 { 289 spec: func() execinfrapb.InterleavedReaderJoinerSpec { 290 spec := copySpec(pdCd2Spec) 291 spec.Tables[0].Post = execinfrapb.PostProcessSpec{ 292 Filter: execinfrapb.Expression{Expr: "@1 <= 18"}, 293 Projection: true, 294 OutputColumns: []uint32{0}, 295 } 296 spec.Tables[1].Post = execinfrapb.PostProcessSpec{ 297 Filter: execinfrapb.Expression{Expr: "@1 >= 12"}, 298 Projection: true, 299 // Skip the primary ID of child2. 300 OutputColumns: []uint32{0, 2}, 301 } 302 return spec 303 }(), 304 expected: "[[12 12 'one-two'] [13 13 'one-three'] [14 14 'one-four'] [15 15 'one-five']]", 305 }, 306 307 // Postprocess limit. 308 { 309 spec: pdCd1Spec, 310 post: execinfrapb.PostProcessSpec{ 311 Limit: 5, 312 }, 313 expected: "[[1 1 1 1 0] [1 1 1 31 3] [1 1 1 61 6] [2 2 2 2 0] [2 2 2 32 3]]", 314 }, 315 316 // With an OnExpr. 317 { 318 spec: func() execinfrapb.InterleavedReaderJoinerSpec { 319 spec := pdCd1Spec 320 spec.OnExpr = execinfrapb.Expression{Expr: "@4 >= 60"} 321 return spec 322 }(), 323 expected: "[[1 1 1 61 6] [2 2 2 62 6] [30 0 30 60 6]]", 324 }, 325 326 // FULL OUTER joins. 327 { 328 spec: func() execinfrapb.InterleavedReaderJoinerSpec { 329 spec := pdCd3Spec 330 spec.Type = sqlbase.FullOuterJoin 331 return spec 332 }(), 333 post: execinfrapb.PostProcessSpec{ 334 Filter: execinfrapb.Expression{Expr: "@1 <= 7 OR @1 IS NOT DISTINCT FROM NULL"}, 335 }, 336 expected: `[[NULL NULL -1 -101 '-101'] [NULL NULL -1 -1 '-1'] [NULL NULL 0 0 '0'] [1 1 1 1 '1'] [2 2 NULL NULL NULL] [3 3 3 3 '3'] [3 3 3 103 '103'] [4 4 NULL NULL NULL] [5 5 5 5 '5'] [6 6 NULL NULL NULL] [7 7 NULL NULL NULL] [NULL NULL 31 31 '31'] [NULL NULL 31 131 '131'] [NULL NULL 32 32 '32']]`, 337 }, 338 339 { 340 spec: func() execinfrapb.InterleavedReaderJoinerSpec { 341 spec := copySpec(pdCd3Spec) 342 spec.Tables[0], spec.Tables[1] = spec.Tables[1], spec.Tables[0] 343 spec.Type = sqlbase.FullOuterJoin 344 return spec 345 }(), 346 post: execinfrapb.PostProcessSpec{ 347 Filter: execinfrapb.Expression{Expr: "@4 <= 7 OR @4 IS NOT DISTINCT FROM NULL"}, 348 }, 349 expected: `[[-1 -101 '-101' NULL NULL] [-1 -1 '-1' NULL NULL] [0 0 '0' NULL NULL] [1 1 '1' 1 1] [NULL NULL NULL 2 2] [3 3 '3' 3 3] [3 103 '103' 3 3] [NULL NULL NULL 4 4] [5 5 '5' 5 5] [NULL NULL NULL 6 6] [NULL NULL NULL 7 7] [31 31 '31' NULL NULL] [31 131 '131' NULL NULL] [32 32 '32' NULL NULL]]`, 350 }, 351 352 // LEFT OUTER joins. 353 { 354 spec: func() execinfrapb.InterleavedReaderJoinerSpec { 355 spec := pdCd3Spec 356 spec.Type = sqlbase.LeftOuterJoin 357 return spec 358 }(), 359 post: execinfrapb.PostProcessSpec{ 360 Filter: execinfrapb.Expression{Expr: "@1 <= 7 OR @1 IS NOT DISTINCT FROM NULL"}, 361 }, 362 expected: `[[1 1 1 1 '1'] [2 2 NULL NULL NULL] [3 3 3 3 '3'] [3 3 3 103 '103'] [4 4 NULL NULL NULL] [5 5 5 5 '5'] [6 6 NULL NULL NULL] [7 7 NULL NULL NULL]]`, 363 }, 364 { 365 spec: func() execinfrapb.InterleavedReaderJoinerSpec { 366 spec := copySpec(pdCd3Spec) 367 spec.Tables[0], spec.Tables[1] = spec.Tables[1], spec.Tables[0] 368 spec.Type = sqlbase.LeftOuterJoin 369 return spec 370 }(), 371 expected: `[[-1 -101 '-101' NULL NULL] [-1 -1 '-1' NULL NULL] [0 0 '0' NULL NULL] [1 1 '1' 1 1] [3 3 '3' 3 3] [3 103 '103' 3 3] [5 5 '5' 5 5] [31 31 '31' NULL NULL] [31 131 '131' NULL NULL] [32 32 '32' NULL NULL]]`, 372 }, 373 374 // RIGHT OUTER joins. 375 { 376 spec: func() execinfrapb.InterleavedReaderJoinerSpec { 377 spec := pdCd3Spec 378 spec.Type = sqlbase.RightOuterJoin 379 return spec 380 }(), 381 expected: `[[NULL NULL -1 -101 '-101'] [NULL NULL -1 -1 '-1'] [NULL NULL 0 0 '0'] [1 1 1 1 '1'] [3 3 3 3 '3'] [3 3 3 103 '103'] [5 5 5 5 '5'] [NULL NULL 31 31 '31'] [NULL NULL 31 131 '131'] [NULL NULL 32 32 '32']]`, 382 }, 383 { 384 spec: func() execinfrapb.InterleavedReaderJoinerSpec { 385 spec := copySpec(pdCd3Spec) 386 spec.Tables[0], spec.Tables[1] = spec.Tables[1], spec.Tables[0] 387 spec.Type = sqlbase.RightOuterJoin 388 return spec 389 }(), 390 post: execinfrapb.PostProcessSpec{ 391 Filter: execinfrapb.Expression{Expr: "@4 <= 7 OR @4 IS NOT DISTINCT FROM NULL"}, 392 }, 393 expected: `[[1 1 '1' 1 1] [NULL NULL NULL 2 2] [3 3 '3' 3 3] [3 103 '103' 3 3] [NULL NULL NULL 4 4] [5 5 '5' 5 5] [NULL NULL NULL 6 6] [NULL NULL NULL 7 7]]`, 394 }, 395 } 396 397 for i, tc := range testCases { 398 t.Run(strconv.Itoa(i), func(t *testing.T) { 399 evalCtx := tree.MakeTestingEvalContext(s.ClusterSettings()) 400 defer evalCtx.Stop(ctx) 401 flowCtx := execinfra.FlowCtx{ 402 EvalCtx: &evalCtx, 403 Cfg: &execinfra.ServerConfig{Settings: s.ClusterSettings()}, 404 // Run in a RootTxn so that there's no txn metadata produced. 405 Txn: kv.NewTxn(ctx, s.DB(), s.NodeID()), 406 NodeID: evalCtx.NodeID, 407 } 408 409 out := &distsqlutils.RowBuffer{} 410 irj, err := newInterleavedReaderJoiner(&flowCtx, 0 /* processorID */, &tc.spec, &tc.post, out) 411 if err != nil { 412 t.Fatal(err) 413 } 414 irj.Run(ctx) 415 if !out.ProducerClosed() { 416 t.Fatalf("output RowReceiver not closed") 417 } 418 419 var res sqlbase.EncDatumRows 420 for { 421 row, meta := out.Next() 422 if meta != nil { 423 t.Fatalf("unexpected metadata: %v", meta) 424 } 425 if row == nil { 426 break 427 } 428 res = append(res, row.Copy()) 429 } 430 431 if result := res.String(irj.OutputTypes()); result != tc.expected { 432 t.Errorf("invalid results.\ngot: %s\nexpected: %s'", result, tc.expected) 433 } 434 }) 435 } 436 } 437 438 func TestInterleavedReaderJoinerErrors(t *testing.T) { 439 defer leaktest.AfterTest(t)() 440 ctx := context.Background() 441 442 s, sqlDB, kvDB := serverutils.StartServer(t, base.TestServerArgs{}) 443 defer s.Stopper().Stop(ctx) 444 445 sqlutils.CreateTable(t, sqlDB, "parent", 446 "id INT PRIMARY KEY", 447 0, 448 sqlutils.ToRowFn(sqlutils.RowIdxFn), 449 ) 450 451 sqlutils.CreateTableInterleaved(t, sqlDB, "child", 452 "pid INT, id INT, PRIMARY KEY (pid, id)", 453 "parent (pid)", 454 0, 455 sqlutils.ToRowFn(sqlutils.RowModuloShiftedFn(0), sqlutils.RowIdxFn), 456 ) 457 458 sqlutils.CreateTableInterleaved(t, sqlDB, "grandchild", 459 "pid INT, cid INT, id INT, PRIMARY KEY (pid, cid, id)", 460 "child (pid, cid)", 461 0, 462 sqlutils.ToRowFn(sqlutils.RowModuloShiftedFn(0, 0), sqlutils.RowModuloShiftedFn(0), sqlutils.RowIdxFn), 463 ) 464 465 pd := sqlbase.GetTableDescriptor(kvDB, keys.SystemSQLCodec, sqlutils.TestDB, "parent") 466 cd := sqlbase.GetTableDescriptor(kvDB, keys.SystemSQLCodec, sqlutils.TestDB, "child") 467 gcd := sqlbase.GetTableDescriptor(kvDB, keys.SystemSQLCodec, sqlutils.TestDB, "grandchild") 468 469 testCases := []struct { 470 spec execinfrapb.InterleavedReaderJoinerSpec 471 post execinfrapb.PostProcessSpec 472 expected string 473 }{ 474 { 475 spec: execinfrapb.InterleavedReaderJoinerSpec{ 476 Tables: []execinfrapb.InterleavedReaderJoinerSpec_Table{ 477 { 478 Desc: *pd, 479 Ordering: execinfrapb.Ordering{Columns: []execinfrapb.Ordering_Column{{ColIdx: 0, Direction: execinfrapb.Ordering_Column_ASC}}}, 480 Spans: []execinfrapb.TableReaderSpan{{Span: pd.PrimaryIndexSpan(keys.SystemSQLCodec)}}, 481 }, 482 { 483 Desc: *cd, 484 Ordering: execinfrapb.Ordering{Columns: []execinfrapb.Ordering_Column{{ColIdx: 0, Direction: execinfrapb.Ordering_Column_DESC}}}, 485 Spans: []execinfrapb.TableReaderSpan{{Span: cd.PrimaryIndexSpan(keys.SystemSQLCodec)}}, 486 }, 487 }, 488 Type: sqlbase.InnerJoin, 489 }, 490 expected: "internal error: unmatched column orderings", 491 }, 492 493 { 494 spec: execinfrapb.InterleavedReaderJoinerSpec{ 495 Tables: []execinfrapb.InterleavedReaderJoinerSpec_Table{ 496 { 497 Desc: *pd, 498 Ordering: execinfrapb.Ordering{Columns: []execinfrapb.Ordering_Column{{ColIdx: 0, Direction: execinfrapb.Ordering_Column_ASC}}}, 499 Spans: []execinfrapb.TableReaderSpan{{Span: pd.PrimaryIndexSpan(keys.SystemSQLCodec)}}, 500 }, 501 }, 502 Type: sqlbase.InnerJoin, 503 }, 504 expected: "internal error: interleavedReaderJoiner only reads from two tables in an interleaved hierarchy", 505 }, 506 507 { 508 spec: execinfrapb.InterleavedReaderJoinerSpec{ 509 Tables: []execinfrapb.InterleavedReaderJoinerSpec_Table{ 510 { 511 Desc: *cd, 512 Ordering: execinfrapb.Ordering{Columns: []execinfrapb.Ordering_Column{{ColIdx: 0, Direction: execinfrapb.Ordering_Column_ASC}}}, 513 Spans: []execinfrapb.TableReaderSpan{{Span: pd.PrimaryIndexSpan(keys.SystemSQLCodec)}}, 514 }, 515 { 516 Desc: *gcd, 517 Ordering: execinfrapb.Ordering{Columns: []execinfrapb.Ordering_Column{{ColIdx: 0, Direction: execinfrapb.Ordering_Column_ASC}}}, 518 Spans: []execinfrapb.TableReaderSpan{{Span: gcd.PrimaryIndexSpan(keys.SystemSQLCodec)}}, 519 }, 520 }, 521 Type: sqlbase.InnerJoin, 522 }, 523 expected: "internal error: interleavedReaderJoiner only supports joins on the entire interleaved prefix", 524 }, 525 } 526 527 for i, tc := range testCases { 528 t.Run(strconv.Itoa(i), func(t *testing.T) { 529 evalCtx := tree.MakeTestingEvalContext(s.ClusterSettings()) 530 defer evalCtx.Stop(ctx) 531 flowCtx := execinfra.FlowCtx{ 532 EvalCtx: &evalCtx, 533 Cfg: &execinfra.ServerConfig{Settings: s.ClusterSettings()}, 534 // Run in a RootTxn so that there's no txn metadata produced. 535 Txn: kv.NewTxn(ctx, s.DB(), s.NodeID()), 536 NodeID: evalCtx.NodeID, 537 } 538 539 out := &distsqlutils.RowBuffer{} 540 _, err := newInterleavedReaderJoiner(&flowCtx, 0 /* processorID */, &tc.spec, &tc.post, out) 541 if err == nil { 542 t.Fatalf("expected an error") 543 } 544 545 // We flatten the error here since the test asserts that the 546 // "internal error: " prefix was added. 547 pgErr := pgerror.Flatten(err) 548 if actual := pgErr.Error(); actual != tc.expected { 549 t.Errorf("expected error: %s, actual: %s", tc.expected, actual) 550 } 551 }) 552 } 553 } 554 555 func TestInterleavedReaderJoinerTrailingMetadata(t *testing.T) { 556 defer leaktest.AfterTest(t)() 557 ctx := context.Background() 558 559 s, sqlDB, kvDB := serverutils.StartServer(t, base.TestServerArgs{}) 560 defer s.Stopper().Stop(ctx) 561 562 sqlutils.CreateTable(t, sqlDB, "parent", 563 "id INT PRIMARY KEY", 564 0, // numRows 565 func(row int) []tree.Datum { return nil }, 566 ) 567 568 sqlutils.CreateTableInterleaved(t, sqlDB, "child", 569 "pid INT, id INT, PRIMARY KEY (pid, id)", 570 "parent (pid)", 571 0, 572 func(row int) []tree.Datum { return nil }, 573 ) 574 575 pd := sqlbase.GetTableDescriptor(kvDB, keys.SystemSQLCodec, sqlutils.TestDB, "parent") 576 cd := sqlbase.GetTableDescriptor(kvDB, keys.SystemSQLCodec, sqlutils.TestDB, "child") 577 578 evalCtx := tree.MakeTestingEvalContext(s.ClusterSettings()) 579 defer evalCtx.Stop(ctx) 580 581 // Run the flow in a snowball trace so that we can test for tracing info. 582 tracer := tracing.NewTracer() 583 ctx, sp := tracing.StartSnowballTrace(ctx, tracer, "test flow ctx") 584 defer sp.Finish() 585 586 rootTxn := kv.NewTxn(ctx, s.DB(), s.NodeID()) 587 leafInputState := rootTxn.GetLeafTxnInputState(ctx) 588 leafTxn := kv.NewLeafTxn(ctx, s.DB(), s.NodeID(), &leafInputState) 589 590 flowCtx := execinfra.FlowCtx{ 591 EvalCtx: &evalCtx, 592 Cfg: &execinfra.ServerConfig{Settings: s.ClusterSettings()}, 593 // Run in a LeafTxn so that txn metadata is produced. 594 Txn: leafTxn, 595 NodeID: evalCtx.NodeID, 596 } 597 598 innerJoinSpec := execinfrapb.InterleavedReaderJoinerSpec{ 599 Tables: []execinfrapb.InterleavedReaderJoinerSpec_Table{ 600 { 601 Desc: *pd, 602 Ordering: execinfrapb.Ordering{Columns: []execinfrapb.Ordering_Column{{ColIdx: 0, Direction: execinfrapb.Ordering_Column_ASC}}}, 603 Spans: []execinfrapb.TableReaderSpan{{Span: pd.PrimaryIndexSpan(keys.SystemSQLCodec)}}, 604 }, 605 { 606 Desc: *cd, 607 Ordering: execinfrapb.Ordering{Columns: []execinfrapb.Ordering_Column{{ColIdx: 0, Direction: execinfrapb.Ordering_Column_ASC}}}, 608 Spans: []execinfrapb.TableReaderSpan{{Span: cd.PrimaryIndexSpan(keys.SystemSQLCodec)}}, 609 }, 610 }, 611 Type: sqlbase.InnerJoin, 612 } 613 614 out := &distsqlutils.RowBuffer{} 615 irj, err := newInterleavedReaderJoiner(&flowCtx, 0 /* processorID */, &innerJoinSpec, &execinfrapb.PostProcessSpec{}, out) 616 if err != nil { 617 t.Fatal(err) 618 } 619 irj.Run(ctx) 620 if !out.ProducerClosed() { 621 t.Fatalf("output RowReceiver not closed") 622 } 623 624 // Check for trailing metadata. 625 var traceSeen, txnFinalStateSeen bool 626 for { 627 row, meta := out.Next() 628 if row != nil { 629 t.Fatalf("row was pushed unexpectedly: %s", row.String(sqlbase.OneIntCol)) 630 } 631 if meta == nil { 632 break 633 } 634 if meta.TraceData != nil { 635 traceSeen = true 636 } 637 if meta.LeafTxnFinalState != nil { 638 txnFinalStateSeen = true 639 } 640 } 641 if !traceSeen { 642 t.Fatal("missing tracing trailing metadata") 643 } 644 if !txnFinalStateSeen { 645 t.Fatal("missing txn final state") 646 } 647 }