github.com/dolthub/go-mysql-server@v0.18.0/sql/memo/select_hints_test.go (about)

     1  package memo
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/stretchr/testify/require"
     7  
     8  	"github.com/dolthub/go-mysql-server/memory"
     9  	"github.com/dolthub/go-mysql-server/sql"
    10  	"github.com/dolthub/go-mysql-server/sql/plan"
    11  )
    12  
    13  func TestHintParsing(t *testing.T) {
    14  	tests := []struct {
    15  		comment string
    16  		hints   []Hint
    17  	}{
    18  		{
    19  			comment: "/*+ join_order(a,b) */",
    20  			hints:   []Hint{{Typ: HintTypeJoinOrder, Args: []string{"a", "b"}}},
    21  		},
    22  		{
    23  			comment: "/*+ JOIN_ORDER(a,b) */",
    24  			hints:   []Hint{{Typ: HintTypeJoinOrder, Args: []string{"a", "b"}}},
    25  		},
    26  		{
    27  			comment: "/*+ join_order(a, b) */",
    28  			hints:   []Hint{{Typ: HintTypeJoinOrder, Args: []string{"a", "b"}}},
    29  		},
    30  		{
    31  			comment: "/*+ join_order(a,    b) */",
    32  			hints:   []Hint{{Typ: HintTypeJoinOrder, Args: []string{"a", "b"}}},
    33  		},
    34  		{
    35  			comment: "/*+JOIN_ORDER(a,b)*/",
    36  			hints:   []Hint{{Typ: HintTypeJoinOrder, Args: []string{"a", "b"}}},
    37  		},
    38  		{
    39  			comment: "/* join_order(a,b) */",
    40  			hints:   []Hint{},
    41  		},
    42  		{
    43  			comment: "/*+ join_order(a_1, b_2) */",
    44  			hints:   []Hint{{Typ: HintTypeJoinOrder, Args: []string{"a_1", "b_2"}}},
    45  		},
    46  		{
    47  			comment: "/*+ join_order(a1, b2) */",
    48  			hints:   []Hint{{Typ: HintTypeJoinOrder, Args: []string{"a1", "b2"}}},
    49  		},
    50  		{
    51  			comment: "/*+ join_order( a1, b2 ) */",
    52  			hints:   []Hint{{Typ: HintTypeJoinOrder, Args: []string{"a1", "b2"}}},
    53  		},
    54  		{
    55  			comment: "/*+ join_order(( a1, b2 )) */",
    56  			hints:   []Hint{},
    57  		},
    58  		{
    59  			comment: "/*+ NO_ICP */",
    60  			hints:   []Hint{{Typ: HintTypeNoIndexConditionPushDown}},
    61  		},
    62  		{
    63  			comment: "/*+ JOIN_FIXED_ORDER */",
    64  			hints:   []Hint{{Typ: HintTypeJoinFixedOrder}},
    65  		},
    66  		{
    67  			comment: "/*+ JOIN_FIXED_ORDER(a) */",
    68  			hints:   []Hint{},
    69  		},
    70  		{
    71  			comment: "/*+ MERGE_JOIN(a,b) */",
    72  			hints:   []Hint{{Typ: HintTypeMergeJoin, Args: []string{"a", "b"}}},
    73  		},
    74  		{
    75  			comment: "/*+ MERGE_JOIN(a,b,c) */",
    76  			hints:   []Hint{},
    77  		},
    78  		{
    79  			comment: "/*+ lookup_join(a,b) */",
    80  			hints:   []Hint{{Typ: HintTypeLookupJoin, Args: []string{"a", "b"}}},
    81  		},
    82  		{
    83  			comment: "/*+ hash_join(a,b) */",
    84  			hints:   []Hint{{Typ: HintTypeHashJoin, Args: []string{"a", "b"}}},
    85  		},
    86  		{
    87  			comment: "/*+ semi_join(a,b) */",
    88  			hints:   []Hint{{Typ: HintTypeSemiJoin, Args: []string{"a", "b"}}},
    89  		},
    90  		{
    91  			comment: "/*+ inner_join(a,b) */",
    92  			hints:   []Hint{{Typ: HintTypeInnerJoin, Args: []string{"a", "b"}}},
    93  		},
    94  		{
    95  			comment: "/*+ anti_join(a,b) */",
    96  			hints:   []Hint{{Typ: HintTypeAntiJoin, Args: []string{"a", "b"}}},
    97  		},
    98  		{
    99  			comment: "/*+ hash_join(a,b) merge_join(b,c) lookup_join(a,d) */",
   100  			hints: []Hint{
   101  				{Typ: HintTypeHashJoin, Args: []string{"a", "b"}},
   102  				{Typ: HintTypeMergeJoin, Args: []string{"b", "c"}},
   103  				{Typ: HintTypeLookupJoin, Args: []string{"a", "d"}},
   104  			},
   105  		},
   106  		{
   107  			comment: "/*+ max_execution_time merge_join(b,c) join_fixed_order */",
   108  			hints: []Hint{
   109  				{Typ: HintTypeMergeJoin, Args: []string{"b", "c"}},
   110  				{Typ: HintTypeJoinFixedOrder},
   111  			},
   112  		},
   113  		{
   114  			comment: "/*+ JOIN_ORDER(a,b,c) LOOKUP_JOIN(a,b) MERGE_JOIN(b,c) NO_ICP ()KF)E)SFKK)SE)F_SE_F)E)S)KEFK */",
   115  			hints: []Hint{
   116  				{Typ: HintTypeJoinOrder, Args: []string{"a", "b", "c"}},
   117  				{Typ: HintTypeLookupJoin, Args: []string{"a", "b"}},
   118  				{Typ: HintTypeMergeJoin, Args: []string{"b", "c"}},
   119  				{Typ: HintTypeNoIndexConditionPushDown},
   120  			},
   121  		},
   122  		{
   123  			comment: "/*+ NOT_A_REAL_HINT JOIN_ORDER(a,b,c) ()KF)E)SFKK) SE)F_SE_F)E)S)KEFK LOOKUP_JOIN(a,b) JOIN_ORDER() MERGE_JOIN(b,c) NO_ICP */",
   124  			hints: []Hint{
   125  				{Typ: HintTypeJoinOrder, Args: []string{"a", "b", "c"}},
   126  				{Typ: HintTypeLookupJoin, Args: []string{"a", "b"}},
   127  				{Typ: HintTypeMergeJoin, Args: []string{"b", "c"}},
   128  				{Typ: HintTypeNoIndexConditionPushDown},
   129  			},
   130  		},
   131  	}
   132  
   133  	for _, tt := range tests {
   134  		t.Run(tt.comment, func(t *testing.T) {
   135  			res := parseJoinHints(tt.comment)
   136  			require.ElementsMatch(t, tt.hints, res)
   137  		})
   138  	}
   139  }
   140  
   141  func TestOrderHintBuilding(t *testing.T) {
   142  	db := memory.NewDatabase("test")
   143  	pro := memory.NewDBProvider(db)
   144  
   145  	p := plan.NewInnerJoin(
   146  		plan.NewInnerJoin(
   147  			plan.NewInnerJoin(
   148  				tableNode(db, "ab"),
   149  				tableNode(db, "xy"),
   150  				newEq("ab.x=xy.x"),
   151  			),
   152  			tableNode(db, "pq"),
   153  			newEq("xy.x=pq.x"),
   154  		),
   155  		tableNode(db, "uv"),
   156  		newEq("pq.x=uv.x"),
   157  	)
   158  
   159  	tests := []struct {
   160  		name    string
   161  		hint    []string
   162  		plan    sql.Node
   163  		exp     map[GroupId]vertexSet
   164  		invalid bool
   165  	}{
   166  		{
   167  			name: "valid1",
   168  			hint: []string{"ab", "xy", "pq", "uv"},
   169  			plan: p,
   170  			exp: map[GroupId]vertexSet{
   171  				1:  testVertexSet(0),          // ab
   172  				2:  testVertexSet(1),          // xy
   173  				3:  testVertexSet(0, 1),       // ab x xy
   174  				4:  testVertexSet(2),          // pq
   175  				5:  testVertexSet(0, 1, 2),    // ab x xy x pq
   176  				6:  testVertexSet(3),          // uv
   177  				7:  testVertexSet(0, 1, 2, 3), // ab x xy x pq x uv
   178  				8:  testVertexSet(0, 2),       // ab x pq
   179  				9:  testVertexSet(1, 2),       // xy x pq
   180  				10: testVertexSet(0, 3),       // ab x uv
   181  				11: testVertexSet(1, 3),       // xy x uv
   182  				12: testVertexSet(0, 1, 3),    //  (ab x xy) x uv
   183  				13: testVertexSet(2, 3),       // pq x uv
   184  				14: testVertexSet(0, 2, 3),    // uv x (ab x pq)
   185  				15: testVertexSet(1, 2, 3),    // xy x pq x uv
   186  			},
   187  		},
   188  		{
   189  			name: "valid2",
   190  			hint: []string{"pq", "xy", "ab", "uv"},
   191  			plan: p,
   192  			exp: map[GroupId]vertexSet{
   193  				1:  testVertexSet(2),          // ab
   194  				2:  testVertexSet(1),          // xy
   195  				3:  testVertexSet(2, 1),       // ab x xy
   196  				4:  testVertexSet(0),          // pq
   197  				5:  testVertexSet(2, 1, 0),    // ab x xy x pq
   198  				6:  testVertexSet(3),          // uv
   199  				7:  testVertexSet(2, 1, 0, 3), // ab x xy x pq x uv
   200  				8:  testVertexSet(2, 0),       // ab x pq
   201  				9:  testVertexSet(1, 0),       // xy x pq
   202  				10: testVertexSet(2, 3),       // ab x uv
   203  				11: testVertexSet(1, 3),       // xy x uv
   204  				12: testVertexSet(2, 1, 3),    // (ab x xy) x uv
   205  				13: testVertexSet(0, 3),       // pq x uv
   206  				14: testVertexSet(3, 2, 0),    // uv x (ab x pq)
   207  				15: testVertexSet(1, 0, 3),    // xy x pq x uv
   208  
   209  			},
   210  		},
   211  		{
   212  			name:    "invalid1",
   213  			hint:    []string{"ab", "xy"},
   214  			plan:    p,
   215  			invalid: true,
   216  		},
   217  		{
   218  			name:    "invalid2",
   219  			hint:    []string{"rs", "pq", "ab", "uv"},
   220  			plan:    p,
   221  			invalid: true,
   222  		},
   223  		{
   224  			name:    "invalid3",
   225  			hint:    []string{},
   226  			plan:    p,
   227  			invalid: true,
   228  		},
   229  		{
   230  			name:    "invalid4",
   231  			hint:    []string{"ab", "xy", "rs"},
   232  			plan:    p,
   233  			invalid: true,
   234  		},
   235  	}
   236  
   237  	for _, tt := range tests {
   238  		t.Run(tt.name, func(t *testing.T) {
   239  			j := NewJoinOrderBuilder(NewMemo(newContext(pro), nil, nil, 0, NewDefaultCoster()))
   240  			j.ReorderJoin(tt.plan)
   241  			j.m.WithJoinOrder(tt.hint)
   242  			if tt.invalid {
   243  				require.Equal(t, j.m.hints.order, (*joinOrderHint)(nil))
   244  			} else {
   245  				require.Equal(t, tt.exp, j.m.hints.order.groups)
   246  			}
   247  		})
   248  	}
   249  }
   250  
   251  func testVertexSet(i ...int) vertexSet {
   252  	s := vertexSet(0)
   253  	for _, i := range i {
   254  		s = s.add(uint64(i))
   255  	}
   256  	return s
   257  }