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  }