github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/sqle/sqlselect_test.go (about)

     1  // Copyright 2020 Dolthub, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package sqle
    16  
    17  import (
    18  	"context"
    19  	"testing"
    20  	"time"
    21  
    22  	"github.com/dolthub/go-mysql-server/sql"
    23  	gmstypes "github.com/dolthub/go-mysql-server/sql/types"
    24  	"github.com/stretchr/testify/assert"
    25  	"github.com/stretchr/testify/require"
    26  
    27  	"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
    28  	"github.com/dolthub/dolt/go/libraries/doltcore/doltdb/durable"
    29  	"github.com/dolthub/dolt/go/libraries/doltcore/dtestutils"
    30  	"github.com/dolthub/dolt/go/libraries/doltcore/env"
    31  	"github.com/dolthub/dolt/go/libraries/doltcore/ref"
    32  	"github.com/dolthub/dolt/go/libraries/doltcore/row"
    33  	"github.com/dolthub/dolt/go/libraries/doltcore/schema"
    34  	"github.com/dolthub/dolt/go/libraries/doltcore/schema/typeinfo"
    35  	"github.com/dolthub/dolt/go/libraries/doltcore/sqle/dtables"
    36  	"github.com/dolthub/dolt/go/libraries/doltcore/sqle/json"
    37  	"github.com/dolthub/dolt/go/store/datas"
    38  	"github.com/dolthub/dolt/go/store/types"
    39  )
    40  
    41  // Set to the name of a single test to run just that test, useful for debugging
    42  const singleSelectQueryTest = "" //"Natural join with join clause"
    43  
    44  // Set to false to run tests known to be broken
    45  const skipBrokenSelect = true
    46  
    47  // Structure for a test of a select query
    48  type SelectTest struct {
    49  	// The name of this test. Names should be unique and descriptive.
    50  	Name string
    51  	// The query to run, excluding an ending semicolon
    52  	Query string
    53  	// The schema of the result of the query, nil if an error is expected
    54  	ExpectedSchema schema.Schema
    55  	// The schema of the result of the query, nil if an error is expected. Mutually exclusive with ExpectedSchema. Use if
    56  	// the schema is difficult to specify with dolt schemas.
    57  	ExpectedSqlSchema sql.Schema
    58  	// The rows this query should return, nil if an error is expected
    59  	ExpectedRows []sql.Row
    60  	// An expected error string
    61  	ExpectedErr string
    62  	// Setup logic to run before executing this test, after initial tables have been created and populated
    63  	AdditionalSetup SetupFn
    64  	// Whether to skip this test on SqlEngine (go-mysql-server) execution.
    65  	// Over time, this should become false for every query.
    66  	SkipOnSqlEngine bool
    67  }
    68  
    69  // We are doing structural equality tests on time.Time values in some of these
    70  // tests. The SQL value layer works with times in time.Local location, but
    71  // go standard library will return different values (which will have the same
    72  // behavior) depending on whether detailed timezone information has been loaded
    73  // for time.Local already. Here, we always load the detailed information so
    74  // that our structural equality tests will be reliable.
    75  var loadedLocalLocation *time.Location
    76  
    77  func LoadedLocalLocation() *time.Location {
    78  	var err error
    79  	loadedLocalLocation, err = time.LoadLocation(time.Local.String())
    80  	if err != nil {
    81  		panic(err)
    82  	}
    83  	if loadedLocalLocation == nil {
    84  		panic("nil LoadedLocalLocation " + time.Local.String())
    85  	}
    86  	return loadedLocalLocation
    87  }
    88  
    89  // BasicSelectTests cover basic select statement features and error handling
    90  func BasicSelectTests() []SelectTest {
    91  	var headCommitHash string
    92  	switch types.Format_Default {
    93  	case types.Format_DOLT:
    94  		headCommitHash = "ias4mf52sgeig337ce2le7ov9vpltppr"
    95  	case types.Format_LD_1:
    96  		headCommitHash = "73hc2robs4v0kt9taoe3m5hd49dmrgun"
    97  	}
    98  
    99  	return []SelectTest{
   100  		{
   101  			Name:           "select * on primary key",
   102  			Query:          "select * from people where id = 2",
   103  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Bart),
   104  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   105  		},
   106  		{
   107  			Name:           "select * ",
   108  			Query:          "select * from people",
   109  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Homer, Marge, Bart, Lisa, Moe, Barney),
   110  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   111  		},
   112  		{
   113  			Name:           "select *, limit 1",
   114  			Query:          "select * from people limit 1",
   115  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Homer),
   116  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   117  		},
   118  		{
   119  			Name:           "select *, limit 1 offset 0",
   120  			Query:          "select * from people limit 0,1",
   121  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Homer),
   122  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   123  		},
   124  		{
   125  			Name:           "select *, limit 1 offset 1",
   126  			Query:          "select * from people limit 1 offset 1;",
   127  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Marge),
   128  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   129  		},
   130  		{
   131  			Name:           "select *, limit 1 offset 5",
   132  			Query:          "select * from people limit 5,1",
   133  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Barney),
   134  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   135  		},
   136  		{
   137  			Name:           "select *, limit 1 offset 6",
   138  			Query:          "select * from people limit 6,1",
   139  			ExpectedRows:   ToSqlRows(PeopleTestSchema),
   140  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   141  		},
   142  		{
   143  			Name:           "select *, limit 0",
   144  			Query:          "select * from people limit 0",
   145  			ExpectedRows:   ToSqlRows(PeopleTestSchema),
   146  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   147  		},
   148  		{
   149  			Name:           "select *, limit 0 offset 0",
   150  			Query:          "select * from people limit 0,0",
   151  			ExpectedRows:   ToSqlRows(PeopleTestSchema),
   152  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   153  		},
   154  		{
   155  			Name:        "select *, limit -1",
   156  			Query:       "select * from people limit -1",
   157  			ExpectedErr: "Limit must be >= 0 if supplied",
   158  		},
   159  		{
   160  			Name:        "select *, offset -1",
   161  			Query:       "select * from people limit -1,1",
   162  			ExpectedErr: "Offset must be >= 0 if supplied",
   163  		},
   164  		{
   165  			Name:           "select *, limit 100",
   166  			Query:          "select * from people limit 100",
   167  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Homer, Marge, Bart, Lisa, Moe, Barney),
   168  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   169  		},
   170  		{
   171  			Name:           "select *, where < int",
   172  			Query:          "select * from people where age < 40",
   173  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Marge, Bart, Lisa),
   174  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   175  		},
   176  		{
   177  			Name:           "select *, where < int, limit 1",
   178  			Query:          "select * from people where age < 40 limit 1",
   179  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Marge),
   180  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   181  		},
   182  		{
   183  			Name:           "select *, where < int, limit 2",
   184  			Query:          "select * from people where age < 40 limit 2",
   185  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Marge, Bart),
   186  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   187  		},
   188  		{
   189  			Name:           "select *, where < int, limit 100",
   190  			Query:          "select * from people where age < 40 limit 100",
   191  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Marge, Bart, Lisa),
   192  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   193  		},
   194  		{
   195  			Name:           "select *, order by int",
   196  			Query:          "select * from people order by id",
   197  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Homer, Marge, Bart, Lisa, Moe, Barney),
   198  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   199  		},
   200  		{
   201  			Name:           "select *, order by int desc",
   202  			Query:          "select * from people order by id desc",
   203  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Barney, Moe, Lisa, Bart, Marge, Homer),
   204  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   205  		},
   206  		{
   207  			Name:           "select *, order by float",
   208  			Query:          "select * from people order by rating",
   209  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Barney, Moe, Marge, Homer, Bart, Lisa),
   210  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   211  		},
   212  		{
   213  			Name:           "select *, order by string",
   214  			Query:          "select * from people order by first_name",
   215  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Barney, Bart, Homer, Lisa, Marge, Moe),
   216  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   217  		},
   218  		{
   219  			Name:           "select *, order by string,string",
   220  			Query:          "select * from people order by last_name desc, first_name asc",
   221  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Moe, Bart, Homer, Lisa, Marge, Barney),
   222  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   223  		},
   224  		{
   225  			Name:           "select *, order by with limit",
   226  			Query:          "select * from people order by first_name limit 2",
   227  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Barney, Bart),
   228  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   229  		},
   230  		{
   231  			Name:           "select *, order by string,string with limit",
   232  			Query:          "select * from people order by last_name desc, first_name asc limit 2",
   233  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Moe, Bart),
   234  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   235  		},
   236  		{
   237  			Name:           "select *, where > int reversed",
   238  			Query:          "select * from people where 40 > age",
   239  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Marge, Bart, Lisa),
   240  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   241  		},
   242  		{
   243  			Name:           "select *, where <= int",
   244  			Query:          "select * from people where age <= 40",
   245  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Homer, Marge, Bart, Lisa, Barney),
   246  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   247  		},
   248  		{
   249  			Name:           "select *, where >= int reversed",
   250  			Query:          "select * from people where 40 >= age",
   251  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Homer, Marge, Bart, Lisa, Barney),
   252  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   253  		},
   254  		{
   255  			Name:           "select *, where > int",
   256  			Query:          "select * from people where age > 40",
   257  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Moe),
   258  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   259  		},
   260  		{
   261  			Name:           "select *, where < int reversed",
   262  			Query:          "select * from people where 40 < age",
   263  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Moe),
   264  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   265  		},
   266  		{
   267  			Name:           "select *, where >= int",
   268  			Query:          "select * from people where age >= 40",
   269  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Homer, Moe, Barney),
   270  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   271  		},
   272  		{
   273  			Name:           "select *, where <= int reversed",
   274  			Query:          "select * from people where 40 <= age",
   275  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Homer, Moe, Barney),
   276  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   277  		},
   278  		{
   279  			Name:           "select *, where > string",
   280  			Query:          "select * from people where last_name > 'Simpson'",
   281  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Moe),
   282  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   283  		},
   284  		{
   285  			Name:           "select *, where < string",
   286  			Query:          "select * from people where last_name < 'Simpson'",
   287  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Barney),
   288  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   289  		},
   290  		{
   291  			Name:           "select *, where = string",
   292  			Query:          "select * from people where last_name = 'Simpson'",
   293  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Homer, Marge, Bart, Lisa),
   294  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   295  		},
   296  		{
   297  			Name:           "select *, where > float",
   298  			Query:          "select * from people where rating > 8.0 order by id",
   299  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Homer, Bart, Lisa),
   300  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   301  		},
   302  		{
   303  			Name:           "select *, where < float",
   304  			Query:          "select * from people where rating < 8.0",
   305  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Moe, Barney),
   306  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   307  		},
   308  		{
   309  			Name:           "select *, where = float",
   310  			Query:          "select * from people where rating = 8.0",
   311  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Marge),
   312  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   313  		},
   314  		{
   315  			Name:           "select *, where < float reversed",
   316  			Query:          "select * from people where 8.0 < rating",
   317  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Homer, Bart, Lisa),
   318  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   319  		},
   320  		{
   321  			Name:           "select *, where > float reversed",
   322  			Query:          "select * from people where 8.0 > rating",
   323  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Moe, Barney),
   324  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   325  		},
   326  		{
   327  			Name:           "select *, where = float reversed",
   328  			Query:          "select * from people where 8.0 = rating",
   329  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Marge),
   330  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   331  		},
   332  		{
   333  			Name:           "select *, where bool = ",
   334  			Query:          "select * from people where is_married = true",
   335  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Homer, Marge),
   336  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   337  		},
   338  		{
   339  			Name:           "select *, where bool = false ",
   340  			Query:          "select * from people where is_married = false",
   341  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Bart, Lisa, Moe, Barney),
   342  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   343  		},
   344  		{
   345  			Name:           "select *, where bool <> ",
   346  			Query:          "select * from people where is_married <> false",
   347  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Homer, Marge),
   348  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   349  		},
   350  		{
   351  			Name:           "select *, where bool",
   352  			Query:          "select * from people where is_married",
   353  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Homer, Marge),
   354  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   355  		},
   356  		{
   357  			Name:           "select *, and clause",
   358  			Query:          "select * from people where is_married and age > 38",
   359  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Homer),
   360  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   361  		},
   362  		{
   363  			Name:           "select *, or clause",
   364  			Query:          "select * from people where is_married or age < 20",
   365  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Homer, Marge, Bart, Lisa),
   366  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   367  		},
   368  		{
   369  			Name:           "select *, in clause string",
   370  			Query:          "select * from people where first_name in ('Homer', 'Marge')",
   371  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Homer, Marge),
   372  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   373  		},
   374  		{
   375  			Name:           "select *, in clause integer",
   376  			Query:          "select * from people where age in (-10, 40)",
   377  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Homer, Barney),
   378  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   379  		},
   380  		{
   381  			Name:           "select *, in clause float",
   382  			Query:          "select * from people where rating in (-10.0, 8.5)",
   383  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Homer),
   384  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   385  		},
   386  		{
   387  			Name:           "select *, in clause, mixed types",
   388  			Query:          "select * from people where first_name in ('Homer', 40)",
   389  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   390  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Homer),
   391  		},
   392  		{
   393  			Name:           "select *, in clause, mixed numeric types",
   394  			Query:          "select * from people where age in (-10.0, 40)",
   395  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   396  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Homer, Barney),
   397  		},
   398  		{
   399  			Name:           "select *, not in clause",
   400  			Query:          "select * from people where first_name not in ('Homer', 'Marge')",
   401  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Bart, Lisa, Moe, Barney),
   402  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   403  		},
   404  		{
   405  			Name:           "select *, in clause single element",
   406  			Query:          "select * from people where first_name in ('Homer')",
   407  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Homer),
   408  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   409  		},
   410  		{
   411  			Name:           "select *, in clause single type mismatch",
   412  			Query:          "select * from people where first_name in (1.0)",
   413  			ExpectedRows:   ToSqlRows(PeopleTestSchema),
   414  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   415  		},
   416  		{
   417  			Name:           "select *, is null clause ",
   418  			Query:          "select * from people where uuid is null",
   419  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Homer),
   420  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   421  		},
   422  		{
   423  			Name:           "select *, is not null clause ",
   424  			Query:          "select * from people where uuid is not null",
   425  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Marge, Bart, Lisa, Moe, Barney),
   426  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   427  		},
   428  		{
   429  			Name:           "select *, is true clause ",
   430  			Query:          "select * from people where is_married is true",
   431  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Homer, Marge),
   432  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   433  		},
   434  		{
   435  			Name:           "select *, is not true clause ",
   436  			Query:          "select * from people where is_married is not true",
   437  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Bart, Lisa, Moe, Barney),
   438  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   439  		},
   440  		{
   441  			Name:           "select *, is false clause ",
   442  			Query:          "select * from people where is_married is false",
   443  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Bart, Lisa, Moe, Barney),
   444  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   445  		},
   446  		{
   447  			Name:           "select *, is not false clause ",
   448  			Query:          "select * from people where is_married is not false",
   449  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Homer, Marge),
   450  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   451  		},
   452  		{
   453  			Name:           "select *, is true clause on non-bool column",
   454  			Query:          "select * from people where age is true",
   455  			ExpectedRows:   ToSqlRows(PeopleTestSchema, AllPeopleRows...),
   456  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   457  		},
   458  		{
   459  			Name:           "binary expression in select",
   460  			Query:          "select age + 1 as a from people where is_married order by a",
   461  			ExpectedRows:   ToSqlRows(NewResultSetSchema("a", types.IntKind), NewResultSetRow(types.Int(39)), NewResultSetRow(types.Int(41))),
   462  			ExpectedSchema: NewResultSetSchema("a", types.IntKind),
   463  		},
   464  		{
   465  			Name:         "and expression in select",
   466  			Query:        "select is_married and age >= 40 from people where last_name = 'Simpson' order by id limit 2",
   467  			ExpectedRows: []sql.Row{{true}, {false}},
   468  			ExpectedSqlSchema: sql.Schema{
   469  				&sql.Column{Name: "is_married and age >= 40", Type: gmstypes.Boolean},
   470  			},
   471  		},
   472  		{
   473  			Name:  "or expression in select",
   474  			Query: "select first_name, age <= 10 or age >= 40 as not_marge from people where last_name = 'Simpson' order by id desc",
   475  			ExpectedRows: []sql.Row{
   476  				{"Lisa", true},
   477  				{"Bart", true},
   478  				{"Marge", false},
   479  				{"Homer", true},
   480  			},
   481  			ExpectedSqlSchema: sql.Schema{
   482  				&sql.Column{Name: "first_name", Type: typeinfo.StringDefaultType.ToSqlType()},
   483  				&sql.Column{Name: "not_marge", Type: gmstypes.Boolean},
   484  			},
   485  		},
   486  		{
   487  			Name:           "unary expression in select",
   488  			Query:          "select -age as age from people where is_married order by age",
   489  			ExpectedRows:   ToSqlRows(NewResultSetSchema("age", types.IntKind), NewResultSetRow(types.Int(-40)), NewResultSetRow(types.Int(-38))),
   490  			ExpectedSchema: NewResultSetSchema("age", types.IntKind),
   491  		},
   492  		{
   493  			Name:            "unary expression in select, alias named after column",
   494  			Query:           "select -age as age from people where is_married order by people.age",
   495  			ExpectedRows:    ToSqlRows(NewResultSetSchema("age", types.IntKind), NewResultSetRow(types.Int(-38)), NewResultSetRow(types.Int(-40))),
   496  			ExpectedSchema:  NewResultSetSchema("age", types.IntKind),
   497  			SkipOnSqlEngine: true, // this seems to be a bug in the engine
   498  		},
   499  		{
   500  			Name:           "select *, -column",
   501  			Query:          "select * from people where -rating = -8.5",
   502  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Homer),
   503  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   504  		},
   505  		{
   506  			Name:           "select *, -column, string type",
   507  			Query:          "select * from people where -first_name = 'Homer'",
   508  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   509  			ExpectedRows:   ToSqlRows(PeopleTestSchema, AllPeopleRows...), // A little weird, but correct due to mysql type conversion rules (both expression evaluate to 0 after conversion)
   510  		},
   511  		{
   512  			Name:           "select *, binary + in where",
   513  			Query:          "select * from people where age + 1 = 41",
   514  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Homer, Barney),
   515  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   516  		},
   517  		{
   518  			Name:           "select *, binary - in where",
   519  			Query:          "select * from people where age - 1 = 39",
   520  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Homer, Barney),
   521  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   522  		},
   523  		{
   524  			Name:           "select *, binary / in where",
   525  			Query:          "select * from people where age / 2 = 20",
   526  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Homer, Barney),
   527  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   528  		},
   529  		{
   530  			Name:           "select *, binary * in where",
   531  			Query:          "select * from people where age * 2 = 80",
   532  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Homer, Barney),
   533  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   534  		},
   535  		{
   536  			Name:           "select *, binary % in where",
   537  			Query:          "select * from people where age % 4 = 0",
   538  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Homer, Lisa, Moe, Barney),
   539  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   540  		},
   541  		{
   542  			Name:           "select *, complex binary expr in where",
   543  			Query:          "select * from people where age / 4 + 2 * 2 = 14",
   544  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Homer, Barney),
   545  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   546  		},
   547  		{
   548  			Name:           "select *, binary + in where type mismatch",
   549  			Query:          "select * from people where first_name + 1 = 41",
   550  			ExpectedRows:   ToSqlRows(PeopleTestSchema),
   551  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   552  		},
   553  		{
   554  			Name:           "select *, binary - in where type mismatch",
   555  			Query:          "select * from people where first_name - 1 = 39",
   556  			ExpectedRows:   ToSqlRows(PeopleTestSchema),
   557  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   558  		},
   559  		{
   560  			Name:           "select *, binary / in where type mismatch",
   561  			Query:          "select * from people where first_name / 2 = 20",
   562  			ExpectedRows:   ToSqlRows(PeopleTestSchema),
   563  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   564  		},
   565  		{
   566  			Name:           "select *, binary * in where type mismatch",
   567  			Query:          "select * from people where first_name * 2 = 80",
   568  			ExpectedRows:   ToSqlRows(PeopleTestSchema),
   569  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   570  		},
   571  		{
   572  			Name:           "select *, binary % in where type mismatch",
   573  			Query:          "select * from people where first_name % 4 = 0",
   574  			ExpectedRows:   ToSqlRows(PeopleTestSchema, AllPeopleRows...), // invalid value is considered as 0
   575  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   576  		},
   577  		{
   578  			Name:           "select * with where, order by",
   579  			Query:          "select * from people where `uuid` is not null and first_name <> 'Marge' order by last_name desc, age",
   580  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Moe, Lisa, Bart, Barney),
   581  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   582  		},
   583  		{
   584  			Name:           "select subset of cols",
   585  			Query:          "select first_name, last_name from people where age >= 40",
   586  			ExpectedRows:   ToSqlRows(SubsetSchema(PeopleTestSchema, "first_name", "last_name"), Homer, Moe, Barney),
   587  			ExpectedSchema: CompressSchema(PeopleTestSchema, "first_name", "last_name"),
   588  		},
   589  		{
   590  			Name:           "column aliases",
   591  			Query:          "select first_name as f, last_name as l from people where age >= 40",
   592  			ExpectedRows:   ToSqlRows(SubsetSchema(PeopleTestSchema, "first_name", "last_name"), Homer, Moe, Barney),
   593  			ExpectedSchema: NewResultSetSchema("f", types.StringKind, "l", types.StringKind),
   594  		},
   595  		{
   596  			Name:           "duplicate column aliases",
   597  			Query:          "select first_name as f, last_name as f from people where age >= 40",
   598  			ExpectedRows:   ToSqlRows(SubsetSchema(PeopleTestSchema, "first_name", "last_name"), Homer, Moe, Barney),
   599  			ExpectedSchema: NewResultSetSchema("f", types.StringKind, "f", types.StringKind),
   600  		},
   601  		{
   602  			Name:  "column selected more than once",
   603  			Query: "select first_name, first_name from people where age >= 40 order by id",
   604  			ExpectedRows: []sql.Row{
   605  				{"Homer", "Homer"},
   606  				{"Moe", "Moe"},
   607  				{"Barney", "Barney"},
   608  			},
   609  			ExpectedSchema: NewResultSetSchema("first_name", types.StringKind, "first_name", types.StringKind),
   610  		},
   611  		{
   612  			Name:        "duplicate table selection",
   613  			Query:       "select first_name as f, last_name as f from people, people where age >= 40",
   614  			ExpectedErr: "Non-unique table name / alias: people",
   615  		},
   616  		{
   617  			Name:        "duplicate table alias",
   618  			Query:       "select * from people p, people p where age >= 40",
   619  			ExpectedErr: "Non-unique table name / alias: 'p'",
   620  		},
   621  		{
   622  			Name:            "column aliases in where clause",
   623  			Query:           `select first_name as f, last_name as l from people where f = "Homer"`,
   624  			ExpectedErr:     "Unknown column: 'f'",
   625  			SkipOnSqlEngine: true, // this is actually a bug (aliases aren't usable in filters)
   626  		},
   627  		{
   628  			Name:           "select subset of columns with order by",
   629  			Query:          "select first_name from people order by age, first_name",
   630  			ExpectedRows:   ToSqlRows(SubsetSchema(PeopleTestSchema, "first_name"), Lisa, Bart, Marge, Barney, Homer, Moe),
   631  			ExpectedSchema: CompressSchema(PeopleTestSchema, "first_name"),
   632  		},
   633  		{
   634  			Name:           "column aliases with order by",
   635  			Query:          "select first_name as f from people order by age, f",
   636  			ExpectedRows:   ToSqlRows(SubsetSchema(PeopleTestSchema, "first_name"), Lisa, Bart, Marge, Barney, Homer, Moe),
   637  			ExpectedSchema: NewResultSetSchema("f", types.StringKind),
   638  		},
   639  		{
   640  			Name:            "ambiguous column in order by",
   641  			Query:           "select first_name as f, last_name as f from people order by f",
   642  			ExpectedErr:     "Ambiguous column: 'f'",
   643  			SkipOnSqlEngine: true, // this is a bug in go-mysql-server
   644  		},
   645  		{
   646  			Name:           "table aliases",
   647  			Query:          "select p.first_name as f, p.last_name as l from people p where p.first_name = 'Homer'",
   648  			ExpectedRows:   ToSqlRows(SubsetSchema(PeopleTestSchema, "first_name", "last_name"), Homer),
   649  			ExpectedSchema: NewResultSetSchema("f", types.StringKind, "l", types.StringKind),
   650  		},
   651  		{
   652  			Name:           "table aliases without column aliases",
   653  			Query:          "select p.first_name, p.last_name from people p where p.first_name = 'Homer'",
   654  			ExpectedRows:   ToSqlRows(SubsetSchema(PeopleTestSchema, "first_name", "last_name"), Homer),
   655  			ExpectedSchema: NewResultSetSchema("first_name", types.StringKind, "last_name", types.StringKind),
   656  		},
   657  		{
   658  			Name:        "table aliases with bad alias",
   659  			Query:       "select m.first_name as f, p.last_name as l from people p where p.f = 'Homer'",
   660  			ExpectedErr: "Unknown table: 'm'",
   661  		},
   662  		{
   663  			Name: "column aliases, all columns",
   664  			Query: `select id as i, first_name as f, last_name as l, is_married as m, age as a,
   665  						rating as r, uuid as u, num_episodes as n from people
   666  						where age >= 40`,
   667  			ExpectedRows: ToSqlRows(PeopleTestSchema, Homer, Moe, Barney),
   668  			ExpectedSchema: NewResultSetSchema("i", types.IntKind, "f", types.StringKind,
   669  				"l", types.StringKind, "m", types.IntKind, "a", types.IntKind, "r", types.FloatKind,
   670  				"u", types.StringKind, "n", types.UintKind),
   671  		},
   672  		{
   673  			Name:           "select *, not equals",
   674  			Query:          "select * from people where age <> 40",
   675  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Marge, Bart, Lisa, Moe),
   676  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   677  		},
   678  		{
   679  			Name:           "empty result set",
   680  			Query:          "select * from people where age > 80",
   681  			ExpectedRows:   ToSqlRows(PeopleTestSchema),
   682  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   683  		},
   684  		{
   685  			Name:           "empty result set with columns",
   686  			Query:          "select id, age from people where age > 80",
   687  			ExpectedRows:   ToSqlRows(PeopleTestSchema),
   688  			ExpectedSchema: CompressSchema(PeopleTestSchema, "id", "age"),
   689  		},
   690  		{
   691  			Name:        "unknown table",
   692  			Query:       "select * from dne",
   693  			ExpectedErr: `Unknown table: 'dne'`,
   694  		},
   695  		{
   696  			Name:        "unknown diff table",
   697  			Query:       "select * from dolt_diff_dne",
   698  			ExpectedErr: `Unknown table: 'dolt_diff_dne'`,
   699  		},
   700  		{
   701  			Name:        "unknown diff table",
   702  			Query:       "select * from dolt_commit_diff_dne",
   703  			ExpectedErr: `Unknown table: 'dolt_commit_diff_dne'`,
   704  		},
   705  		{
   706  			Name:        "unknown history table",
   707  			Query:       "select * from dolt_history_dne",
   708  			ExpectedErr: `Unknown table: 'dolt_history_dne'`,
   709  		},
   710  		{
   711  			Name:        "unknown table in join",
   712  			Query:       "select * from people join dne",
   713  			ExpectedErr: `Unknown table: 'dne'`,
   714  		},
   715  		{
   716  			Name:  "no table",
   717  			Query: "select 1",
   718  			ExpectedSqlSchema: sql.Schema{
   719  				&sql.Column{
   720  					Name: "1",
   721  					Type: gmstypes.Int8,
   722  				},
   723  			},
   724  			ExpectedRows: []sql.Row{{int8(1)}},
   725  		},
   726  		{
   727  			Name:        "unknown column in where",
   728  			Query:       "select * from people where dne > 8.0",
   729  			ExpectedErr: `Unknown column: 'dne'`,
   730  		},
   731  		{
   732  			Name:        "unknown column in order by",
   733  			Query:       "select * from people where rating > 8.0 order by dne",
   734  			ExpectedErr: `Unknown column: 'dne'`,
   735  		},
   736  		{
   737  			Name:        "unsupported comparison",
   738  			Query:       "select * from people where function(first_name)",
   739  			ExpectedErr: "not supported",
   740  		},
   741  		{
   742  			Name:           "type mismatch in where clause",
   743  			Query:          `select * from people where id = "0"`,
   744  			ExpectedSchema: CompressSchema(PeopleTestSchema),
   745  			ExpectedRows:   ToSqlRows(PeopleTestSchema, Homer),
   746  		},
   747  		{
   748  			Name:  "select * from log system table",
   749  			Query: "select * from dolt_log",
   750  			ExpectedRows: []sql.Row{
   751  				{
   752  					headCommitHash,
   753  					"billy bob",
   754  					"bigbillieb@fake.horse",
   755  					time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC).In(LoadedLocalLocation()),
   756  					"Initialize data repository",
   757  				},
   758  			},
   759  			ExpectedSqlSchema: sql.Schema{
   760  				&sql.Column{Name: "commit_hash", Type: gmstypes.Text},
   761  				&sql.Column{Name: "committer", Type: gmstypes.Text},
   762  				&sql.Column{Name: "email", Type: gmstypes.Text},
   763  				&sql.Column{Name: "date", Type: gmstypes.Datetime},
   764  				&sql.Column{Name: "message", Type: gmstypes.Text},
   765  			},
   766  		},
   767  		{
   768  			Name:         "select * from conflicts system table",
   769  			Query:        "select * from dolt_conflicts",
   770  			ExpectedRows: []sql.Row{},
   771  			ExpectedSqlSchema: sql.Schema{
   772  				&sql.Column{Name: "table", Type: gmstypes.Text},
   773  				&sql.Column{Name: "num_conflicts", Type: gmstypes.Uint64},
   774  			},
   775  		},
   776  		{
   777  			Name:  "select * from branches system table",
   778  			Query: "select * from dolt_branches",
   779  			ExpectedRows: []sql.Row{
   780  				{
   781  					env.DefaultInitBranch,
   782  					headCommitHash,
   783  					"billy bob", "bigbillieb@fake.horse",
   784  					time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC).In(LoadedLocalLocation()),
   785  					"Initialize data repository",
   786  					"",
   787  					"",
   788  				},
   789  			},
   790  			ExpectedSqlSchema: sql.Schema{
   791  				&sql.Column{Name: "name", Type: gmstypes.Text},
   792  				&sql.Column{Name: "hash", Type: gmstypes.Text},
   793  				&sql.Column{Name: "latest_committer", Type: gmstypes.Text},
   794  				&sql.Column{Name: "latest_committer_email", Type: gmstypes.Text},
   795  				&sql.Column{Name: "latest_commit_date", Type: gmstypes.Datetime},
   796  				&sql.Column{Name: "latest_commit_message", Type: gmstypes.Text},
   797  				&sql.Column{Name: "remote", Type: gmstypes.Text},
   798  				&sql.Column{Name: "branch", Type: gmstypes.Text},
   799  			},
   800  		},
   801  	}
   802  }
   803  
   804  var sqlDiffSchema = sql.Schema{
   805  	&sql.Column{Name: "to_id", Type: gmstypes.Int64},
   806  	&sql.Column{Name: "to_first_name", Type: typeinfo.StringDefaultType.ToSqlType()},
   807  	&sql.Column{Name: "to_last_name", Type: typeinfo.StringDefaultType.ToSqlType()},
   808  	&sql.Column{Name: "to_addr", Type: typeinfo.StringDefaultType.ToSqlType()},
   809  	&sql.Column{Name: "from_id", Type: gmstypes.Int64},
   810  	&sql.Column{Name: "from_first_name", Type: typeinfo.StringDefaultType.ToSqlType()},
   811  	&sql.Column{Name: "from_last_name", Type: typeinfo.StringDefaultType.ToSqlType()},
   812  	&sql.Column{Name: "from_addr", Type: typeinfo.StringDefaultType.ToSqlType()},
   813  	&sql.Column{Name: "diff_type", Type: typeinfo.StringDefaultType.ToSqlType()},
   814  }
   815  
   816  var SelectDiffTests = []SelectTest{
   817  	{
   818  		Name:  "select from diff system table",
   819  		Query: "select to_id, to_first_name, to_last_name, to_addr, from_id, from_first_name, from_last_name, from_addr, diff_type from dolt_diff_test_table",
   820  		ExpectedRows: ToSqlRows(DiffSchema,
   821  			mustRow(row.New(types.Format_Default, DiffSchema, row.TaggedValues{0: types.Int(6), 1: types.String("Katie"), 2: types.String("McCulloch"), 14: types.String("added")})),
   822  			mustRow(row.New(types.Format_Default, DiffSchema, row.TaggedValues{0: types.Int(0), 1: types.String("Aaron"), 2: types.String("Son"), 3: types.String("123 Fake St"), 7: types.Int(0), 8: types.String("Aaron"), 9: types.String("Son"), 10: types.String("123 Fake St"), 14: types.String("modified")})),
   823  			mustRow(row.New(types.Format_Default, DiffSchema, row.TaggedValues{0: types.Int(1), 1: types.String("Brian"), 2: types.String("Hendriks"), 3: types.String("456 Bull Ln"), 7: types.Int(1), 8: types.String("Brian"), 9: types.String("Hendriks"), 10: types.String("456 Bull Ln"), 14: types.String("modified")})),
   824  			mustRow(row.New(types.Format_Default, DiffSchema, row.TaggedValues{0: types.Int(2), 1: types.String("Tim"), 2: types.String("Sehn"), 3: types.String("789 Not Real Ct"), 7: types.Int(2), 8: types.String("Tim"), 9: types.String("Sehn"), 10: types.String("789 Not Real Ct"), 14: types.String("modified")})),
   825  			mustRow(row.New(types.Format_Default, DiffSchema, row.TaggedValues{0: types.Int(3), 1: types.String("Zach"), 2: types.String("Musgrave"), 3: types.String("-1 Imaginary Wy"), 7: types.Int(3), 8: types.String("Zach"), 9: types.String("Musgrave"), 14: types.String("modified")})),
   826  			mustRow(row.New(types.Format_Default, DiffSchema, row.TaggedValues{0: types.Int(5), 1: types.String("Daylon"), 2: types.String("Wilkins"), 14: types.String("added")})),
   827  			mustRow(row.New(types.Format_Default, DiffSchema, row.TaggedValues{0: types.Int(0), 1: types.String("Aaron"), 2: types.String("Son"), 3: types.String("123 Fake St"), 14: types.String("added")})),
   828  			mustRow(row.New(types.Format_Default, DiffSchema, row.TaggedValues{0: types.Int(1), 1: types.String("Brian"), 2: types.String("Hendriks"), 3: types.String("456 Bull Ln"), 14: types.String("added")})),
   829  			mustRow(row.New(types.Format_Default, DiffSchema, row.TaggedValues{0: types.Int(2), 1: types.String("Tim"), 2: types.String("Sehn"), 3: types.String("789 Not Real Ct"), 14: types.String("added")})),
   830  			mustRow(row.New(types.Format_Default, DiffSchema, row.TaggedValues{0: types.Int(3), 1: types.String("Zach"), 2: types.String("Musgrave"), 14: types.String("added")})),
   831  			mustRow(row.New(types.Format_Default, DiffSchema, row.TaggedValues{0: types.Int(4), 1: types.String("Matt"), 2: types.String("Jesuele"), 14: types.String("added")})),
   832  		),
   833  		ExpectedSqlSchema: sqlDiffSchema,
   834  	},
   835  	{
   836  		Name:  "select from diff system table with to commit",
   837  		Query: "select to_id, to_first_name, to_last_name, to_addr, from_id, from_first_name, from_last_name, from_addr, diff_type from dolt_diff_test_table where to_commit = 'WORKING'",
   838  		ExpectedRows: ToSqlRows(DiffSchema,
   839  			mustRow(row.New(types.Format_Default, DiffSchema, row.TaggedValues{0: types.Int(6), 1: types.String("Katie"), 2: types.String("McCulloch"), 14: types.String("added")})),
   840  		),
   841  		ExpectedSqlSchema: sqlDiffSchema,
   842  	},
   843  	// TODO: fix dependencies to hashof function can be registered and used here, also create branches when generating the history so that different from and to commits can be tested.
   844  	/*{
   845  		Name:  "select from diff system table with from and to commit and test insensitive name",
   846  		Query: "select to_id, to_first_name, to_last_name, to_addr, to_age_4, to_age_5, from_id, from_first_name, from_last_name, from_addr, from_age_4, from_age_5, diff_type from dolt_diff_TeSt_TaBlE where from_commit = 'add-age' and to_commit = 'main'",
   847  		ExpectedRows: ToSqlRows(DiffSchema,
   848  			mustRow(row.New(types.Format_Default, DiffSchema, row.TaggedValues{7: types.Int(0), 8: types.String("Aaron"), 9: types.String("Son"), 11: types.Int(35), 0: types.Int(0), 1: types.String("Aaron"), 2: types.String("Son"), 3: types.String("123 Fake St"), 5: types.Uint(35), 13: types.String("add-age"), 6: types.String("main"), 14: types.String("modified")})),
   849  			mustRow(row.New(types.Format_Default, DiffSchema, row.TaggedValues{7: types.Int(1), 8: types.String("Brian"), 9: types.String("Hendriks"), 11: types.Int(38), 0: types.Int(1), 1: types.String("Brian"), 2: types.String("Hendriks"), 3: types.String("456 Bull Ln"), 5: types.Uint(38), 13: types.String("add-age"), 6: types.String("main"), 14: types.String("modified")})),
   850  			mustRow(row.New(types.Format_Default, DiffSchema, row.TaggedValues{7: types.Int(2), 8: types.String("Tim"), 9: types.String("Sehn"), 11: types.Int(37), 0: types.Int(2), 1: types.String("Tim"), 2: types.String("Sehn"), 3: types.String("789 Not Real Ct"), 5: types.Uint(37), 13: types.String("add-age"), 6: types.String("main"), 14: types.String("modified")})),
   851  			mustRow(row.New(types.Format_Default, DiffSchema, row.TaggedValues{7: types.Int(3), 8: types.String("Zach"), 9: types.String("Musgrave"), 11: types.Int(37), 0: types.Int(3), 1: types.String("Zach"), 2: types.String("Musgrave"), 3: types.String("-1 Imaginary Wy"), 5: types.Uint(37), 13: types.String("add-age"), 6: types.String("main"), 14: types.String("modified")})),
   852  			mustRow(row.New(types.Format_Default, DiffSchema, row.TaggedValues{0: types.Int(4), 1: types.String("Matt"), 2: types.String("Jesuele"), 3: types.NullValue, 13: types.String("add-age"), 6: types.String("main"), 14: types.String("added")})),
   853  			mustRow(row.New(types.Format_Default, DiffSchema, row.TaggedValues{0: types.Int(5), 1: types.String("Daylon"), 2: types.String("Wilkins"), 3: types.NullValue, 13: types.String("add-age"), 6: types.String("main"), 14: types.String("added")})),
   854  		),
   855  		ExpectedSqlSchema: sqlDiffSchema,
   856  	},*/
   857  }
   858  
   859  var AsOfTests = []SelectTest{
   860  	{
   861  		Name:  "select * from seed branch",
   862  		Query: "select * from test_table as of 'seed'",
   863  		ExpectedRows: ToSqlRows(InitialHistSch,
   864  			mustRow(row.New(types.Format_Default, InitialHistSch, row.TaggedValues{0: types.Int(0), 1: types.String("Aaron"), 2: types.String("Son")})),
   865  			mustRow(row.New(types.Format_Default, InitialHistSch, row.TaggedValues{0: types.Int(1), 1: types.String("Brian"), 2: types.String("Hendriks")})),
   866  			mustRow(row.New(types.Format_Default, InitialHistSch, row.TaggedValues{0: types.Int(2), 1: types.String("Tim"), 2: types.String("Sehn")})),
   867  		),
   868  		ExpectedSchema: InitialHistSch,
   869  	},
   870  	{
   871  		Name:  "select * from add-age branch",
   872  		Query: "select * from test_table as of 'add-age'",
   873  		ExpectedRows: ToSqlRows(AddAgeAt4HistSch,
   874  			mustRow(row.New(types.Format_Default, AddAgeAt4HistSch, row.TaggedValues{0: types.Int(0), 1: types.String("Aaron"), 2: types.String("Son"), 4: types.Int(35)})),
   875  			mustRow(row.New(types.Format_Default, AddAgeAt4HistSch, row.TaggedValues{0: types.Int(1), 1: types.String("Brian"), 2: types.String("Hendriks"), 4: types.Int(38)})),
   876  			mustRow(row.New(types.Format_Default, AddAgeAt4HistSch, row.TaggedValues{0: types.Int(2), 1: types.String("Tim"), 2: types.String("Sehn"), 4: types.Int(37)})),
   877  			mustRow(row.New(types.Format_Default, AddAgeAt4HistSch, row.TaggedValues{0: types.Int(3), 1: types.String("Zach"), 2: types.String("Musgrave"), 4: types.Int(37)})),
   878  		),
   879  		ExpectedSchema: AddAgeAt4HistSch,
   880  	},
   881  	{
   882  		Name:  "select * from main branch",
   883  		Query: "select * from test_table as of 'main'",
   884  		ExpectedRows: ToSqlRows(ReaddAgeAt5HistSch,
   885  			mustRow(row.New(types.Format_Default, ReaddAgeAt5HistSch, row.TaggedValues{0: types.Int(0), 1: types.String("Aaron"), 2: types.String("Son"), 3: types.String("123 Fake St"), 5: types.Uint(35)})),
   886  			mustRow(row.New(types.Format_Default, ReaddAgeAt5HistSch, row.TaggedValues{0: types.Int(1), 1: types.String("Brian"), 2: types.String("Hendriks"), 3: types.String("456 Bull Ln"), 5: types.Uint(38)})),
   887  			mustRow(row.New(types.Format_Default, ReaddAgeAt5HistSch, row.TaggedValues{0: types.Int(2), 1: types.String("Tim"), 2: types.String("Sehn"), 3: types.String("789 Not Real Ct"), 5: types.Uint(37)})),
   888  			mustRow(row.New(types.Format_Default, ReaddAgeAt5HistSch, row.TaggedValues{0: types.Int(3), 1: types.String("Zach"), 2: types.String("Musgrave"), 3: types.String("-1 Imaginary Wy"), 5: types.Uint(37)})),
   889  			mustRow(row.New(types.Format_Default, ReaddAgeAt5HistSch, row.TaggedValues{0: types.Int(4), 1: types.String("Matt"), 2: types.String("Jesuele")})),
   890  			mustRow(row.New(types.Format_Default, ReaddAgeAt5HistSch, row.TaggedValues{0: types.Int(5), 1: types.String("Daylon"), 2: types.String("Wilkins")})),
   891  		),
   892  		ExpectedSchema: ReaddAgeAt5HistSch,
   893  	},
   894  	{
   895  		Name:  "select * from HEAD~",
   896  		Query: "select * from test_table as of 'HEAD~'",
   897  		ExpectedRows: ToSqlRows(AddAddrAt3HistSch,
   898  			mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(0), 1: types.String("Aaron"), 2: types.String("Son"), 3: types.String("123 Fake St")})),
   899  			mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(1), 1: types.String("Brian"), 2: types.String("Hendriks"), 3: types.String("456 Bull Ln")})),
   900  			mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(2), 1: types.String("Tim"), 2: types.String("Sehn"), 3: types.String("789 Not Real Ct")})),
   901  			mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(3), 1: types.String("Zach"), 2: types.String("Musgrave")})),
   902  			mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(4), 1: types.String("Matt"), 2: types.String("Jesuele")})),
   903  		),
   904  		ExpectedSchema: AddAddrAt3HistSch,
   905  	},
   906  	{
   907  		Name:  "select * from HEAD^",
   908  		Query: "select * from test_table as of 'HEAD^'",
   909  		ExpectedRows: ToSqlRows(AddAddrAt3HistSch,
   910  			mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(0), 1: types.String("Aaron"), 2: types.String("Son"), 3: types.String("123 Fake St")})),
   911  			mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(1), 1: types.String("Brian"), 2: types.String("Hendriks"), 3: types.String("456 Bull Ln")})),
   912  			mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(2), 1: types.String("Tim"), 2: types.String("Sehn"), 3: types.String("789 Not Real Ct")})),
   913  			mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(3), 1: types.String("Zach"), 2: types.String("Musgrave")})),
   914  			mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(4), 1: types.String("Matt"), 2: types.String("Jesuele")})),
   915  		),
   916  		ExpectedSchema: AddAddrAt3HistSch,
   917  	},
   918  	{
   919  		Name:  "select * from main^",
   920  		Query: "select * from test_table as of 'main^'",
   921  		ExpectedRows: ToSqlRows(AddAddrAt3HistSch,
   922  			mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(0), 1: types.String("Aaron"), 2: types.String("Son"), 3: types.String("123 Fake St")})),
   923  			mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(1), 1: types.String("Brian"), 2: types.String("Hendriks"), 3: types.String("456 Bull Ln")})),
   924  			mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(2), 1: types.String("Tim"), 2: types.String("Sehn"), 3: types.String("789 Not Real Ct")})),
   925  			mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(3), 1: types.String("Zach"), 2: types.String("Musgrave")})),
   926  			mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(4), 1: types.String("Matt"), 2: types.String("Jesuele")})),
   927  		),
   928  		ExpectedSchema: AddAddrAt3HistSch,
   929  	},
   930  	// Because of an implementation detail in the way we process history for test setup, each commit is 2 hours apart.
   931  	{
   932  		Name:  "select * from timestamp after HEAD",
   933  		Query: "select * from test_table as of CONVERT('1970-01-01 10:00:00', DATETIME)",
   934  		ExpectedRows: ToSqlRows(ReaddAgeAt5HistSch,
   935  			mustRow(row.New(types.Format_Default, ReaddAgeAt5HistSch, row.TaggedValues{0: types.Int(0), 1: types.String("Aaron"), 2: types.String("Son"), 3: types.String("123 Fake St"), 5: types.Uint(35)})),
   936  			mustRow(row.New(types.Format_Default, ReaddAgeAt5HistSch, row.TaggedValues{0: types.Int(1), 1: types.String("Brian"), 2: types.String("Hendriks"), 3: types.String("456 Bull Ln"), 5: types.Uint(38)})),
   937  			mustRow(row.New(types.Format_Default, ReaddAgeAt5HistSch, row.TaggedValues{0: types.Int(2), 1: types.String("Tim"), 2: types.String("Sehn"), 3: types.String("789 Not Real Ct"), 5: types.Uint(37)})),
   938  			mustRow(row.New(types.Format_Default, ReaddAgeAt5HistSch, row.TaggedValues{0: types.Int(3), 1: types.String("Zach"), 2: types.String("Musgrave"), 3: types.String("-1 Imaginary Wy"), 5: types.Uint(37)})),
   939  			mustRow(row.New(types.Format_Default, ReaddAgeAt5HistSch, row.TaggedValues{0: types.Int(4), 1: types.String("Matt"), 2: types.String("Jesuele")})),
   940  			mustRow(row.New(types.Format_Default, ReaddAgeAt5HistSch, row.TaggedValues{0: types.Int(5), 1: types.String("Daylon"), 2: types.String("Wilkins")})),
   941  		),
   942  		ExpectedSchema: ReaddAgeAt5HistSch,
   943  	},
   944  	{
   945  		Name:  "select * from timestamp, HEAD exact",
   946  		Query: "select * from test_table as of CONVERT('1970-01-01 08:00:00', DATETIME)",
   947  		ExpectedRows: ToSqlRows(ReaddAgeAt5HistSch,
   948  			mustRow(row.New(types.Format_Default, ReaddAgeAt5HistSch, row.TaggedValues{0: types.Int(0), 1: types.String("Aaron"), 2: types.String("Son"), 3: types.String("123 Fake St"), 5: types.Uint(35)})),
   949  			mustRow(row.New(types.Format_Default, ReaddAgeAt5HistSch, row.TaggedValues{0: types.Int(1), 1: types.String("Brian"), 2: types.String("Hendriks"), 3: types.String("456 Bull Ln"), 5: types.Uint(38)})),
   950  			mustRow(row.New(types.Format_Default, ReaddAgeAt5HistSch, row.TaggedValues{0: types.Int(2), 1: types.String("Tim"), 2: types.String("Sehn"), 3: types.String("789 Not Real Ct"), 5: types.Uint(37)})),
   951  			mustRow(row.New(types.Format_Default, ReaddAgeAt5HistSch, row.TaggedValues{0: types.Int(3), 1: types.String("Zach"), 2: types.String("Musgrave"), 3: types.String("-1 Imaginary Wy"), 5: types.Uint(37)})),
   952  			mustRow(row.New(types.Format_Default, ReaddAgeAt5HistSch, row.TaggedValues{0: types.Int(4), 1: types.String("Matt"), 2: types.String("Jesuele")})),
   953  			mustRow(row.New(types.Format_Default, ReaddAgeAt5HistSch, row.TaggedValues{0: types.Int(5), 1: types.String("Daylon"), 2: types.String("Wilkins")})),
   954  		),
   955  		ExpectedSchema: ReaddAgeAt5HistSch,
   956  	},
   957  	{
   958  		Name:  "select * from timestamp, HEAD~ + 1",
   959  		Query: "select * from test_table as of CONVERT('1970-01-01 07:00:00', DATETIME)",
   960  		ExpectedRows: ToSqlRows(AddAddrAt3HistSch,
   961  			mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(0), 1: types.String("Aaron"), 2: types.String("Son"), 3: types.String("123 Fake St")})),
   962  			mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(1), 1: types.String("Brian"), 2: types.String("Hendriks"), 3: types.String("456 Bull Ln")})),
   963  			mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(2), 1: types.String("Tim"), 2: types.String("Sehn"), 3: types.String("789 Not Real Ct")})),
   964  			mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(3), 1: types.String("Zach"), 2: types.String("Musgrave")})),
   965  			mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(4), 1: types.String("Matt"), 2: types.String("Jesuele")})),
   966  		),
   967  		ExpectedSchema: AddAddrAt3HistSch,
   968  	},
   969  	{
   970  		Name:  "select * from timestamp, HEAD~",
   971  		Query: "select * from test_table as of CONVERT('1970-01-01 06:00:00', DATETIME)",
   972  		ExpectedRows: ToSqlRows(AddAddrAt3HistSch,
   973  			mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(0), 1: types.String("Aaron"), 2: types.String("Son"), 3: types.String("123 Fake St")})),
   974  			mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(1), 1: types.String("Brian"), 2: types.String("Hendriks"), 3: types.String("456 Bull Ln")})),
   975  			mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(2), 1: types.String("Tim"), 2: types.String("Sehn"), 3: types.String("789 Not Real Ct")})),
   976  			mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(3), 1: types.String("Zach"), 2: types.String("Musgrave")})),
   977  			mustRow(row.New(types.Format_Default, AddAddrAt3HistSch, row.TaggedValues{0: types.Int(4), 1: types.String("Matt"), 2: types.String("Jesuele")})),
   978  		),
   979  		ExpectedSchema: AddAddrAt3HistSch,
   980  	},
   981  	{
   982  		Name:        "select * from timestamp, before table creation",
   983  		Query:       "select * from test_table as of CONVERT('1970-01-01 02:00:00', DATETIME)",
   984  		ExpectedErr: "not found",
   985  	},
   986  	{
   987  		Name: "select from dolt_docs as of main",
   988  		AdditionalSetup: CreateTableFn("dolt_docs", doltdb.DocsSchema,
   989  			"INSERT INTO dolt_docs VALUES ('LICENSE.md','A license')"),
   990  		Query:          "select * from dolt_docs as of 'main'",
   991  		ExpectedRows:   []sql.Row{{"LICENSE.md", "A license"}},
   992  		ExpectedSchema: CompressSchema(doltdb.DocsSchema),
   993  	},
   994  }
   995  
   996  // Tests of join functionality, basically any query involving more than one table should go here for now.
   997  var JoinTests = []SelectTest{
   998  	{
   999  		Name:  "Full cross product",
  1000  		Query: `select * from people, episodes`,
  1001  		ExpectedRows: ToSqlRows(CompressSchemas(PeopleTestSchema, EpisodesTestSchema),
  1002  			ConcatRows(PeopleTestSchema, Homer, EpisodesTestSchema, Ep1),
  1003  			ConcatRows(PeopleTestSchema, Homer, EpisodesTestSchema, Ep2),
  1004  			ConcatRows(PeopleTestSchema, Homer, EpisodesTestSchema, Ep3),
  1005  			ConcatRows(PeopleTestSchema, Homer, EpisodesTestSchema, Ep4),
  1006  			ConcatRows(PeopleTestSchema, Marge, EpisodesTestSchema, Ep1),
  1007  			ConcatRows(PeopleTestSchema, Marge, EpisodesTestSchema, Ep2),
  1008  			ConcatRows(PeopleTestSchema, Marge, EpisodesTestSchema, Ep3),
  1009  			ConcatRows(PeopleTestSchema, Marge, EpisodesTestSchema, Ep4),
  1010  			ConcatRows(PeopleTestSchema, Bart, EpisodesTestSchema, Ep1),
  1011  			ConcatRows(PeopleTestSchema, Bart, EpisodesTestSchema, Ep2),
  1012  			ConcatRows(PeopleTestSchema, Bart, EpisodesTestSchema, Ep3),
  1013  			ConcatRows(PeopleTestSchema, Bart, EpisodesTestSchema, Ep4),
  1014  			ConcatRows(PeopleTestSchema, Lisa, EpisodesTestSchema, Ep1),
  1015  			ConcatRows(PeopleTestSchema, Lisa, EpisodesTestSchema, Ep2),
  1016  			ConcatRows(PeopleTestSchema, Lisa, EpisodesTestSchema, Ep3),
  1017  			ConcatRows(PeopleTestSchema, Lisa, EpisodesTestSchema, Ep4),
  1018  			ConcatRows(PeopleTestSchema, Moe, EpisodesTestSchema, Ep1),
  1019  			ConcatRows(PeopleTestSchema, Moe, EpisodesTestSchema, Ep2),
  1020  			ConcatRows(PeopleTestSchema, Moe, EpisodesTestSchema, Ep3),
  1021  			ConcatRows(PeopleTestSchema, Moe, EpisodesTestSchema, Ep4),
  1022  			ConcatRows(PeopleTestSchema, Barney, EpisodesTestSchema, Ep1),
  1023  			ConcatRows(PeopleTestSchema, Barney, EpisodesTestSchema, Ep2),
  1024  			ConcatRows(PeopleTestSchema, Barney, EpisodesTestSchema, Ep3),
  1025  			ConcatRows(PeopleTestSchema, Barney, EpisodesTestSchema, Ep4),
  1026  		),
  1027  		ExpectedSchema: CompressSchemas(PeopleTestSchema, EpisodesTestSchema),
  1028  	},
  1029  	{
  1030  		Name:  "Natural join with where clause",
  1031  		Query: `select * from people p, episodes e where e.id = p.id`,
  1032  		ExpectedRows: ToSqlRows(CompressSchemas(PeopleTestSchema, EpisodesTestSchema),
  1033  			ConcatRows(PeopleTestSchema, Marge, EpisodesTestSchema, Ep1),
  1034  			ConcatRows(PeopleTestSchema, Bart, EpisodesTestSchema, Ep2),
  1035  			ConcatRows(PeopleTestSchema, Lisa, EpisodesTestSchema, Ep3),
  1036  			ConcatRows(PeopleTestSchema, Moe, EpisodesTestSchema, Ep4),
  1037  		),
  1038  		ExpectedSchema: CompressSchemas(PeopleTestSchema, EpisodesTestSchema),
  1039  	},
  1040  	{
  1041  		Name:  "Three table natural join with where clause",
  1042  		Query: `select p.*, e.* from people p, episodes e, appearances a where a.episode_id = e.id and a.character_id = p.id`,
  1043  		ExpectedRows: ToSqlRows(CompressSchemas(PeopleTestSchema, EpisodesTestSchema),
  1044  			ConcatRows(PeopleTestSchema, Homer, EpisodesTestSchema, Ep1),
  1045  			ConcatRows(PeopleTestSchema, Homer, EpisodesTestSchema, Ep2),
  1046  			ConcatRows(PeopleTestSchema, Homer, EpisodesTestSchema, Ep3),
  1047  			ConcatRows(PeopleTestSchema, Marge, EpisodesTestSchema, Ep1),
  1048  			ConcatRows(PeopleTestSchema, Marge, EpisodesTestSchema, Ep3),
  1049  			ConcatRows(PeopleTestSchema, Bart, EpisodesTestSchema, Ep2),
  1050  			ConcatRows(PeopleTestSchema, Lisa, EpisodesTestSchema, Ep2),
  1051  			ConcatRows(PeopleTestSchema, Lisa, EpisodesTestSchema, Ep3),
  1052  			ConcatRows(PeopleTestSchema, Moe, EpisodesTestSchema, Ep2),
  1053  			ConcatRows(PeopleTestSchema, Barney, EpisodesTestSchema, Ep3),
  1054  		),
  1055  		ExpectedSchema: CompressSchemas(PeopleTestSchema, EpisodesTestSchema),
  1056  	},
  1057  	// TODO: error messages are different in SQL engine
  1058  	{
  1059  		Name:        "ambiguous column in select",
  1060  		Query:       `select id from people p, episodes e, appearances a where a.episode_id = e.id and a.character_id = p.id`,
  1061  		ExpectedErr: "Ambiguous column: 'id'",
  1062  	},
  1063  	{
  1064  		Name:        "ambiguous column in where",
  1065  		Query:       `select p.*, e.* from people p, episodes e, appearances a where a.episode_id = id and a.character_id = id`,
  1066  		ExpectedErr: "Ambiguous column: 'id'",
  1067  	},
  1068  	{
  1069  		Name:  "Natural join with where clause, select subset of columns",
  1070  		Query: `select e.id, p.id, e.name, p.first_name, p.last_name from people p, episodes e where e.id = p.id`,
  1071  		ExpectedRows: ToSqlRows(
  1072  			NewResultSetSchema("id", types.IntKind, "id", types.IntKind,
  1073  				"name", types.StringKind, "first_name", types.StringKind, "last_name", types.StringKind),
  1074  			NewResultSetRow(types.Int(1), types.Int(1), types.String("Simpsons Roasting On an Open Fire"), types.String("Marge"), types.String("Simpson")),
  1075  			NewResultSetRow(types.Int(2), types.Int(2), types.String("Bart the Genius"), types.String("Bart"), types.String("Simpson")),
  1076  			NewResultSetRow(types.Int(3), types.Int(3), types.String("Homer's Odyssey"), types.String("Lisa"), types.String("Simpson")),
  1077  			NewResultSetRow(types.Int(4), types.Int(4), types.String("There's No Disgrace Like Home"), types.String("Moe"), types.String("Szyslak")),
  1078  		),
  1079  		ExpectedSchema: NewResultSetSchema("id", types.IntKind, "id", types.IntKind,
  1080  			"name", types.StringKind, "first_name", types.StringKind, "last_name", types.StringKind),
  1081  	},
  1082  	{
  1083  		Name:  "Natural join with where clause and column aliases",
  1084  		Query: "select e.id as eid, p.id as pid, e.name as ename, p.first_name as pfirst_name, p.last_name last_name from people p, episodes e where e.id = p.id",
  1085  		ExpectedRows: ToSqlRows(
  1086  			NewResultSetSchema("eid", types.IntKind, "pid", types.IntKind,
  1087  				"ename", types.StringKind, "pfirst_name", types.StringKind, "last_name", types.StringKind),
  1088  			NewResultSetRow(types.Int(1), types.Int(1), types.String("Simpsons Roasting On an Open Fire"), types.String("Marge"), types.String("Simpson")),
  1089  			NewResultSetRow(types.Int(2), types.Int(2), types.String("Bart the Genius"), types.String("Bart"), types.String("Simpson")),
  1090  			NewResultSetRow(types.Int(3), types.Int(3), types.String("Homer's Odyssey"), types.String("Lisa"), types.String("Simpson")),
  1091  			NewResultSetRow(types.Int(4), types.Int(4), types.String("There's No Disgrace Like Home"), types.String("Moe"), types.String("Szyslak")),
  1092  		),
  1093  		ExpectedSchema: NewResultSetSchema("eid", types.IntKind, "pid", types.IntKind,
  1094  			"ename", types.StringKind, "pfirst_name", types.StringKind, "last_name", types.StringKind),
  1095  	},
  1096  	{
  1097  		Name:  "Natural join with where clause and quoted column alias",
  1098  		Query: "select e.id as eid, p.id as `p.id`, e.name as ename, p.first_name as pfirst_name, p.last_name last_name from people p, episodes e where e.id = p.id",
  1099  		ExpectedRows: ToSqlRows(
  1100  			NewResultSetSchema("eid", types.IntKind, "p.id", types.IntKind,
  1101  				"ename", types.StringKind, "pfirst_name", types.StringKind, "last_name", types.StringKind),
  1102  			NewResultSetRow(types.Int(1), types.Int(1), types.String("Simpsons Roasting On an Open Fire"), types.String("Marge"), types.String("Simpson")),
  1103  			NewResultSetRow(types.Int(2), types.Int(2), types.String("Bart the Genius"), types.String("Bart"), types.String("Simpson")),
  1104  			NewResultSetRow(types.Int(3), types.Int(3), types.String("Homer's Odyssey"), types.String("Lisa"), types.String("Simpson")),
  1105  			NewResultSetRow(types.Int(4), types.Int(4), types.String("There's No Disgrace Like Home"), types.String("Moe"), types.String("Szyslak")),
  1106  		),
  1107  		ExpectedSchema: NewResultSetSchema("eid", types.IntKind, "p.id", types.IntKind,
  1108  			"ename", types.StringKind, "pfirst_name", types.StringKind, "last_name", types.StringKind),
  1109  	},
  1110  	{
  1111  		Name:  "Natural join with join clause",
  1112  		Query: `select * from people p join episodes e on e.id = p.id`,
  1113  		ExpectedRows: ToSqlRows(CompressSchemas(PeopleTestSchema, EpisodesTestSchema),
  1114  			ConcatRows(PeopleTestSchema, Marge, EpisodesTestSchema, Ep1),
  1115  			ConcatRows(PeopleTestSchema, Bart, EpisodesTestSchema, Ep2),
  1116  			ConcatRows(PeopleTestSchema, Lisa, EpisodesTestSchema, Ep3),
  1117  			ConcatRows(PeopleTestSchema, Moe, EpisodesTestSchema, Ep4),
  1118  		),
  1119  		ExpectedSchema: CompressSchemas(PeopleTestSchema, EpisodesTestSchema),
  1120  	},
  1121  	{
  1122  		Name:  "Three table natural join with join clause",
  1123  		Query: `select p.*, e.* from people p join appearances a on a.character_id = p.id join episodes e on a.episode_id = e.id`,
  1124  		ExpectedRows: ToSqlRows(CompressSchemas(PeopleTestSchema, EpisodesTestSchema),
  1125  			ConcatRows(PeopleTestSchema, Homer, EpisodesTestSchema, Ep1),
  1126  			ConcatRows(PeopleTestSchema, Homer, EpisodesTestSchema, Ep2),
  1127  			ConcatRows(PeopleTestSchema, Homer, EpisodesTestSchema, Ep3),
  1128  			ConcatRows(PeopleTestSchema, Marge, EpisodesTestSchema, Ep1),
  1129  			ConcatRows(PeopleTestSchema, Marge, EpisodesTestSchema, Ep3),
  1130  			ConcatRows(PeopleTestSchema, Bart, EpisodesTestSchema, Ep2),
  1131  			ConcatRows(PeopleTestSchema, Lisa, EpisodesTestSchema, Ep2),
  1132  			ConcatRows(PeopleTestSchema, Lisa, EpisodesTestSchema, Ep3),
  1133  			ConcatRows(PeopleTestSchema, Moe, EpisodesTestSchema, Ep2),
  1134  			ConcatRows(PeopleTestSchema, Barney, EpisodesTestSchema, Ep3),
  1135  		),
  1136  		ExpectedSchema: CompressSchemas(PeopleTestSchema, EpisodesTestSchema),
  1137  	},
  1138  	{
  1139  		Name:  "Natural join with join clause, select subset of columns",
  1140  		Query: `select e.id, p.id, e.name, p.first_name, p.last_name from people p join episodes e on e.id = p.id`,
  1141  		ExpectedRows: ToSqlRows(
  1142  			NewResultSetSchema("id", types.IntKind, "id", types.IntKind,
  1143  				"name", types.StringKind, "first_name", types.StringKind, "last_name", types.StringKind),
  1144  			NewResultSetRow(types.Int(1), types.Int(1), types.String("Simpsons Roasting On an Open Fire"), types.String("Marge"), types.String("Simpson")),
  1145  			NewResultSetRow(types.Int(2), types.Int(2), types.String("Bart the Genius"), types.String("Bart"), types.String("Simpson")),
  1146  			NewResultSetRow(types.Int(3), types.Int(3), types.String("Homer's Odyssey"), types.String("Lisa"), types.String("Simpson")),
  1147  			NewResultSetRow(types.Int(4), types.Int(4), types.String("There's No Disgrace Like Home"), types.String("Moe"), types.String("Szyslak")),
  1148  		),
  1149  		ExpectedSchema: NewResultSetSchema("id", types.IntKind, "id", types.IntKind,
  1150  			"name", types.StringKind, "first_name", types.StringKind, "last_name", types.StringKind),
  1151  	},
  1152  	{
  1153  		Name:  "Natural join with join clause, select subset of columns, join columns not selected",
  1154  		Query: `select e.name, p.first_name, p.last_name from people p join episodes e on e.id = p.id`,
  1155  		ExpectedRows: ToSqlRows(NewResultSetSchema("name", types.StringKind, "first_name", types.StringKind, "last_name", types.StringKind),
  1156  			NewResultSetRow(types.String("Simpsons Roasting On an Open Fire"), types.String("Marge"), types.String("Simpson")),
  1157  			NewResultSetRow(types.String("Bart the Genius"), types.String("Bart"), types.String("Simpson")),
  1158  			NewResultSetRow(types.String("Homer's Odyssey"), types.String("Lisa"), types.String("Simpson")),
  1159  			NewResultSetRow(types.String("There's No Disgrace Like Home"), types.String("Moe"), types.String("Szyslak")),
  1160  		),
  1161  		ExpectedSchema: NewResultSetSchema("name", types.StringKind, "first_name", types.StringKind, "last_name", types.StringKind),
  1162  	},
  1163  	{
  1164  		Name: "Natural join with join clause, select subset of columns, order by clause",
  1165  		Query: `select e.id, p.id, e.name, p.first_name, p.last_name from people p 
  1166  							join episodes e on e.id = p.id
  1167  							order by e.name`,
  1168  		ExpectedRows: ToSqlRows(
  1169  			NewResultSetSchema("id", types.IntKind, "id", types.IntKind,
  1170  				"name", types.StringKind, "first_name", types.StringKind, "last_name", types.StringKind),
  1171  			NewResultSetRow(types.Int(2), types.Int(2), types.String("Bart the Genius"), types.String("Bart"), types.String("Simpson")),
  1172  			NewResultSetRow(types.Int(3), types.Int(3), types.String("Homer's Odyssey"), types.String("Lisa"), types.String("Simpson")),
  1173  			NewResultSetRow(types.Int(1), types.Int(1), types.String("Simpsons Roasting On an Open Fire"), types.String("Marge"), types.String("Simpson")),
  1174  			NewResultSetRow(types.Int(4), types.Int(4), types.String("There's No Disgrace Like Home"), types.String("Moe"), types.String("Szyslak")),
  1175  		),
  1176  		ExpectedSchema: NewResultSetSchema("id", types.IntKind, "id", types.IntKind,
  1177  			"name", types.StringKind, "first_name", types.StringKind, "last_name", types.StringKind),
  1178  	},
  1179  	{
  1180  		Name: "Natural join with join clause, select subset of columns, order by clause on non-selected column",
  1181  		Query: `select e.id, p.id, e.name, p.first_name, p.last_name from people p 
  1182  							join episodes e on e.id = p.id
  1183  							order by age`,
  1184  		ExpectedRows: ToSqlRows(
  1185  			NewResultSetSchema("id", types.IntKind, "id", types.IntKind,
  1186  				"name", types.StringKind, "first_name", types.StringKind, "last_name", types.StringKind),
  1187  			NewResultSetRow(types.Int(3), types.Int(3), types.String("Homer's Odyssey"), types.String("Lisa"), types.String("Simpson")),
  1188  			NewResultSetRow(types.Int(2), types.Int(2), types.String("Bart the Genius"), types.String("Bart"), types.String("Simpson")),
  1189  			NewResultSetRow(types.Int(1), types.Int(1), types.String("Simpsons Roasting On an Open Fire"), types.String("Marge"), types.String("Simpson")),
  1190  			NewResultSetRow(types.Int(4), types.Int(4), types.String("There's No Disgrace Like Home"), types.String("Moe"), types.String("Szyslak")),
  1191  		),
  1192  		ExpectedSchema: NewResultSetSchema("id", types.IntKind, "id", types.IntKind,
  1193  			"name", types.StringKind, "first_name", types.StringKind, "last_name", types.StringKind),
  1194  	},
  1195  	{
  1196  		Name:  "Natural join with join clause and column aliases",
  1197  		Query: "select e.id as eid, p.id as pid, e.name as ename, p.first_name as pfirst_name, p.last_name last_name from people p join episodes e on e.id = p.id",
  1198  		ExpectedRows: ToSqlRows(
  1199  			NewResultSetSchema("eid", types.IntKind, "pid", types.IntKind,
  1200  				"ename", types.StringKind, "pfirst_name", types.StringKind, "last_name", types.StringKind),
  1201  			NewResultSetRow(types.Int(1), types.Int(1), types.String("Simpsons Roasting On an Open Fire"), types.String("Marge"), types.String("Simpson")),
  1202  			NewResultSetRow(types.Int(2), types.Int(2), types.String("Bart the Genius"), types.String("Bart"), types.String("Simpson")),
  1203  			NewResultSetRow(types.Int(3), types.Int(3), types.String("Homer's Odyssey"), types.String("Lisa"), types.String("Simpson")),
  1204  			NewResultSetRow(types.Int(4), types.Int(4), types.String("There's No Disgrace Like Home"), types.String("Moe"), types.String("Szyslak")),
  1205  		),
  1206  		ExpectedSchema: NewResultSetSchema("eid", types.IntKind, "pid", types.IntKind,
  1207  			"ename", types.StringKind, "pfirst_name", types.StringKind, "last_name", types.StringKind),
  1208  	},
  1209  	{
  1210  		Name:  "Natural join with join clause and column aliases, order by",
  1211  		Query: "select e.id as eid, p.id as pid, e.name as ename, p.first_name as pfirst_name, p.last_name last_name from people p join episodes e on e.id = p.id order by ename",
  1212  		ExpectedRows: ToSqlRows(
  1213  			NewResultSetSchema("eid", types.IntKind, "pid", types.IntKind,
  1214  				"ename", types.StringKind, "pfirst_name", types.StringKind, "last_name", types.StringKind),
  1215  			NewResultSetRow(types.Int(2), types.Int(2), types.String("Bart the Genius"), types.String("Bart"), types.String("Simpson")),
  1216  			NewResultSetRow(types.Int(3), types.Int(3), types.String("Homer's Odyssey"), types.String("Lisa"), types.String("Simpson")),
  1217  			NewResultSetRow(types.Int(1), types.Int(1), types.String("Simpsons Roasting On an Open Fire"), types.String("Marge"), types.String("Simpson")),
  1218  			NewResultSetRow(types.Int(4), types.Int(4), types.String("There's No Disgrace Like Home"), types.String("Moe"), types.String("Szyslak")),
  1219  		),
  1220  		ExpectedSchema: NewResultSetSchema("eid", types.IntKind, "pid", types.IntKind,
  1221  			"ename", types.StringKind, "pfirst_name", types.StringKind, "last_name", types.StringKind),
  1222  	},
  1223  	{
  1224  		Name:  "Natural join with join clause and quoted column alias",
  1225  		Query: "select e.id as eid, p.id as `p.id`, e.name as ename, p.first_name as pfirst_name, p.last_name last_name from people p join episodes e on e.id = p.id",
  1226  		ExpectedRows: ToSqlRows(
  1227  			NewResultSetSchema("eid", types.IntKind, "p.id", types.IntKind,
  1228  				"ename", types.StringKind, "pfirst_name", types.StringKind, "last_name", types.StringKind),
  1229  			NewResultSetRow(types.Int(1), types.Int(1), types.String("Simpsons Roasting On an Open Fire"), types.String("Marge"), types.String("Simpson")),
  1230  			NewResultSetRow(types.Int(2), types.Int(2), types.String("Bart the Genius"), types.String("Bart"), types.String("Simpson")),
  1231  			NewResultSetRow(types.Int(3), types.Int(3), types.String("Homer's Odyssey"), types.String("Lisa"), types.String("Simpson")),
  1232  			NewResultSetRow(types.Int(4), types.Int(4), types.String("There's No Disgrace Like Home"), types.String("Moe"), types.String("Szyslak")),
  1233  		),
  1234  		ExpectedSchema: NewResultSetSchema("eid", types.IntKind, "p.id", types.IntKind,
  1235  			"ename", types.StringKind, "pfirst_name", types.StringKind, "last_name", types.StringKind),
  1236  	},
  1237  	{
  1238  		Name: "Join from table with two key columns to table with one key column",
  1239  		Query: `select a.episode_id as eid, p.id as pid, p.first_name
  1240  						from appearances a join people p on a.character_id = p.id order by eid, pid`,
  1241  		ExpectedRows: ToSqlRows(
  1242  			NewResultSetSchema("eid", types.IntKind, "pid", types.IntKind,
  1243  				"first_name", types.StringKind),
  1244  			NewResultSetRow(types.Int(1), types.Int(0), types.String("Homer")),
  1245  			NewResultSetRow(types.Int(1), types.Int(1), types.String("Marge")),
  1246  			NewResultSetRow(types.Int(2), types.Int(0), types.String("Homer")),
  1247  			NewResultSetRow(types.Int(2), types.Int(2), types.String("Bart")),
  1248  			NewResultSetRow(types.Int(2), types.Int(3), types.String("Lisa")),
  1249  			NewResultSetRow(types.Int(2), types.Int(4), types.String("Moe")),
  1250  			NewResultSetRow(types.Int(3), types.Int(0), types.String("Homer")),
  1251  			NewResultSetRow(types.Int(3), types.Int(1), types.String("Marge")),
  1252  			NewResultSetRow(types.Int(3), types.Int(3), types.String("Lisa")),
  1253  			NewResultSetRow(types.Int(3), types.Int(5), types.String("Barney")),
  1254  		),
  1255  		ExpectedSchema: NewResultSetSchema("eid", types.IntKind, "pid", types.IntKind,
  1256  			"first_name", types.StringKind),
  1257  	},
  1258  }
  1259  
  1260  func TestSelect(t *testing.T) {
  1261  	for _, test := range BasicSelectTests() {
  1262  		t.Run(test.Name, func(t *testing.T) {
  1263  			testSelectQuery(t, test)
  1264  		})
  1265  	}
  1266  }
  1267  
  1268  func TestAsOfQueries(t *testing.T) {
  1269  	if types.Format_Default != types.Format_LD_1 {
  1270  		t.Skip("") // todo: convert to enginetests
  1271  	}
  1272  	for _, test := range AsOfTests {
  1273  		t.Run(test.Name, func(t *testing.T) {
  1274  			// AS OF queries use the same history as the diff tests, so exercise the same test setup
  1275  			testSelectDiffQuery(t, test)
  1276  		})
  1277  	}
  1278  }
  1279  
  1280  func TestJoins(t *testing.T) {
  1281  	for _, tt := range JoinTests {
  1282  		if tt.Name == "Join from table with two key columns to table with one key column" {
  1283  			t.Run(tt.Name, func(t *testing.T) {
  1284  				testSelectQuery(t, tt)
  1285  			})
  1286  		}
  1287  	}
  1288  }
  1289  
  1290  var systemTableSelectTests = []SelectTest{
  1291  	{
  1292  		Name: "select from dolt_docs",
  1293  		AdditionalSetup: CreateTableFn("dolt_docs", doltdb.DocsSchema,
  1294  			"INSERT INTO dolt_docs VALUES ('LICENSE.md','A license')"),
  1295  		Query:          "select * from dolt_docs",
  1296  		ExpectedRows:   []sql.Row{{"LICENSE.md", "A license"}},
  1297  		ExpectedSchema: CompressSchema(doltdb.DocsSchema),
  1298  	},
  1299  	{
  1300  		Name: "select from dolt_query_catalog",
  1301  		AdditionalSetup: CreateTableFn(doltdb.DoltQueryCatalogTableName, dtables.DoltQueryCatalogSchema,
  1302  			"INSERT INTO dolt_query_catalog VALUES ('existingEntry', 2, 'example', 'select 2+2 from dual', 'description')"),
  1303  		Query: "select * from dolt_query_catalog",
  1304  		ExpectedRows: ToSqlRows(CompressSchema(dtables.DoltQueryCatalogSchema),
  1305  			NewRow(types.String("existingEntry"), types.Uint(2), types.String("example"), types.String("select 2+2 from dual"), types.String("description")),
  1306  		),
  1307  		ExpectedSchema: CompressSchema(dtables.DoltQueryCatalogSchema),
  1308  	},
  1309  	{
  1310  		Name: "select from dolt_schemas",
  1311  		AdditionalSetup: CreateTableFn(doltdb.SchemasTableName, schemaTableSchema,
  1312  			`INSERT INTO dolt_schemas VALUES ('view', 'name', 'create view name as select 2+2 from dual', NULL, NULL)`),
  1313  		Query:          "select * from dolt_schemas",
  1314  		ExpectedRows:   []sql.Row{{"view", "name", "create view name as select 2+2 from dual", nil, nil}},
  1315  		ExpectedSchema: CompressSchema(schemaTableSchema),
  1316  	},
  1317  }
  1318  
  1319  func TestSelectSystemTables(t *testing.T) {
  1320  	for _, test := range systemTableSelectTests {
  1321  		t.Run(test.Name, func(t *testing.T) {
  1322  			testSelectQuery(t, test)
  1323  		})
  1324  	}
  1325  }
  1326  
  1327  type testCommitClock struct {
  1328  	unixNano int64
  1329  }
  1330  
  1331  func (tcc *testCommitClock) Now() time.Time {
  1332  	now := time.Unix(0, tcc.unixNano)
  1333  	tcc.unixNano += int64(time.Hour)
  1334  	return now
  1335  }
  1336  
  1337  func installTestCommitClock() func() {
  1338  	oldNowFunc := datas.CommitterDate
  1339  	oldCommitLoc := datas.CommitLoc
  1340  	tcc := &testCommitClock{}
  1341  	datas.CommitterDate = tcc.Now
  1342  	datas.CommitLoc = time.UTC
  1343  	return func() {
  1344  		datas.CommitterDate = oldNowFunc
  1345  		datas.CommitLoc = oldCommitLoc
  1346  	}
  1347  }
  1348  
  1349  // Tests the given query on a freshly created dataset, asserting that the result has the given schema and rows. If
  1350  // expectedErr is set, asserts instead that the execution returns an error that matches.
  1351  func testSelectQuery(t *testing.T, test SelectTest) {
  1352  	validateTest(t, test)
  1353  
  1354  	cleanup := installTestCommitClock()
  1355  	defer cleanup()
  1356  
  1357  	dEnv, err := CreateTestDatabase()
  1358  	require.NoError(t, err)
  1359  	defer dEnv.DoltDB.Close()
  1360  
  1361  	if test.AdditionalSetup != nil {
  1362  		test.AdditionalSetup(t, dEnv)
  1363  	}
  1364  
  1365  	root, _ := dEnv.WorkingRoot(context.Background())
  1366  	actualRows, sch, err := executeSelect(t, context.Background(), dEnv, root, test.Query)
  1367  	if len(test.ExpectedErr) > 0 {
  1368  		require.Error(t, err)
  1369  		// Too much work to synchronize error messages between the two implementations, so for now we'll just assert that an error occurred.
  1370  		// require.Contains(t, err.Error(), test.ExpectedErr)
  1371  		return
  1372  	} else {
  1373  		require.NoError(t, err)
  1374  	}
  1375  
  1376  	// JSON columns must be compared using like so
  1377  	assert.Equal(t, len(test.ExpectedRows), len(actualRows))
  1378  	for i := 0; i < len(test.ExpectedRows); i++ {
  1379  		assert.Equal(t, len(test.ExpectedRows[i]), len(actualRows[i]))
  1380  		for j := 0; j < len(test.ExpectedRows[i]); j++ {
  1381  			if _, ok := actualRows[i][j].(json.NomsJSON); ok {
  1382  				cmp, err := gmstypes.CompareJSON(actualRows[i][j].(json.NomsJSON), test.ExpectedRows[i][j].(json.NomsJSON))
  1383  				assert.NoError(t, err)
  1384  				assert.Equal(t, 0, cmp)
  1385  			} else {
  1386  				assert.Equal(t, test.ExpectedRows[i][j], actualRows[i][j])
  1387  			}
  1388  
  1389  		}
  1390  	}
  1391  
  1392  	var sqlSchema sql.Schema
  1393  	if test.ExpectedSqlSchema != nil {
  1394  		sqlSchema = test.ExpectedSqlSchema
  1395  	} else {
  1396  		sqlSchema = mustSqlSchema(test.ExpectedSchema)
  1397  	}
  1398  
  1399  	assertSchemasEqual(t, sqlSchema, sch)
  1400  }
  1401  
  1402  const TableWithHistoryName = "test_table"
  1403  
  1404  var InitialHistSch = dtestutils.MustSchema(idColTag0TypeUUID, firstColTag1TypeStr, lastColTag2TypeStr)
  1405  var AddAddrAt3HistSch = dtestutils.MustSchema(idColTag0TypeUUID, firstColTag1TypeStr, lastColTag2TypeStr, addrColTag3TypeStr)
  1406  var AddAgeAt4HistSch = dtestutils.MustSchema(idColTag0TypeUUID, firstColTag1TypeStr, lastColTag2TypeStr, ageColTag4TypeInt)
  1407  var ReaddAgeAt5HistSch = dtestutils.MustSchema(idColTag0TypeUUID, firstColTag1TypeStr, lastColTag2TypeStr, addrColTag3TypeStr, ageColTag5TypeUint)
  1408  
  1409  // TableUpdate defines a list of modifications that should be made to a table
  1410  type TableUpdate struct {
  1411  	// NewSch is an updated schema for this table. It overwrites the existing value.  If not provided the existing value
  1412  	// will not change
  1413  	NewSch schema.Schema
  1414  
  1415  	// NewRowData if provided overwrites the entirety of the row data in the table.
  1416  	NewRowData *types.Map
  1417  
  1418  	// RowUpdates are new values for rows that should be set in the map.  They can be updates or inserts.
  1419  	RowUpdates []row.Row
  1420  }
  1421  
  1422  // HistoryNode represents a commit to be made
  1423  type HistoryNode struct {
  1424  	// Branch the branch that the commit should be on
  1425  	Branch string
  1426  
  1427  	// CommitMessag is the commit message that should be applied
  1428  	CommitMsg string
  1429  
  1430  	// Updates are the changes that should be made to the table's states before committing
  1431  	Updates map[string]TableUpdate
  1432  
  1433  	// Children are the child commits of this commit
  1434  	Children []HistoryNode
  1435  }
  1436  
  1437  // mustRowData converts a slice of row.TaggedValues into a noms types.Map containing that data.
  1438  func mustRowData(t *testing.T, ctx context.Context, vrw types.ValueReadWriter, sch schema.Schema, colVals []row.TaggedValues) *types.Map {
  1439  	m, err := types.NewMap(ctx, vrw)
  1440  	require.NoError(t, err)
  1441  
  1442  	me := m.Edit()
  1443  	for _, taggedVals := range colVals {
  1444  		r, err := row.New(types.Format_Default, sch, taggedVals)
  1445  		require.NoError(t, err)
  1446  
  1447  		me = me.Set(r.NomsMapKey(sch), r.NomsMapValue(sch))
  1448  	}
  1449  
  1450  	m, err = me.Map(ctx)
  1451  	require.NoError(t, err)
  1452  
  1453  	return &m
  1454  }
  1455  
  1456  func CreateHistory(ctx context.Context, dEnv *env.DoltEnv, t *testing.T) []HistoryNode {
  1457  	vrw := dEnv.DoltDB.ValueReadWriter()
  1458  
  1459  	return []HistoryNode{
  1460  		{
  1461  			Branch:    "seed",
  1462  			CommitMsg: "Seeding with initial user data",
  1463  			Updates: map[string]TableUpdate{
  1464  				TableWithHistoryName: {
  1465  					NewSch: InitialHistSch,
  1466  					NewRowData: mustRowData(t, ctx, vrw, InitialHistSch, []row.TaggedValues{
  1467  						{0: types.Int(0), 1: types.String("Aaron"), 2: types.String("Son")},
  1468  						{0: types.Int(1), 1: types.String("Brian"), 2: types.String("Hendriks")},
  1469  						{0: types.Int(2), 1: types.String("Tim"), 2: types.String("Sehn")},
  1470  					}),
  1471  				},
  1472  			},
  1473  			Children: []HistoryNode{
  1474  				{
  1475  					Branch:    "add-age",
  1476  					CommitMsg: "Adding int age to users with tag 3",
  1477  					Updates: map[string]TableUpdate{
  1478  						TableWithHistoryName: {
  1479  							NewSch: AddAgeAt4HistSch,
  1480  							NewRowData: mustRowData(t, ctx, vrw, AddAgeAt4HistSch, []row.TaggedValues{
  1481  								{0: types.Int(0), 1: types.String("Aaron"), 2: types.String("Son"), 4: types.Int(35)},
  1482  								{0: types.Int(1), 1: types.String("Brian"), 2: types.String("Hendriks"), 4: types.Int(38)},
  1483  								{0: types.Int(2), 1: types.String("Tim"), 2: types.String("Sehn"), 4: types.Int(37)},
  1484  								{0: types.Int(3), 1: types.String("Zach"), 2: types.String("Musgrave"), 4: types.Int(37)},
  1485  							}),
  1486  						},
  1487  					},
  1488  					Children: nil,
  1489  				},
  1490  				{
  1491  					Branch:    env.DefaultInitBranch,
  1492  					CommitMsg: "Adding string address to users with tag 3",
  1493  					Updates: map[string]TableUpdate{
  1494  						TableWithHistoryName: {
  1495  							NewSch: AddAddrAt3HistSch,
  1496  							NewRowData: mustRowData(t, ctx, vrw, AddAddrAt3HistSch, []row.TaggedValues{
  1497  								{0: types.Int(0), 1: types.String("Aaron"), 2: types.String("Son"), 3: types.String("123 Fake St")},
  1498  								{0: types.Int(1), 1: types.String("Brian"), 2: types.String("Hendriks"), 3: types.String("456 Bull Ln")},
  1499  								{0: types.Int(2), 1: types.String("Tim"), 2: types.String("Sehn"), 3: types.String("789 Not Real Ct")},
  1500  								{0: types.Int(3), 1: types.String("Zach"), 2: types.String("Musgrave")},
  1501  								{0: types.Int(4), 1: types.String("Matt"), 2: types.String("Jesuele")},
  1502  							}),
  1503  						},
  1504  					},
  1505  					Children: []HistoryNode{
  1506  						{
  1507  							Branch:    env.DefaultInitBranch,
  1508  							CommitMsg: "Re-add age as a uint with tag 4",
  1509  							Updates: map[string]TableUpdate{
  1510  								TableWithHistoryName: {
  1511  									NewSch: ReaddAgeAt5HistSch,
  1512  									NewRowData: mustRowData(t, ctx, vrw, ReaddAgeAt5HistSch, []row.TaggedValues{
  1513  										{0: types.Int(0), 1: types.String("Aaron"), 2: types.String("Son"), 3: types.String("123 Fake St"), 5: types.Uint(35)},
  1514  										{0: types.Int(1), 1: types.String("Brian"), 2: types.String("Hendriks"), 3: types.String("456 Bull Ln"), 5: types.Uint(38)},
  1515  										{0: types.Int(2), 1: types.String("Tim"), 2: types.String("Sehn"), 3: types.String("789 Not Real Ct"), 5: types.Uint(37)},
  1516  										{0: types.Int(3), 1: types.String("Zach"), 2: types.String("Musgrave"), 3: types.String("-1 Imaginary Wy"), 5: types.Uint(37)},
  1517  										{0: types.Int(4), 1: types.String("Matt"), 2: types.String("Jesuele")},
  1518  										{0: types.Int(5), 1: types.String("Daylon"), 2: types.String("Wilkins")},
  1519  									}),
  1520  								},
  1521  							},
  1522  							Children: nil,
  1523  						},
  1524  					},
  1525  				},
  1526  			},
  1527  		},
  1528  	}
  1529  }
  1530  
  1531  var idColTag0TypeUUID = schema.NewColumn("id", 0, types.IntKind, true)
  1532  var firstColTag1TypeStr = schema.NewColumn("first_name", 1, types.StringKind, false)
  1533  var lastColTag2TypeStr = schema.NewColumn("last_name", 2, types.StringKind, false)
  1534  var addrColTag3TypeStr = schema.NewColumn("addr", 3, types.StringKind, false)
  1535  var ageColTag4TypeInt = schema.NewColumn("age", 4, types.IntKind, false)
  1536  var ageColTag5TypeUint = schema.NewColumn("age", 5, types.UintKind, false)
  1537  
  1538  var DiffSchema = dtestutils.MustSchema(
  1539  	schema.NewColumn("to_id", 0, types.IntKind, false),
  1540  	schema.NewColumn("to_first_name", 1, types.StringKind, false),
  1541  	schema.NewColumn("to_last_name", 2, types.StringKind, false),
  1542  	schema.NewColumn("to_addr", 3, types.StringKind, false),
  1543  	schema.NewColumn("from_id", 7, types.IntKind, false),
  1544  	schema.NewColumn("from_first_name", 8, types.StringKind, false),
  1545  	schema.NewColumn("from_last_name", 9, types.StringKind, false),
  1546  	schema.NewColumn("from_addr", 10, types.StringKind, false),
  1547  	schema.NewColumn("diff_type", 14, types.StringKind, false),
  1548  )
  1549  
  1550  func testSelectDiffQuery(t *testing.T, test SelectTest) {
  1551  	validateTest(t, test)
  1552  	ctx := context.Background()
  1553  	cleanup := installTestCommitClock()
  1554  	defer cleanup()
  1555  	dEnv := dtestutils.CreateTestEnv()
  1556  	initializeWithHistory(t, ctx, dEnv, CreateHistory(ctx, dEnv, t)...)
  1557  	if test.AdditionalSetup != nil {
  1558  		test.AdditionalSetup(t, dEnv)
  1559  	}
  1560  
  1561  	cs, err := doltdb.NewCommitSpec("main")
  1562  	require.NoError(t, err)
  1563  
  1564  	optCmt, err := dEnv.DoltDB.Resolve(ctx, cs, nil)
  1565  	require.NoError(t, err)
  1566  
  1567  	cm, ok := optCmt.ToCommit()
  1568  	require.True(t, ok)
  1569  
  1570  	root, err := cm.GetRootValue(ctx)
  1571  	require.NoError(t, err)
  1572  
  1573  	err = dEnv.UpdateStagedRoot(ctx, root)
  1574  	require.NoError(t, err)
  1575  
  1576  	err = dEnv.UpdateWorkingRoot(ctx, root)
  1577  	require.NoError(t, err)
  1578  
  1579  	root, err = dEnv.WorkingRoot(context.Background())
  1580  	require.NoError(t, err)
  1581  
  1582  	root = updateTables(t, ctx, root, createWorkingRootUpdate())
  1583  
  1584  	err = dEnv.UpdateWorkingRoot(ctx, root)
  1585  	require.NoError(t, err)
  1586  
  1587  	actualRows, sch, err := executeSelect(t, ctx, dEnv, root, test.Query)
  1588  	if len(test.ExpectedErr) > 0 {
  1589  		require.Error(t, err)
  1590  		return
  1591  	} else {
  1592  		require.NoError(t, err)
  1593  	}
  1594  
  1595  	assert.Equal(t, test.ExpectedRows, actualRows)
  1596  
  1597  	var sqlSchema sql.Schema
  1598  	if test.ExpectedSqlSchema != nil {
  1599  		sqlSchema = test.ExpectedSqlSchema
  1600  	} else {
  1601  		sqlSchema = mustSqlSchema(test.ExpectedSchema)
  1602  	}
  1603  
  1604  	assertSchemasEqual(t, sqlSchema, sch)
  1605  }
  1606  
  1607  // TODO: this shouldn't be here
  1608  func createWorkingRootUpdate() map[string]TableUpdate {
  1609  	return map[string]TableUpdate{
  1610  		TableWithHistoryName: {
  1611  			RowUpdates: []row.Row{
  1612  				mustRow(row.New(types.Format_Default, ReaddAgeAt5HistSch, row.TaggedValues{
  1613  					0: types.Int(6), 1: types.String("Katie"), 2: types.String("McCulloch"),
  1614  				})),
  1615  			},
  1616  		},
  1617  	}
  1618  }
  1619  
  1620  func updateTables(t *testing.T, ctx context.Context, root doltdb.RootValue, tblUpdates map[string]TableUpdate) doltdb.RootValue {
  1621  	for tblName, updates := range tblUpdates {
  1622  		tbl, ok, err := root.GetTable(ctx, doltdb.TableName{Name: tblName})
  1623  		require.NoError(t, err)
  1624  
  1625  		var sch schema.Schema
  1626  		if updates.NewSch != nil {
  1627  			sch = updates.NewSch
  1628  		} else {
  1629  			sch, err = tbl.GetSchema(ctx)
  1630  			require.NoError(t, err)
  1631  		}
  1632  
  1633  		var rowData types.Map
  1634  		if updates.NewRowData == nil {
  1635  			if ok {
  1636  				rowData, err = tbl.GetNomsRowData(ctx)
  1637  				require.NoError(t, err)
  1638  			} else {
  1639  				rowData, err = types.NewMap(ctx, root.VRW())
  1640  				require.NoError(t, err)
  1641  			}
  1642  		} else {
  1643  			rowData = *updates.NewRowData
  1644  		}
  1645  
  1646  		if updates.RowUpdates != nil {
  1647  			me := rowData.Edit()
  1648  
  1649  			for _, r := range updates.RowUpdates {
  1650  				me = me.Set(r.NomsMapKey(sch), r.NomsMapValue(sch))
  1651  			}
  1652  
  1653  			rowData, err = me.Map(ctx)
  1654  			require.NoError(t, err)
  1655  		}
  1656  
  1657  		var indexData durable.IndexSet
  1658  		require.NoError(t, err)
  1659  		if tbl != nil {
  1660  			indexData, err = tbl.GetIndexSet(ctx)
  1661  			require.NoError(t, err)
  1662  		}
  1663  
  1664  		tbl, err = doltdb.NewNomsTable(ctx, root.VRW(), root.NodeStore(), sch, rowData, indexData, nil)
  1665  		require.NoError(t, err)
  1666  
  1667  		root, err = root.PutTable(ctx, doltdb.TableName{Name: tblName}, tbl)
  1668  		require.NoError(t, err)
  1669  	}
  1670  
  1671  	return root
  1672  }
  1673  
  1674  // initializeWithHistory will go through the provided historyNodes and create the intended commit graph
  1675  func initializeWithHistory(t *testing.T, ctx context.Context, dEnv *env.DoltEnv, historyNodes ...HistoryNode) {
  1676  	for _, node := range historyNodes {
  1677  		cs, err := doltdb.NewCommitSpec(env.DefaultInitBranch)
  1678  		require.NoError(t, err)
  1679  
  1680  		optCmt, err := dEnv.DoltDB.Resolve(ctx, cs, nil)
  1681  		require.NoError(t, err)
  1682  
  1683  		cm, ok := optCmt.ToCommit()
  1684  		require.True(t, ok)
  1685  
  1686  		processNode(t, ctx, dEnv, node, cm)
  1687  	}
  1688  }
  1689  
  1690  func processNode(t *testing.T, ctx context.Context, dEnv *env.DoltEnv, node HistoryNode, parent *doltdb.Commit) {
  1691  	branchRef := ref.NewBranchRef(node.Branch)
  1692  	ok, err := dEnv.DoltDB.HasRef(ctx, branchRef)
  1693  	require.NoError(t, err)
  1694  
  1695  	if !ok {
  1696  		err = dEnv.DoltDB.NewBranchAtCommit(ctx, branchRef, parent, nil)
  1697  		require.NoError(t, err)
  1698  	}
  1699  
  1700  	cs, err := doltdb.NewCommitSpec(branchRef.String())
  1701  	require.NoError(t, err)
  1702  
  1703  	optCmt, err := dEnv.DoltDB.Resolve(ctx, cs, nil)
  1704  	require.NoError(t, err)
  1705  
  1706  	cm, ok := optCmt.ToCommit()
  1707  	require.True(t, ok)
  1708  
  1709  	root, err := cm.GetRootValue(ctx)
  1710  	require.NoError(t, err)
  1711  
  1712  	root = updateTables(t, ctx, root, node.Updates)
  1713  	r, h, err := dEnv.DoltDB.WriteRootValue(ctx, root)
  1714  	require.NoError(t, err)
  1715  	root = r
  1716  
  1717  	meta, err := datas.NewCommitMeta("Ash Ketchum", "ash@poke.mon", node.CommitMsg)
  1718  	require.NoError(t, err)
  1719  
  1720  	cm, err = dEnv.DoltDB.Commit(ctx, h, branchRef, meta)
  1721  	require.NoError(t, err)
  1722  
  1723  	for _, child := range node.Children {
  1724  		processNode(t, ctx, dEnv, child, cm)
  1725  	}
  1726  }
  1727  
  1728  func validateTest(t *testing.T, test SelectTest) {
  1729  	if (test.ExpectedRows == nil) != (test.ExpectedSchema == nil && test.ExpectedSqlSchema == nil) {
  1730  		require.Fail(t, "Incorrect test setup: schema and rows must both be provided if one is")
  1731  	}
  1732  
  1733  	if len(test.ExpectedErr) == 0 && (test.ExpectedSchema == nil) == (test.ExpectedSqlSchema == nil) {
  1734  		require.Fail(t, "Incorrect test setup: must set at most one of ExpectedSchema, ExpectedSqlSchema")
  1735  	}
  1736  
  1737  	if len(singleSelectQueryTest) > 0 && test.Name != singleSelectQueryTest {
  1738  		t.Skip("Skipping tests until " + singleSelectQueryTest)
  1739  	}
  1740  
  1741  	if len(singleSelectQueryTest) == 0 && test.SkipOnSqlEngine && skipBrokenSelect {
  1742  		t.Skip("Skipping test broken on SQL engine")
  1743  	}
  1744  }