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 }