github.com/dolthub/go-mysql-server@v0.18.0/enginetest/join_stats_tests.go (about) 1 package enginetest 2 3 import ( 4 "strings" 5 "testing" 6 7 "github.com/stretchr/testify/require" 8 9 "github.com/dolthub/go-mysql-server/enginetest/scriptgen/setup" 10 "github.com/dolthub/go-mysql-server/memory" 11 "github.com/dolthub/go-mysql-server/sql" 12 ) 13 14 func TestJoinStats(t *testing.T, harness Harness) { 15 harness.Setup(setup.MydbData) 16 17 for _, tt := range JoinStatTests { 18 t.Run(tt.name, func(t *testing.T) { 19 harness.Setup([]setup.SetupScript{setup.MydbData[0]}) 20 e := mustNewEngine(t, harness) 21 defer e.Close() 22 23 tfp, ok := e.EngineAnalyzer().Catalog.DbProvider.(sql.TableFunctionProvider) 24 if !ok { 25 return 26 } 27 newPro, err := tfp.WithTableFunctions(memory.ExponentialDistTable{}, memory.NormalDistTable{}) 28 require.NoError(t, err) 29 e.EngineAnalyzer().Catalog.DbProvider = newPro.(sql.DatabaseProvider) 30 31 ctx := harness.NewContext() 32 for _, q := range tt.setup { 33 _, iter, err := e.Query(ctx, q) 34 require.NoError(t, err) 35 _, err = sql.RowIterToRows(ctx, iter) 36 require.NoError(t, err) 37 } 38 39 for _, tt := range tt.tests { 40 if tt.order != nil { 41 evalJoinOrder(t, harness, e, tt.q, tt.order, tt.skipOld) 42 } 43 if tt.exp != nil { 44 evalJoinCorrectness(t, harness, e, tt.q, tt.q, tt.exp, false) 45 } 46 } 47 }) 48 } 49 } 50 51 var JoinStatTests = []struct { 52 name string 53 setup []string 54 tests []JoinPlanTest 55 }{ 56 { 57 name: "test table orders with normal distributions", 58 setup: []string{ 59 "create table u0 (a int primary key, b int, c int, key (b,c))", 60 "insert into u0 select * from normal_dist(2, 500, 0, 5)", 61 "create table u0_2 (a int primary key, b int, c int, key (b,c))", 62 "insert into u0_2 select * from normal_dist(2, 3000, 0, 5)", 63 "create table `u-15` (a int primary key, b int, c int, key (b,c))", 64 "insert into `u-15` select * from normal_dist(2, 5000, -15, 5)", 65 "create table `u+15` (a int primary key, b int, c int, key (b,c))", 66 "insert into `u+15` select * from normal_dist(2, 6000, 15, 5)", 67 "analyze table u0", 68 "analyze table u0_2", 69 "analyze table `u-15`", 70 "analyze table `u+15`", 71 }, 72 tests: []JoinPlanTest{ 73 { 74 // a is smaller 75 q: "select /*+ LEFT_DEEP */ count(*) from `u-15` a join `u+15` b on a.b = b.b", 76 order: [][]string{{"a", "b"}}, 77 }, 78 { 79 // b with filter is smaller 80 q: "select /*+ LEFT_DEEP */ count(*) from `u-15` a join `u+15` b on a.b = b.b where b.b < 15", 81 order: [][]string{{"b", "a"}}, 82 }, 83 }, 84 }, 85 { 86 name: "test table orders with exponential distributions", 87 setup: []string{ 88 "create table mid (a int primary key, b int, c int, key (b,c))", 89 "insert into mid select * from normal_dist(2, 1000, 9, 1)", 90 "create table low (a int primary key, b int, c int, key (b,c))", 91 "insert into low select * from exponential_dist(2, 2000, .1)", 92 "create table high (a int primary key, b int, c int, key (b,c))", 93 "insert into high select col0, 10-col1, col2 from exponential_dist(2, 4000, .1)", 94 "analyze table low, mid, high", 95 }, 96 tests: []JoinPlanTest{ 97 { 98 // low is flattish exponential upwards from 0-> 99 // high is flattish exponential downwards from <-10 100 // mid is sharp normal near high 101 // order (mid x low) x high is the easiest and expected 102 // for certain seeds, (low, high) will be smaller than mid and chosen 103 q: "select /*+ LEFT_DEEP LOOKUP_JOIN(low, high) LOOKUP_JOIN(mid, low) */ count(*) from low join mid on low.b = mid.b join high on low.b = high.b", 104 order: [][]string{{"mid", "low", "high"}, {"low", "high", "mid"}}, 105 }, 106 }, 107 }, 108 { 109 // there is a trade-off for these where we either pick the first table 110 // first if card(b) < card(axc), or we choose (axc) if its intermediate 111 // result cardinality is smaller than filtered (b). 112 name: "test table orders with filters and normal distributions", 113 setup: []string{ 114 "create table u0 (a int primary key, b int, c int, key (b,c))", 115 "insert into u0 select * from normal_dist(2, 2000, 0, 5)", 116 "create table u0_2 (a int primary key, b int, c int, key (b,c))", 117 "insert into u0_2 select * from normal_dist(2, 2000, 0, 5)", 118 "create table `u-15` (a int primary key, b int, c int, key (b,c))", 119 "insert into `u-15` select * from normal_dist(2, 2000, -10, 5)", 120 "create table `u+15` (a int primary key, b int, c int, key (b,c))", 121 "insert into `u+15` select * from normal_dist(2, 2000, 10, 5)", 122 "analyze table u0", 123 "analyze table u0_2", 124 "analyze table `u-15`", 125 "analyze table `u+15`", 126 }, 127 tests: []JoinPlanTest{ 128 { 129 // axc is smallest join, a is smallest table 130 // (axb) is less than (axc) maybe 1/50 times 131 q: "select /*+ LEFT_DEEP LOOKUP_JOIN(a,b) LOOKUP_JOIN(b,c) */ count(*) from u0 b join `u-15` a on a.b = b.b join `u+15` c on a.b = c.b where a.b > 3", 132 order: [][]string{{"a", "c", "b"}, {"a", "b", "c"}}, 133 }, 134 { 135 // b is smallest table, bxc is smallest b-connected join 136 // due to b < 0 filter and positive c skew 137 q: "select /*+ LEFT_DEEP */ count(*) from u0 b join `u-15` a on a.b = b.b join `u+15` c on a.b = c.b where b.b < -2", 138 order: [][]string{{"b", "c", "a"}}, 139 }, 140 { 141 q: "select /*+ LEFT_DEEP */ count(*) from u0 b join `u-15` a on a.b = b.b join `u+15` c on a.b = c.b where b.b < -2", 142 order: [][]string{{"b", "c", "a"}}, 143 }, 144 { 145 // b is smallest table, bxa is smallest b-connected join 146 // due to b > 0 filter and negative c skew 147 // for certain seeds (cxa) is much smaller that b 148 q: "select /*+ LEFT_DEEP LOOKUP_JOIN(a,b) LOOKUP_JOIN(b,c) */ count(*) from `u-15` a join u0 b on a.b = b.b join `u+15` c on a.b = c.b where b.b > 2", 149 order: [][]string{{"b", "a", "c"}, {"c", "a", "b"}}, 150 }, 151 { 152 q: "select /*+ LEFT_DEEP LOOKUP_JOIN(a,b) LOOKUP_JOIN(b,c) */ count(*) from u0 b join `u-15` a on a.b = b.b join `u+15` c on a.b = c.b where b.b > 2", 153 order: [][]string{{"b", "a", "c"}, {"c", "a", "b"}}, 154 }, 155 }, 156 }, 157 { 158 name: "partial stats don't error", 159 setup: []string{ 160 "create table xy (x int primary key, y int, key(y))", 161 "insert into xy select col0, col1 from normal_dist(1, 10, 0,10)", 162 "create table uv (u int primary key, v int, key(v))", 163 "insert into uv select col0, col1 from normal_dist(1, 20, 0,10)", 164 "analyze table xy", 165 }, 166 tests: []JoinPlanTest{ 167 { 168 q: "select * from uv join xy on y = v", 169 // we don't care what the order is, just don't error in join planning 170 order: [][]string{{"xy", "uv"}, {"uv", "xy"}}, 171 }, 172 }, 173 }, 174 { 175 name: "varchar column doesn't error", 176 setup: []string{ 177 "create table xy (x varchar(10) primary key, y int)", 178 "insert into xy select concat('text', col0), col1 from normal_dist(1, 10, 0,10)", 179 "create table uv (u varchar(10) primary key, v int)", 180 "insert into uv select concat('text', col0), col1 from normal_dist(1, 20, 0,10)", 181 "analyze table xy, uv", 182 }, 183 tests: []JoinPlanTest{ 184 { 185 q: "select * from uv join xy on u = x", 186 // we don't care what the order is, just don't error in join planning 187 order: [][]string{{"xy", "uv"}, {"uv", "xy"}}, 188 }, 189 }, 190 }, 191 { 192 name: "varchar column doesn't prevent valid prefix", 193 setup: []string{ 194 "create table xy (x varchar(10) primary key, y int, key (y,x))", 195 "insert into xy select concat('text', col0), col1 from normal_dist(1, 10, 0,10)", 196 "create table uv (u varchar(10) primary key, v int, key (v,u))", 197 "insert into uv select concat('text', col0), col1 from normal_dist(1, 20, 0,10)", 198 "analyze table xy, uv", 199 }, 200 tests: []JoinPlanTest{ 201 { 202 q: "select * from uv join xy on y = v", 203 // we don't care what the order is, just don't error in join planning 204 order: [][]string{{"xy", "uv"}, {"uv", "xy"}}, 205 }, 206 { 207 q: "select * from uv join xy on x = u and y = v", 208 // we don't care what the order is, just don't error in join planning 209 order: [][]string{{"xy", "uv"}, {"uv", "xy"}}, 210 }, 211 { 212 q: "select * from uv join xy on y = v where x in ('text1', 'text2')", 213 // we don't care what the order is, just don't error in join planning 214 order: [][]string{{"xy", "uv"}, {"uv", "xy"}}, 215 }, 216 { 217 q: "select * from uv join xy on y = v where x > 'text2'", 218 // we don't care what the order is, just don't error in join planning 219 order: [][]string{{"xy", "uv"}, {"uv", "xy"}}, 220 }, 221 }, 222 }, 223 { 224 name: "index ordinals not in order", 225 setup: []string{ 226 "create table xy (x int primary key, y int, key (y,x))", 227 "insert into xy select col0, col1 from normal_dist(1, 10, 0,10)", 228 "create table uv (u int primary key, v int, w int, key (v,w,u))", 229 "insert into uv select col0, col1, col2 from normal_dist(2, 20, 0,10)", 230 "analyze table xy, uv", 231 }, 232 tests: []JoinPlanTest{ 233 { 234 q: "select * from uv join xy on y = v and y = w and y = u", 235 // we don't care what the order is, just don't error in join planning 236 order: [][]string{{"xy", "uv"}, {"uv", "xy"}}, 237 }, 238 }, 239 }, 240 { 241 name: "absurdly long index", 242 setup: []string{ 243 "create table xy (x int primary key, y int, key (y,x))", 244 "insert into xy select col0, col1 from normal_dist(1, 10, 0,10)", 245 "create table uv (a int primary key, b int, c int, d int, e int, f int, g int, h int, i int, j int, key (a,b,c,d,e,f,g,h,i,j))", 246 "insert into uv select * from normal_dist(9, 20, 0,10)", 247 "analyze table xy, uv", 248 }, 249 tests: []JoinPlanTest{ 250 { 251 q: "select * from uv join xy on y = a and y = b and y = c and y = d and y = e and y = f and y = g and y = h and y = i and y = j", 252 // we don't care what the order is, just don't error in join planning 253 order: [][]string{{"xy", "uv"}, {"uv", "xy"}}, 254 }, 255 }, 256 }, 257 { 258 name: "int type mismatch doesn't error", 259 setup: []string{ 260 "create table xy (x int primary key, y int, key (y,x))", 261 "insert into xy select col0, col1 from normal_dist(1, 10, 0,10)", 262 "create table uv (u double primary key, v float, w decimal, key (v,u), key(w))", 263 "insert into uv select col0, col1, col2 from normal_dist(2, 20, 0,10)", 264 "analyze table xy, uv", 265 }, 266 tests: []JoinPlanTest{ 267 { 268 q: "select * from uv join xy on y = v", 269 // we don't care what the order is, just don't error in join planning 270 order: [][]string{{"xy", "uv"}, {"uv", "xy"}}, 271 }, 272 { 273 q: "select * from uv join xy on y = u", 274 // we don't care what the order is, just don't error in join planning 275 order: [][]string{{"xy", "uv"}, {"uv", "xy"}}, 276 }, 277 { 278 q: "select * from uv join xy on y = w", 279 // we don't care what the order is, just don't error in join planning 280 order: [][]string{{"xy", "uv"}, {"uv", "xy"}}, 281 }, 282 }, 283 }, 284 { 285 name: "float type mismatch doesn't error", 286 setup: []string{ 287 "create table xy (x double primary key, y double, key (y,x))", 288 "insert into xy select col0, col1 from normal_dist(1, 10, 0,10)", 289 "create table uv (u double primary key, v double, w decimal, key (v,u), key(w))", 290 "insert into uv select col0, col1, col2 from normal_dist(2, 20, 0,10)", 291 "analyze table xy, uv", 292 }, 293 tests: []JoinPlanTest{ 294 { 295 q: "select * from uv join xy on y = v", 296 // we don't care what the order is, just don't error in join planning 297 order: [][]string{{"xy", "uv"}, {"uv", "xy"}}, 298 }, 299 { 300 q: "select * from uv join xy on y = u", 301 // we don't care what the order is, just don't error in join planning 302 order: [][]string{{"xy", "uv"}, {"uv", "xy"}}, 303 }, 304 { 305 q: "select * from uv join xy on y = w", 306 // we don't care what the order is, just don't error in join planning 307 order: [][]string{{"xy", "uv"}, {"uv", "xy"}}, 308 }, 309 }, 310 }, 311 { 312 name: "decimal type mismatch doesn't error", 313 setup: []string{ 314 "create table xy (x decimal primary key, y decimal, key (y,x))", 315 "insert into xy select col0, col1 from normal_dist(1, 10, 0,10)", 316 "create table uv (u double primary key, v double, w decimal, key (v,u), key(w))", 317 "insert into uv select col0, col1, col2 from normal_dist(2, 20, 0,10)", 318 "analyze table xy, uv", 319 }, 320 tests: []JoinPlanTest{ 321 { 322 q: "select * from uv join xy on y = v", 323 // we don't care what the order is, just don't error in join planning 324 order: [][]string{{"xy", "uv"}, {"uv", "xy"}}, 325 }, 326 { 327 q: "select * from uv join xy on y = u", 328 // we don't care what the order is, just don't error in join planning 329 order: [][]string{{"xy", "uv"}, {"uv", "xy"}}, 330 }, 331 { 332 q: "select * from uv join xy on y = w", 333 // we don't care what the order is, just don't error in join planning 334 order: [][]string{{"xy", "uv"}, {"uv", "xy"}}, 335 }, 336 }, 337 }, 338 } 339 340 func NewTestProvider(dbProvider *sql.MutableDatabaseProvider, tf ...sql.TableFunction) *TestProvider { 341 tfs := make(map[string]sql.TableFunction) 342 for _, tf := range tf { 343 tfs[strings.ToLower(tf.Name())] = tf 344 } 345 return &TestProvider{ 346 *dbProvider, 347 tfs, 348 } 349 } 350 351 var _ sql.FunctionProvider = (*TestProvider)(nil) 352 353 type TestProvider struct { 354 sql.MutableDatabaseProvider 355 tableFunctions map[string]sql.TableFunction 356 } 357 358 func (t TestProvider) Function(_ *sql.Context, name string) (sql.Function, error) { 359 return nil, sql.ErrFunctionNotFound.New(name) 360 } 361 362 func (t TestProvider) TableFunction(_ *sql.Context, name string) (sql.TableFunction, error) { 363 if tf, ok := t.tableFunctions[strings.ToLower(name)]; ok { 364 return tf, nil 365 } 366 367 return nil, sql.ErrTableFunctionNotFound.New(name) 368 } 369 370 func (t TestProvider) WithTableFunctions(fns ...sql.TableFunction) (sql.TableFunctionProvider, error) { 371 return t, nil 372 }