github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/execinfrapb/flow_diagram_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 execinfrapb 12 13 import ( 14 "encoding/json" 15 "fmt" 16 "reflect" 17 "strings" 18 "testing" 19 20 "github.com/cockroachdb/cockroach/pkg/roachpb" 21 "github.com/cockroachdb/cockroach/pkg/sql/sqlbase" 22 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 23 ) 24 25 // compareDiagrams verifies that two JSON strings decode to equal diagramData 26 // structures. This allows the expected string to be formatted differently. 27 func compareDiagrams(t *testing.T, result string, expected string) { 28 dec := json.NewDecoder(strings.NewReader(result)) 29 var resData, expData diagramData 30 if err := dec.Decode(&resData); err != nil { 31 t.Fatalf("error decoding '%s': %s", result, err) 32 } 33 dec = json.NewDecoder(strings.NewReader(expected)) 34 if err := dec.Decode(&expData); err != nil { 35 t.Fatal(err) 36 } 37 fmt.Printf("res: %+v\n", resData) 38 fmt.Printf("exp: %+v\n", expData) 39 if !reflect.DeepEqual(resData, expData) { 40 t.Errorf("\ngot:\n%s\nwant:\n%s", result, expected) 41 } 42 } 43 44 func TestPlanDiagramIndexJoin(t *testing.T) { 45 defer leaktest.AfterTest(t)() 46 47 flows := make(map[roachpb.NodeID]*FlowSpec) 48 49 desc := &sqlbase.TableDescriptor{ 50 Name: "Table", 51 Indexes: []sqlbase.IndexDescriptor{{Name: "SomeIndex"}}, 52 } 53 tr := TableReaderSpec{ 54 Table: *desc, 55 IndexIdx: 1, 56 } 57 58 flows[1] = &FlowSpec{ 59 Processors: []ProcessorSpec{{ 60 Core: ProcessorCoreUnion{TableReader: &tr}, 61 Post: PostProcessSpec{ 62 Projection: true, 63 OutputColumns: []uint32{0, 1}, 64 }, 65 Output: []OutputRouterSpec{{ 66 Type: OutputRouterSpec_PASS_THROUGH, 67 Streams: []StreamEndpointSpec{ 68 {StreamID: 0}, 69 }, 70 }}, 71 StageID: 1, 72 ProcessorID: 0, 73 }}, 74 } 75 76 flows[2] = &FlowSpec{ 77 Processors: []ProcessorSpec{{ 78 Core: ProcessorCoreUnion{TableReader: &tr}, 79 Post: PostProcessSpec{ 80 Projection: true, 81 OutputColumns: []uint32{0, 1}, 82 }, 83 Output: []OutputRouterSpec{{ 84 Type: OutputRouterSpec_PASS_THROUGH, 85 Streams: []StreamEndpointSpec{ 86 {StreamID: 1}, 87 }, 88 }}, 89 StageID: 1, 90 ProcessorID: 1, 91 }}, 92 } 93 94 flows[3] = &FlowSpec{ 95 Processors: []ProcessorSpec{ 96 { 97 Core: ProcessorCoreUnion{TableReader: &tr}, 98 Post: PostProcessSpec{ 99 Projection: true, 100 OutputColumns: []uint32{0, 1}, 101 }, 102 Output: []OutputRouterSpec{{ 103 Type: OutputRouterSpec_PASS_THROUGH, 104 Streams: []StreamEndpointSpec{ 105 {StreamID: 2}, 106 }, 107 }}, 108 StageID: 1, 109 ProcessorID: 2, 110 }, 111 { 112 Input: []InputSyncSpec{{ 113 Type: InputSyncSpec_ORDERED, 114 Ordering: Ordering{Columns: []Ordering_Column{ 115 {ColIdx: 1, Direction: Ordering_Column_ASC}}, 116 }, 117 Streams: []StreamEndpointSpec{ 118 {StreamID: 0}, 119 {StreamID: 1}, 120 {StreamID: 2}, 121 }, 122 }}, 123 Core: ProcessorCoreUnion{JoinReader: &JoinReaderSpec{Table: *desc}}, 124 Post: PostProcessSpec{ 125 Filter: Expression{Expr: "@1+@2<@3"}, 126 Projection: true, 127 OutputColumns: []uint32{2}, 128 }, 129 Output: []OutputRouterSpec{{ 130 Type: OutputRouterSpec_PASS_THROUGH, 131 Streams: []StreamEndpointSpec{{Type: StreamEndpointSpec_SYNC_RESPONSE}}, 132 }}, 133 StageID: 2, 134 ProcessorID: 3, 135 }, 136 }, 137 } 138 139 json, url, err := GeneratePlanDiagramURL("SOME SQL HERE", flows, true /* showInputTypes */) 140 if err != nil { 141 t.Fatal(err) 142 } 143 144 expected := ` 145 { 146 "sql":"SOME SQL HERE", 147 "nodeNames":["1","2","3"], 148 "processors":[ 149 {"nodeIdx":0,"inputs":[],"core":{"title":"TableReader/0","details":["SomeIndex@Table","Out: @1,@2"]},"outputs":[],"stage":1}, 150 {"nodeIdx":1,"inputs":[],"core":{"title":"TableReader/1","details":["SomeIndex@Table","Out: @1,@2"]},"outputs":[],"stage":1}, 151 {"nodeIdx":2,"inputs":[],"core":{"title":"TableReader/2","details":["SomeIndex@Table","Out: @1,@2"]},"outputs":[],"stage":1}, 152 {"nodeIdx":2,"inputs":[{"title":"ordered","details":["@2+"]}],"core":{"title":"JoinReader/3","details":["primary@Table","Filter: @1+@2\u003c@3","Out: @3"]},"outputs":[],"stage":2}, 153 {"nodeIdx":2,"inputs":[],"core":{"title":"Response","details":[]},"outputs":[]} 154 ], 155 "edges":[ 156 {"sourceProc":0,"sourceOutput":0,"destProc":3,"destInput":1}, 157 {"sourceProc":1,"sourceOutput":0,"destProc":3,"destInput":1}, 158 {"sourceProc":2,"sourceOutput":0,"destProc":3,"destInput":1}, 159 {"sourceProc":3,"sourceOutput":0,"destProc":4,"destInput":0} 160 ] 161 } 162 ` 163 164 compareDiagrams(t, json, expected) 165 166 expectedURL := "https://cockroachdb.github.io/distsqlplan/decode.html#eJy0kkFLwzAUx-9-ivG_LmCbeMqpl4kTdbp50x5q8xiBLqlJCpPS7y5NZVvByWR6zHvv_36_PtrCv1eQWC3uZ5PV093kZracgcFYRQ_FhjzkC1IwcDAI5Ay1syV5b13fauPgXG0hEwZt6ib05ZyhtI4gWwQdKoLEc_FW0ZIKRe4yAYOiUOgqrl_ZDc2Nom0Wh8CwaIKcZCnLOPKOwTZhv9iHYk2QaccO4Onp8PTP4fx0OP9X-J5pnSJHakzL-BR5943hrdXmS1CMI7XTm8J97PSudRXI9YbTjL82SSLKTOy0xVFn_puDLcnX1ngaqRzbnPQfRGpNwwG8bVxJj86W8Y8cnouYiwVFPgxdMTzmJrbiUQ_D6Tlhfk5Y_Bi-GoWTLu8uPgMAAP__nLk4fw==" 167 if url.String() != expectedURL { 168 t.Errorf("expected `%s` got `%s`", expectedURL, url.String()) 169 } 170 } 171 172 func TestPlanDiagramJoin(t *testing.T) { 173 defer leaktest.AfterTest(t)() 174 175 flows := make(map[roachpb.NodeID]*FlowSpec) 176 177 descA := &sqlbase.TableDescriptor{Name: "TableA"} 178 descB := &sqlbase.TableDescriptor{Name: "TableB"} 179 180 trA := TableReaderSpec{Table: *descA} 181 182 trB := TableReaderSpec{Table: *descB} 183 184 hj := HashJoinerSpec{ 185 LeftEqColumns: []uint32{0, 2}, 186 RightEqColumns: []uint32{2, 1}, 187 OnExpr: Expression{Expr: "@1+@2<@6"}, 188 MergedColumns: true, 189 } 190 191 flows[1] = &FlowSpec{ 192 Processors: []ProcessorSpec{ 193 { 194 Core: ProcessorCoreUnion{TableReader: &trA}, 195 Post: PostProcessSpec{ 196 Projection: true, 197 OutputColumns: []uint32{0, 1, 3}, 198 }, 199 Output: []OutputRouterSpec{{ 200 Type: OutputRouterSpec_BY_HASH, 201 HashColumns: []uint32{0, 1}, 202 Streams: []StreamEndpointSpec{ 203 {StreamID: 11}, 204 {StreamID: 12}, 205 }, 206 }}, 207 ProcessorID: 0, 208 }, 209 }, 210 } 211 212 flows[2] = &FlowSpec{ 213 Processors: []ProcessorSpec{ 214 { 215 Core: ProcessorCoreUnion{TableReader: &trA}, 216 Post: PostProcessSpec{ 217 Projection: true, 218 OutputColumns: []uint32{0, 1, 3}, 219 }, 220 Output: []OutputRouterSpec{{ 221 Type: OutputRouterSpec_BY_HASH, 222 HashColumns: []uint32{0, 1}, 223 Streams: []StreamEndpointSpec{ 224 {StreamID: 21}, 225 {StreamID: 22}, 226 }, 227 }}, 228 ProcessorID: 1, 229 }, 230 { 231 Input: []InputSyncSpec{ 232 { 233 Type: InputSyncSpec_UNORDERED, 234 Streams: []StreamEndpointSpec{ 235 {StreamID: 11}, 236 {StreamID: 21}, 237 {StreamID: 31}, 238 }, 239 }, 240 { 241 Type: InputSyncSpec_UNORDERED, 242 Streams: []StreamEndpointSpec{ 243 {StreamID: 41}, 244 {StreamID: 51}, 245 }, 246 }, 247 }, 248 Core: ProcessorCoreUnion{HashJoiner: &hj}, 249 Post: PostProcessSpec{ 250 Projection: true, 251 OutputColumns: []uint32{0, 1, 2, 3, 4, 5}, 252 }, 253 Output: []OutputRouterSpec{{ 254 Type: OutputRouterSpec_PASS_THROUGH, 255 Streams: []StreamEndpointSpec{ 256 {StreamID: 101}, 257 }, 258 }}, 259 ProcessorID: 2, 260 }, 261 { 262 Input: []InputSyncSpec{{ 263 Type: InputSyncSpec_UNORDERED, 264 Streams: []StreamEndpointSpec{ 265 {StreamID: 101}, 266 {StreamID: 102}, 267 }, 268 }}, 269 Core: ProcessorCoreUnion{Noop: &NoopCoreSpec{}}, 270 Output: []OutputRouterSpec{{ 271 Type: OutputRouterSpec_PASS_THROUGH, 272 Streams: []StreamEndpointSpec{{Type: StreamEndpointSpec_SYNC_RESPONSE}}, 273 }}, 274 ProcessorID: 3, 275 }, 276 }, 277 } 278 279 flows[3] = &FlowSpec{ 280 Processors: []ProcessorSpec{ 281 { 282 Core: ProcessorCoreUnion{TableReader: &trA}, 283 Post: PostProcessSpec{ 284 Projection: true, 285 OutputColumns: []uint32{0, 1, 3}, 286 }, 287 Output: []OutputRouterSpec{{ 288 Type: OutputRouterSpec_BY_HASH, 289 HashColumns: []uint32{0, 1}, 290 Streams: []StreamEndpointSpec{ 291 {StreamID: 31}, 292 {StreamID: 32}, 293 }, 294 }}, 295 ProcessorID: 4, 296 }, 297 { 298 Core: ProcessorCoreUnion{TableReader: &trB}, 299 Post: PostProcessSpec{ 300 Projection: true, 301 OutputColumns: []uint32{1, 2, 4}, 302 }, 303 Output: []OutputRouterSpec{{ 304 Type: OutputRouterSpec_BY_HASH, 305 HashColumns: []uint32{2, 1}, 306 Streams: []StreamEndpointSpec{ 307 {StreamID: 41}, 308 {StreamID: 42}, 309 }, 310 }}, 311 ProcessorID: 5, 312 }, 313 { 314 Input: []InputSyncSpec{ 315 { 316 Type: InputSyncSpec_UNORDERED, 317 Streams: []StreamEndpointSpec{ 318 {StreamID: 12}, 319 {StreamID: 22}, 320 {StreamID: 32}, 321 }, 322 }, 323 { 324 Type: InputSyncSpec_UNORDERED, 325 Streams: []StreamEndpointSpec{ 326 {StreamID: 42}, 327 {StreamID: 52}, 328 }, 329 }, 330 }, 331 Core: ProcessorCoreUnion{HashJoiner: &hj}, 332 Output: []OutputRouterSpec{{ 333 Type: OutputRouterSpec_PASS_THROUGH, 334 Streams: []StreamEndpointSpec{ 335 {StreamID: 101}, 336 }, 337 }}, 338 ProcessorID: 6, 339 }, 340 }, 341 } 342 343 flows[4] = &FlowSpec{ 344 Processors: []ProcessorSpec{{ 345 Core: ProcessorCoreUnion{TableReader: &trB}, 346 Post: PostProcessSpec{ 347 Projection: true, 348 OutputColumns: []uint32{1, 2, 4}, 349 }, 350 Output: []OutputRouterSpec{{ 351 Type: OutputRouterSpec_BY_HASH, 352 HashColumns: []uint32{2, 1}, 353 Streams: []StreamEndpointSpec{ 354 {StreamID: 51}, 355 {StreamID: 52}, 356 }, 357 }}, 358 ProcessorID: 7, 359 }}, 360 } 361 362 diagram, err := GeneratePlanDiagram("SOME SQL HERE", flows, true /* showInputTypes */) 363 if err != nil { 364 t.Fatal(err) 365 } 366 s, _, err := diagram.ToURL() 367 if err != nil { 368 t.Fatal(err) 369 } 370 371 expected := ` 372 { 373 "sql":"SOME SQL HERE", 374 "nodeNames":["1","2","3","4"], 375 "processors":[ 376 {"nodeIdx":0,"inputs":[],"core":{"title":"TableReader/0","details":["primary@TableA","Out: @1,@2,@4"]},"outputs":[{"title":"by hash","details":["@1,@2"]}],"stage":0}, 377 {"nodeIdx":1,"inputs":[],"core":{"title":"TableReader/1","details":["primary@TableA","Out: @1,@2,@4"]},"outputs":[{"title":"by hash","details":["@1,@2"]}],"stage":0}, 378 {"nodeIdx":1,"inputs":[{"title":"unordered","details":[]},{"title":"unordered","details":[]}],"core":{"title":"HashJoiner/2","details":["left(@1,@3)=right(@3,@2)","ON @1+@2\u003c@6","Merged columns: 2","Out: @1,@2,@3,@4,@5,@6"]},"outputs":[],"stage":0}, 379 {"nodeIdx":1,"inputs":[{"title":"unordered","details":[]}],"core":{"title":"No-op/3","details":[]},"outputs":[],"stage":0}, 380 {"nodeIdx":2,"inputs":[],"core":{"title":"TableReader/4","details":["primary@TableA","Out: @1,@2,@4"]},"outputs":[{"title":"by hash","details":["@1,@2"]}],"stage":0}, 381 {"nodeIdx":2,"inputs":[],"core":{"title":"TableReader/5","details":["primary@TableB","Out: @2,@3,@5"]},"outputs":[{"title":"by hash","details":["@3,@2"]}],"stage":0}, 382 {"nodeIdx":2,"inputs":[{"title":"unordered","details":[]},{"title":"unordered","details":[]}],"core":{"title":"HashJoiner/6","details":["left(@1,@3)=right(@3,@2)","ON @1+@2\u003c@6","Merged columns: 2"]},"outputs":[],"stage":0}, 383 {"nodeIdx":3,"inputs":[],"core":{"title":"TableReader/7","details":["primary@TableB","Out: @2,@3,@5"]},"outputs":[{"title":"by hash","details":["@3,@2"]}],"stage":0}, 384 {"nodeIdx":1,"inputs":[],"core":{"title":"Response","details":[]},"outputs":[],"stage":0} 385 ], 386 "edges":[ 387 {"sourceProc":0,"sourceOutput":1,"destProc":2,"destInput":1}, 388 {"sourceProc":0,"sourceOutput":1,"destProc":6,"destInput":1}, 389 {"sourceProc":1,"sourceOutput":1,"destProc":2,"destInput":1}, 390 {"sourceProc":1,"sourceOutput":1,"destProc":6,"destInput":1}, 391 {"sourceProc":2,"sourceOutput":0,"destProc":3,"destInput":1}, 392 {"sourceProc":3,"sourceOutput":0,"destProc":8,"destInput":0}, 393 {"sourceProc":4,"sourceOutput":1,"destProc":2,"destInput":1}, 394 {"sourceProc":4,"sourceOutput":1,"destProc":6,"destInput":1}, 395 {"sourceProc":5,"sourceOutput":1,"destProc":2,"destInput":2}, 396 {"sourceProc":5,"sourceOutput":1,"destProc":6,"destInput":2}, 397 {"sourceProc":6,"sourceOutput":0,"destProc":3,"destInput":1}, 398 {"sourceProc":7,"sourceOutput":1,"destProc":2,"destInput":2}, 399 {"sourceProc":7,"sourceOutput":1,"destProc":6,"destInput":2} 400 ] 401 } 402 ` 403 404 compareDiagrams(t, s, expected) 405 }