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  }