mirror of
https://github.com/dolthub/dolt.git
synced 2026-01-31 20:38:55 -06:00
refactored tests, added validation
This commit is contained in:
@@ -16,13 +16,9 @@ package querydiff
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/liquidata-inc/go-mysql-server/sql/expression"
|
||||
|
||||
sqle "github.com/liquidata-inc/go-mysql-server"
|
||||
"github.com/liquidata-inc/go-mysql-server/sql"
|
||||
"github.com/liquidata-inc/go-mysql-server/sql/expression"
|
||||
"github.com/liquidata-inc/go-mysql-server/sql/plan"
|
||||
)
|
||||
|
||||
@@ -161,24 +157,3 @@ func shiftIndicesForSortFields(offset int, order ...plan.SortField) []plan.SortF
|
||||
}
|
||||
return shifted
|
||||
}
|
||||
|
||||
func errWithQueryPlan(ctx *sql.Context, eng *sqle.Engine, query string, cause error) error {
|
||||
_, iter, err := eng.Query(ctx, fmt.Sprintf("describe %s", query))
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot diff query. Error describing query plan: %s\n", err.Error())
|
||||
}
|
||||
|
||||
var qp strings.Builder
|
||||
qp.WriteString("query plan:\n")
|
||||
for {
|
||||
r, err := iter.Next()
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return fmt.Errorf("cannot diff query. Error describing query plan: %s\n", err.Error())
|
||||
}
|
||||
qp.WriteString(fmt.Sprintf("\t%s\n", r[0].(string)))
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot diff query: %s\n%s", cause.Error(), qp.String())
|
||||
}
|
||||
|
||||
@@ -17,13 +17,15 @@ package querydiff
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
|
||||
sqle "github.com/liquidata-inc/go-mysql-server"
|
||||
"github.com/liquidata-inc/go-mysql-server/sql"
|
||||
"github.com/liquidata-inc/go-mysql-server/sql/expression/function"
|
||||
"github.com/liquidata-inc/go-mysql-server/sql/parse"
|
||||
"github.com/liquidata-inc/go-mysql-server/sql/plan"
|
||||
"io"
|
||||
"math"
|
||||
"strings"
|
||||
"vitess.io/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/liquidata-inc/dolt/go/libraries/doltcore/doltdb"
|
||||
"github.com/liquidata-inc/dolt/go/libraries/doltcore/env"
|
||||
@@ -153,7 +155,7 @@ func (qd *QueryDiffer) NextDiff() (fromRow sql.Row, toRow sql.Row, err error) {
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
eq, err := fromRow.Equals(toRow, qd.sch)
|
||||
eq, err := nullSafeRowEquality(toRow, fromRow, qd.sch)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -219,12 +221,31 @@ func (qd *QueryDiffer) Schema() sql.Schema {
|
||||
return qd.sch
|
||||
}
|
||||
|
||||
func (qd *QueryDiffer) Order() []plan.SortField {
|
||||
return qd.order
|
||||
}
|
||||
|
||||
func (qd *QueryDiffer) Close() error {
|
||||
qd.from.close()
|
||||
qd.to.close()
|
||||
return qd.ae.Get()
|
||||
}
|
||||
|
||||
func nullSafeRowEquality(left, right sql.Row, sch sql.Schema) (bool, error) {
|
||||
if len(left) != len(right) {
|
||||
return false, nil
|
||||
}
|
||||
for i := range left {
|
||||
if left[i] == sql.Null {
|
||||
left[i] = nil // RIP Lisa Lopes
|
||||
}
|
||||
if right[i] == sql.Null {
|
||||
right[i] = nil
|
||||
}
|
||||
}
|
||||
return left.Equals(right, sch)
|
||||
}
|
||||
|
||||
func makeSqlEngine(ctx context.Context, dEnv *env.DoltEnv, root *doltdb.RootValue) (*sql.Context, *sqle.Engine, error) {
|
||||
doltSqlDB := dsqle.NewDatabase("db", dEnv.DoltDB, dEnv.RepoState, dEnv.RepoStateWriter())
|
||||
|
||||
@@ -260,6 +281,11 @@ func makeSqlEngine(ctx context.Context, dEnv *env.DoltEnv, root *doltdb.RootValu
|
||||
}
|
||||
|
||||
func getQueryPlans(fromCtx, toCtx *sql.Context, fromEng, toEng *sqle.Engine, query string) (fromPlan, toPlan sql.Node, err error) {
|
||||
err = validateQueryType(fromCtx, fromEng, query)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
parsed, err := parse.Parse(fromCtx, query)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -269,11 +295,139 @@ func getQueryPlans(fromCtx, toCtx *sql.Context, fromEng, toEng *sqle.Engine, que
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error executing query on from root: %s", err.Error())
|
||||
}
|
||||
err = validateQueryPlan(fromCtx, fromEng, fromPlan, query)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
toPlan, err = toEng.Analyzer.Analyze(toCtx, parsed)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error executing query on to root: %s", err.Error())
|
||||
}
|
||||
err = validateQueryPlan(toCtx, toEng, toPlan, query)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return fromPlan, toPlan, nil
|
||||
}
|
||||
|
||||
func validateQueryType(ctx *sql.Context, eng *sqle.Engine, query string) error {
|
||||
sqlStatement, err := sqlparser.Parse(query)
|
||||
if err == sqlparser.ErrEmpty {
|
||||
return QueryDiffError{cause: "query is empty"}
|
||||
} else if err != nil {
|
||||
return QueryDiffError{cause: err.Error()}
|
||||
}
|
||||
|
||||
if strings.Contains(strings.ToLower(query), "information_schema") {
|
||||
return QueryDiffError{cause: "querying information_schema is not supported"}
|
||||
}
|
||||
|
||||
switch sqlStatement.(type) {
|
||||
case *sqlparser.Select:
|
||||
return nil
|
||||
case *sqlparser.Union:
|
||||
return errPrintQueryPlan(ctx, eng, query, "UNION queries not supported")
|
||||
case *sqlparser.Show:
|
||||
return errPrintQueryPlan(ctx, eng, query, "SHOW queries not supported")
|
||||
case *sqlparser.OtherRead, *sqlparser.Explain:
|
||||
return errPrintQueryPlan(ctx, eng, query, "EXPLAIN queries not supported")
|
||||
case *sqlparser.Use:
|
||||
return errPrintQueryPlan(ctx, eng, query, "USE queries not supported")
|
||||
case *sqlparser.Set:
|
||||
return errPrintQueryPlan(ctx, eng, query, "SET queries not supported")
|
||||
case *sqlparser.Insert, *sqlparser.Update, *sqlparser.Delete:
|
||||
return errPrintQueryPlan(ctx, eng, query, "write queries not supported")
|
||||
case *sqlparser.DDL:
|
||||
return errPrintQueryPlan(ctx, eng, query, "DDL queries not supported")
|
||||
default:
|
||||
return QueryDiffError{cause: fmt.Sprintf("cannot diff query, unsupported SQL statement: '%s'", query)}
|
||||
}
|
||||
}
|
||||
|
||||
func validateQueryPlan(ctx *sql.Context, eng *sqle.Engine, node sql.Node, query string) (err error) {
|
||||
if node == plan.Nothing || node == plan.EmptyTable{
|
||||
return errPrintQueryPlan(ctx, eng, query, "queries returning no rows are not supported")
|
||||
}
|
||||
|
||||
switch node.(type) {
|
||||
case *plan.Distinct:
|
||||
// todo: create DiffDistinct node that evaluates projections before hashing
|
||||
return errPrintQueryPlan(ctx, eng, query, "DISTINCT queries not supported")
|
||||
case *plan.Generate:
|
||||
return errPrintQueryPlan(ctx, eng, query, "EXPLODE queries not supported")
|
||||
case *plan.Commit:
|
||||
return errPrintQueryPlan(ctx, eng, query, "COMMIT queries not supported")
|
||||
case *plan.Rollback:
|
||||
return errPrintQueryPlan(ctx, eng, query, "ROLLBACK queries not supported")
|
||||
case *plan.UnresolvedTable:
|
||||
return errPrintQueryPlan(ctx, eng, query, "query references unresolved table")
|
||||
}
|
||||
|
||||
unsupportedTables := []string{
|
||||
"dual",
|
||||
}
|
||||
if rt, ok := node.(*plan.ResolvedTable); ok {
|
||||
for _, tn := range unsupportedTables {
|
||||
if strings.ToLower(rt.Table.Name()) == tn {
|
||||
return errPrintQueryPlan(ctx, eng, query, fmt.Sprintf("queries on table '%s' not supported", tn))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_, err = plan.TransformExpressions(node, func(e sql.Expression) (sql.Expression, error) {
|
||||
switch e.(type) {
|
||||
case *function.JSONExtract,
|
||||
*function.JSONUnquote:
|
||||
return nil, errPrintQueryPlan(ctx, eng, query, "JSON functions not supported")
|
||||
case *function.Explode:
|
||||
return nil, errPrintQueryPlan(ctx, eng, query, "EXPLODE function not supported")
|
||||
}
|
||||
return e, nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, c := range node.Children() {
|
||||
err = validateQueryPlan(ctx, eng, c, query)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type QueryDiffError struct {
|
||||
plan string
|
||||
cause string
|
||||
}
|
||||
|
||||
var _ error = QueryDiffError{}
|
||||
|
||||
func (qde QueryDiffError) Error() string {
|
||||
return fmt.Sprintf("cannot diff query: %s\n%s", qde.cause, qde.plan)
|
||||
}
|
||||
|
||||
func errPrintQueryPlan(ctx *sql.Context, eng *sqle.Engine, query string, cause string) error {
|
||||
_, iter, err := eng.Query(ctx, fmt.Sprintf("describe %s", query))
|
||||
if err != nil {
|
||||
return QueryDiffError{cause: fmt.Sprintf("cannot diff query: %s\n", err.Error())}
|
||||
}
|
||||
|
||||
var qp strings.Builder
|
||||
qp.WriteString("query plan:\n")
|
||||
for {
|
||||
r, err := iter.Next()
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return QueryDiffError{cause: fmt.Sprintf("cannot diff query: %s\n", err.Error())}
|
||||
}
|
||||
qp.WriteString(fmt.Sprintf("\t%s\n", r[0].(string)))
|
||||
}
|
||||
|
||||
return QueryDiffError{plan: qp.String(), cause: cause}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,14 @@ package querydiff_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/liquidata-inc/go-mysql-server/sql/expression"
|
||||
"github.com/liquidata-inc/go-mysql-server/sql/plan"
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/liquidata-inc/go-mysql-server/enginetest"
|
||||
"github.com/liquidata-inc/go-mysql-server/sql"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -28,34 +33,13 @@ import (
|
||||
"github.com/liquidata-inc/dolt/go/libraries/doltcore/diff/querydiff"
|
||||
"github.com/liquidata-inc/dolt/go/libraries/doltcore/dtestutils"
|
||||
"github.com/liquidata-inc/dolt/go/libraries/doltcore/env"
|
||||
det "github.com/liquidata-inc/dolt/go/libraries/doltcore/sqle/enginetest"
|
||||
)
|
||||
|
||||
func TestQueryDiffer(t *testing.T) {
|
||||
queryDiffTests := [][]queryDifferTest{
|
||||
selectTests,
|
||||
joinTests,
|
||||
groupByTests,
|
||||
}
|
||||
for _, testSet := range queryDiffTests {
|
||||
for _, test := range testSet {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
testQueryDiffer(t, test)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
dEnv := setupEngineTests(t)
|
||||
engineTestQueries := [][]engineTestQuery{
|
||||
engineTestSelectQueries,
|
||||
engineTestAggregateQueries,
|
||||
}
|
||||
for _, testSet := range engineTestQueries {
|
||||
for _, test := range testSet {
|
||||
t.Run(test.query, func(t *testing.T) {
|
||||
engineTestQueryDiffer(t, test, dEnv)
|
||||
})
|
||||
}
|
||||
}
|
||||
testQueryDiffer(t)
|
||||
TestEngineTestQueryDifferBefore(t)
|
||||
EngineTestQueryDifferAfter(t)
|
||||
}
|
||||
|
||||
type queryDifferTest struct {
|
||||
@@ -97,6 +81,12 @@ var queryDifferTestSetup = []testCommand{
|
||||
{commands.CommitCmd{}, []string{"-m", "setup common"}},
|
||||
}
|
||||
|
||||
var queryDiffTests = [][]queryDifferTest{
|
||||
selectTests,
|
||||
joinTests,
|
||||
groupByTests,
|
||||
}
|
||||
|
||||
var selectTests = []queryDifferTest{
|
||||
{
|
||||
name: "query diff",
|
||||
@@ -328,50 +318,53 @@ var groupByTests = []queryDifferTest{
|
||||
},
|
||||
}
|
||||
|
||||
func testQueryDiffer(t *testing.T, test queryDifferTest) {
|
||||
dEnv := dtestutils.CreateTestEnv()
|
||||
ctx := context.Background()
|
||||
func testQueryDiffer(t *testing.T) {
|
||||
inner := func(t *testing.T, test queryDifferTest) {
|
||||
dEnv := dtestutils.CreateTestEnv()
|
||||
ctx := context.Background()
|
||||
for _, c := range queryDifferTestSetup {
|
||||
exitCode := c.cmd.Exec(ctx, c.cmd.Name(), c.args, dEnv)
|
||||
assert.Equal(t, 0, exitCode)
|
||||
}
|
||||
for _, c := range test.setup {
|
||||
exitCode := c.cmd.Exec(ctx, c.cmd.Name(), c.args, dEnv)
|
||||
assert.Equal(t, 0, exitCode)
|
||||
}
|
||||
|
||||
for _, c := range queryDifferTestSetup {
|
||||
exitCode := c.cmd.Exec(ctx, c.cmd.Name(), c.args, dEnv)
|
||||
assert.Equal(t, 0, exitCode)
|
||||
}
|
||||
fromRoot, err := dEnv.HeadRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
toRoot, err := dEnv.WorkingRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, c := range test.setup {
|
||||
exitCode := c.cmd.Exec(ctx, c.cmd.Name(), c.args, dEnv)
|
||||
assert.Equal(t, 0, exitCode)
|
||||
}
|
||||
qd, err := querydiff.MakeQueryDiffer(ctx, dEnv, fromRoot, toRoot, test.query)
|
||||
require.NoError(t, err)
|
||||
|
||||
fromRoot, err := dEnv.HeadRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
toRoot, err := dEnv.WorkingRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
qd, err := querydiff.MakeQueryDiffer(ctx, dEnv, fromRoot, toRoot, test.query)
|
||||
require.NoError(t, err)
|
||||
|
||||
qd.Start()
|
||||
for _, expected := range test.diffRows {
|
||||
qd.Start()
|
||||
for _, expected := range test.diffRows {
|
||||
from, to, err := qd.NextDiff()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected.from, from)
|
||||
assert.Equal(t, expected.to, to)
|
||||
}
|
||||
from, to, err := qd.NextDiff()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected.from, from)
|
||||
assert.Equal(t, expected.to, to)
|
||||
assert.Nil(t, from)
|
||||
assert.Nil(t, to)
|
||||
assert.Equal(t, io.EOF, err)
|
||||
}
|
||||
|
||||
for _, testSet := range queryDiffTests {
|
||||
for _, test := range testSet {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
inner(t, test)
|
||||
})
|
||||
}
|
||||
}
|
||||
from, to, err := qd.NextDiff()
|
||||
assert.Nil(t, from)
|
||||
assert.Nil(t, to)
|
||||
assert.Equal(t, io.EOF, err)
|
||||
}
|
||||
|
||||
// subset of test data from go-mysql-server/enginetest/testdata.go:CreateSubsetTestData()
|
||||
var engineTestSetup = []testCommand{
|
||||
{commands.SqlCmd{}, []string{"-q", "create table mytable (" +
|
||||
"i bigint primary key," +
|
||||
"s text);"}},
|
||||
{commands.SqlCmd{}, []string{"-q", "insert into mytable values " +
|
||||
"(1, 'first row'), " +
|
||||
"(2, 'second row'), " +
|
||||
"(3, 'third row');"}},
|
||||
{commands.SqlCmd{}, []string{"-q", "create table one_pk (" +
|
||||
"pk tinyint primary key, " +
|
||||
"c1 tinyint," +
|
||||
@@ -379,11 +372,6 @@ var engineTestSetup = []testCommand{
|
||||
"c3 tinyint," +
|
||||
"c4 tinyint," +
|
||||
"c5 tinyint);"}},
|
||||
{commands.SqlCmd{}, []string{"-q", "insert into one_pk values " +
|
||||
"(0, 0, 0, 0, 0, 0)," +
|
||||
"(1, 10, 10, 10, 10, 10)," +
|
||||
"(2, 20, 20, 20, 20, 20)," +
|
||||
"(3, 30, 30, 30, 30, 30);"}},
|
||||
{commands.SqlCmd{}, []string{"-q", "create table two_pk (" +
|
||||
"pk1 tinyint," +
|
||||
"pk2 tinyint," +
|
||||
@@ -393,38 +381,73 @@ var engineTestSetup = []testCommand{
|
||||
"c4 tinyint," +
|
||||
"c5 tinyint," +
|
||||
"primary key (pk1, pk2));"}},
|
||||
{commands.SqlCmd{}, []string{"-q", "insert into two_pk values " +
|
||||
"(0, 0, 0, 0, 0, 0, 0)," +
|
||||
"(0, 1, 10, 10, 10, 10, 10)," +
|
||||
"(1, 0, 20, 20, 20, 20, 20)," +
|
||||
"(1, 1, 30, 30, 30, 30, 30);"}},
|
||||
{commands.SqlCmd{}, []string{"-q", "create table othertable (" +
|
||||
"s2 text primary key," +
|
||||
"i2 bigint);"}},
|
||||
{commands.SqlCmd{}, []string{"-q", "insert into othertable values " +
|
||||
"('first', 3)," +
|
||||
"('second', 2)," +
|
||||
"('third', 1);"}},
|
||||
{commands.SqlCmd{}, []string{"-q", "create table tabletest (" +
|
||||
"i int primary key," +
|
||||
"s text);"}},
|
||||
{commands.SqlCmd{}, []string{"-q", "insert into tabletest values " +
|
||||
"(1, 'first row')," +
|
||||
"(2, 'second row')," +
|
||||
"(3, 'third row');"}},
|
||||
{commands.SqlCmd{}, []string{"-q", "create table emptytable (" +
|
||||
"i int primary key," +
|
||||
"s text);"}},
|
||||
{commands.SqlCmd{}, []string{"-q", "create table other_table (" +
|
||||
"`text` text primary key," +
|
||||
"number int);"}},
|
||||
{commands.SqlCmd{}, []string{"-q", "create table bigtable (" +
|
||||
"t text primary key," +
|
||||
"n bigint);"}},
|
||||
{commands.SqlCmd{}, []string{"-q", "create table floattable (" +
|
||||
"i bigint primary key," +
|
||||
"f32 float," +
|
||||
"f64 double);"}},
|
||||
{commands.SqlCmd{}, []string{"-q", "create table niltable (" +
|
||||
"i bigint primary key," +
|
||||
"i2 bigint," +
|
||||
"b bool," +
|
||||
"f float);"}},
|
||||
{commands.SqlCmd{}, []string{"-q", "create table newlinetable (" +
|
||||
"i bigint primary key," +
|
||||
"s text);"}},
|
||||
{commands.SqlCmd{}, []string{"-q", "create table stringandtable (" +
|
||||
"k bigint primary key," +
|
||||
"i bigint," +
|
||||
"v text);"}},
|
||||
{commands.SqlCmd{}, []string{"-q", "create table reservedWordsTable (" +
|
||||
"`Timestamp` text primary key," +
|
||||
"`and` text," +
|
||||
"`or` text," +
|
||||
"`select` text);"}},
|
||||
{commands.SqlCmd{}, []string{"-q", `create view myview as select * from mytable`}},
|
||||
//{commands.SqlCmd{}, []string{"-q", "create view myview1 as select * from myhistorytable"}},
|
||||
{commands.SqlCmd{}, []string{"-q", "create view myview2 as select * from myview where i = 1"}},
|
||||
{commands.AddCmd{}, []string{"."}},
|
||||
{commands.CommitCmd{}, []string{"-m", "setup enginetest test tables"}},
|
||||
{commands.SqlCmd{}, []string{"-q", "insert into mytable values " +
|
||||
"(1, 'first row'), " +
|
||||
"(2, 'second row'), " +
|
||||
"(3, 'third row');"}},
|
||||
{commands.SqlCmd{}, []string{"-q", "insert into one_pk values " +
|
||||
"(0, 0, 0, 0, 0, 0)," +
|
||||
"(1, 10, 10, 10, 10, 10)," +
|
||||
"(2, 20, 20, 20, 20, 20)," +
|
||||
"(3, 30, 30, 30, 30, 30);"}},
|
||||
{commands.SqlCmd{}, []string{"-q", "insert into two_pk values " +
|
||||
"(0, 0, 0, 0, 0, 0, 0)," +
|
||||
"(0, 1, 10, 10, 10, 10, 10)," +
|
||||
"(1, 0, 20, 20, 20, 20, 20)," +
|
||||
"(1, 1, 30, 30, 30, 30, 30);"}},
|
||||
{commands.SqlCmd{}, []string{"-q", "insert into othertable values " +
|
||||
"('first', 3)," +
|
||||
"('second', 2)," +
|
||||
"('third', 1);"}},
|
||||
{commands.SqlCmd{}, []string{"-q", "insert into tabletest values " +
|
||||
"(1, 'first row')," +
|
||||
"(2, 'second row')," +
|
||||
"(3, 'third row');"}},
|
||||
{commands.SqlCmd{}, []string{"-q", "insert into other_table values " +
|
||||
"('a', 4)," +
|
||||
"('b', 2)," +
|
||||
"('c', 0);"}},
|
||||
{commands.SqlCmd{}, []string{"-q", "create table bigtable (" +
|
||||
"t text primary key," +
|
||||
"n bigint);"}},
|
||||
{commands.SqlCmd{}, []string{"-q", "insert into bigtable values " +
|
||||
"('a', 1)," +
|
||||
"('s', 2)," +
|
||||
@@ -440,10 +463,6 @@ var engineTestSetup = []testCommand{
|
||||
"('c', 7)," +
|
||||
"('v', 8)," +
|
||||
"('b', 9);"}},
|
||||
{commands.SqlCmd{}, []string{"-q", "create table floattable (" +
|
||||
"i bigint primary key," +
|
||||
"f32 float," +
|
||||
"f64 double);"}},
|
||||
{commands.SqlCmd{}, []string{"-q", "insert into floattable values " +
|
||||
"(1, 1.0, 1.0)," +
|
||||
"(2, 1.5, 1.5)," +
|
||||
@@ -451,11 +470,6 @@ var engineTestSetup = []testCommand{
|
||||
"(4, 2.5, 2.5)," +
|
||||
"(-1, -1.0, -1.0)," +
|
||||
"(-2, -1.5, -1.5);"}},
|
||||
{commands.SqlCmd{}, []string{"-q", "create table niltable (" +
|
||||
"i bigint primary key," +
|
||||
"i2 bigint," +
|
||||
"b bool," +
|
||||
"f float);"}},
|
||||
{commands.SqlCmd{}, []string{"-q", "insert into niltable values " +
|
||||
"(1, NULL, NULL, NULL)," +
|
||||
"(2, 2, 1, NULL)," +
|
||||
@@ -463,19 +477,12 @@ var engineTestSetup = []testCommand{
|
||||
"(4, 4, NULL, 4)," +
|
||||
"(5, NULL, 1, 5)," +
|
||||
"(6, 6, 0, 6);"}},
|
||||
{commands.SqlCmd{}, []string{"-q", "create table newlinetable (" +
|
||||
"i bigint primary key," +
|
||||
"s text);"}},
|
||||
{commands.SqlCmd{}, []string{"-q", "insert into newlinetable values " +
|
||||
"(1, '\nthere is some text in here')," +
|
||||
"(2, 'there is some\ntext in here')," +
|
||||
"(3, 'there is some text\nin here')," +
|
||||
"(4, 'there is some text in here\n')," +
|
||||
"(5, 'there is some text in here');"}},
|
||||
{commands.SqlCmd{}, []string{"-q", "create table stringandtable (" +
|
||||
"k bigint primary key," +
|
||||
"i bigint," +
|
||||
"v text);"}},
|
||||
{commands.SqlCmd{}, []string{"-q", "insert into stringandtable values " +
|
||||
"(0, 0, '0')," +
|
||||
"(1, 1, '1')," +
|
||||
@@ -484,12 +491,11 @@ var engineTestSetup = []testCommand{
|
||||
"(4, 4, 'false')," +
|
||||
"(5, 5, NULL)," +
|
||||
"(6, NULL, '2');"}},
|
||||
{commands.AddCmd{}, []string{"."}},
|
||||
{commands.CommitCmd{}, []string{"-m", "setup enginetest test data"}},
|
||||
{commands.SqlCmd{}, []string{"-q", `insert into reservedWordsTable values
|
||||
("1", "1.1", "aaa", "create");`}},
|
||||
}
|
||||
|
||||
func setupEngineTests(t *testing.T) *env.DoltEnv {
|
||||
// engineTestQueries are read-only, sharing a dEnv speeds up tests
|
||||
dEnv := dtestutils.CreateTestEnv()
|
||||
for _, c := range engineTestSetup {
|
||||
exitCode := c.cmd.Exec(context.Background(), c.cmd.Name(), c.args, dEnv)
|
||||
@@ -498,370 +504,159 @@ func setupEngineTests(t *testing.T) *env.DoltEnv {
|
||||
return dEnv
|
||||
}
|
||||
|
||||
type engineTestQuery struct {
|
||||
query string
|
||||
func commitData(t *testing.T, dEnv *env.DoltEnv) {
|
||||
exitCode := commands.AddCmd{}.Exec(context.Background(), "Add", []string{"."}, dEnv)
|
||||
assert.Equal(t, 0, exitCode)
|
||||
exitCode = commands.CommitCmd{}.Exec(context.Background(), "Commit", []string{"-m", "setup enginetest test tables"}, dEnv)
|
||||
assert.Equal(t, 0, exitCode)
|
||||
}
|
||||
|
||||
var engineTestSelectQueries = []engineTestQuery{
|
||||
{query: "SELECT * FROM mytable INNER JOIN othertable ON i = i2 ORDER BY i"},
|
||||
{query: "SELECT i, i2, s2 FROM mytable INNER JOIN othertable ON i = i2 ORDER BY i"},
|
||||
{query: "SELECT s2, i2, i FROM mytable INNER JOIN othertable ON i = i2 ORDER BY i"},
|
||||
{query: "SELECT * FROM mytable;"},
|
||||
{query: "SELECT * FROM mytable ORDER BY i DESC;"},
|
||||
{query: "SELECT i FROM mytable;"},
|
||||
{query: "SELECT i FROM mytable AS mt;"},
|
||||
{query: "SELECT s,i FROM mytable;"},
|
||||
{query: "SELECT mytable.s,i FROM mytable;"},
|
||||
{query: "SELECT t.s,i FROM mytable AS t;"},
|
||||
{query: "SELECT s,i FROM (select i,s FROM mytable) mt;"},
|
||||
{query: "SELECT s,i FROM MyTable ORDER BY 2"},
|
||||
{query: "SELECT S,I FROM MyTable ORDER BY 2"},
|
||||
{query: "SELECT mt.s,mt.i FROM MyTable MT ORDER BY 2;"},
|
||||
{query: "SELECT mT.S,Mt.I FROM MyTable MT ORDER BY 2;"},
|
||||
{query: "SELECT mt.* FROM MyTable MT ORDER BY mT.I;"},
|
||||
{query: "SELECT MyTABLE.s,myTable.i FROM MyTable ORDER BY 2;"},
|
||||
{query: "SELECT myTable.* FROM MYTABLE ORDER BY myTable.i;"},
|
||||
{query: "SELECT MyTABLE.S,myTable.I FROM MyTable ORDER BY mytable.i;"},
|
||||
{query: "SELECT i + 1 FROM mytable;"},
|
||||
{query: "SELECT i div 2 FROM mytable order by 1;"},
|
||||
{query: "SELECT i DIV 2 FROM mytable order by 1;"},
|
||||
{query: "SELECT -i FROM mytable;"},
|
||||
{query: "SELECT +i FROM mytable;"},
|
||||
{query: "SELECT + - i FROM mytable;"},
|
||||
{query: "SELECT i FROM mytable WHERE -i = -2;"},
|
||||
{query: "SELECT i FROM mytable WHERE i = 2;"},
|
||||
{query: "SELECT i FROM mytable WHERE 2 = i;"},
|
||||
{query: "SELECT i FROM mytable WHERE i > 2;"},
|
||||
{query: "SELECT i FROM mytable WHERE 2 < i;"},
|
||||
{query: "SELECT i FROM mytable WHERE i < 2;"},
|
||||
{query: "SELECT i FROM mytable WHERE 2 > i;"},
|
||||
{query: "SELECT i FROM mytable WHERE i <> 2;"},
|
||||
{query: "SELECT i FROM mytable WHERE i IN (1, 3)"},
|
||||
{query: "SELECT i FROM mytable WHERE i = 1 OR i = 3"},
|
||||
{query: "SELECT i FROM mytable WHERE i >= 2 ORDER BY 1"},
|
||||
{query: "SELECT i FROM mytable WHERE 2 <= i ORDER BY 1"},
|
||||
{query: "SELECT i FROM mytable WHERE i <= 2 ORDER BY 1"},
|
||||
{query: "SELECT i FROM mytable WHERE 2 >= i ORDER BY 1"},
|
||||
{query: "SELECT i FROM mytable WHERE i > 2"},
|
||||
{query: "SELECT i FROM mytable WHERE i < 2"},
|
||||
{query: "SELECT i FROM mytable WHERE i >= 2 OR i = 1 ORDER BY 1"},
|
||||
{query: "SELECT f32 FROM floattable WHERE f64 = 2.0;"},
|
||||
{query: "SELECT f32 FROM floattable WHERE f64 < 2.0;"},
|
||||
{query: "SELECT f32 FROM floattable WHERE f64 > 2.0;"},
|
||||
{query: "SELECT f32 FROM floattable WHERE f64 <> 2.0;"},
|
||||
{query: "SELECT f64 FROM floattable WHERE f32 = 2.0;"},
|
||||
{query: "SELECT f64 FROM floattable WHERE f32 = -1.5;"},
|
||||
{query: "SELECT f64 FROM floattable WHERE -f32 = -2.0;"},
|
||||
{query: "SELECT f64 FROM floattable WHERE f32 < 2.0;"},
|
||||
{query: "SELECT f64 FROM floattable WHERE f32 > 2.0;"},
|
||||
{query: "SELECT f64 FROM floattable WHERE f32 <> 2.0;"},
|
||||
{query: "SELECT f32 FROM floattable ORDER BY f64;"},
|
||||
{query: "SELECT i FROM mytable ORDER BY i DESC;"},
|
||||
{query: "SELECT i FROM mytable WHERE 'hello';"},
|
||||
{query: "SELECT i FROM mytable WHERE NOT 'hello';"},
|
||||
{query: "SELECT i FROM mytable WHERE s = 'first row' ORDER BY i DESC;"},
|
||||
{query: "SELECT i FROM mytable WHERE s = 'first row' ORDER BY i DESC LIMIT 1;"},
|
||||
{query: "SELECT i FROM mytable ORDER BY i LIMIT 1 OFFSET 1;"},
|
||||
{query: "SELECT i FROM mytable ORDER BY i LIMIT 1,1;"},
|
||||
{query: "SELECT i FROM mytable ORDER BY i LIMIT 3,1;"},
|
||||
{query: "SELECT i FROM mytable ORDER BY i LIMIT 2,100;"},
|
||||
{query: "SELECT i FROM niltable WHERE b IS NULL"},
|
||||
{query: "SELECT i FROM niltable WHERE b IS NOT NULL"},
|
||||
{query: "SELECT i FROM niltable WHERE b"},
|
||||
{query: "SELECT i FROM niltable WHERE NOT b"},
|
||||
{query: "SELECT i FROM niltable WHERE b IS TRUE"},
|
||||
{query: "SELECT i FROM niltable WHERE b IS NOT TRUE"},
|
||||
{query: "SELECT f FROM niltable WHERE b IS FALSE"},
|
||||
{query: "SELECT i FROM niltable WHERE f < 5"},
|
||||
{query: "SELECT i FROM niltable WHERE f > 5"},
|
||||
{query: "SELECT i FROM niltable WHERE b IS NOT FALSE"},
|
||||
{query: "SELECT substring(s, 2, 3) FROM mytable"},
|
||||
{query: `SELECT substring("foo", 2, 2)`},
|
||||
{query: `SELECT SUBSTRING_INDEX('a.b.c.d.e.f', '.', 2)`},
|
||||
{query: `SELECT SUBSTRING_INDEX('a.b.c.d.e.f', '.', -2)`},
|
||||
{query: `SELECT SUBSTRING_INDEX(SUBSTRING_INDEX('source{d}', '{d}', 1), 'r', -1)`},
|
||||
{query: "SELECT YEAR('2007-12-11') FROM mytable"},
|
||||
{query: "SELECT MONTH('2007-12-11') FROM mytable"},
|
||||
{query: "SELECT DAY('2007-12-11') FROM mytable"},
|
||||
{query: "SELECT HOUR('2007-12-11 20:21:22') FROM mytable"},
|
||||
{query: "SELECT MINUTE('2007-12-11 20:21:22') FROM mytable"},
|
||||
{query: "SELECT SECOND('2007-12-11 20:21:22') FROM mytable"},
|
||||
{query: "SELECT DAYOFYEAR('2007-12-11 20:21:22') FROM mytable"},
|
||||
{query: "SELECT SECOND('2007-12-11T20:21:22Z') FROM mytable"},
|
||||
{query: "SELECT DAYOFYEAR('2007-12-11') FROM mytable"},
|
||||
{query: "SELECT DAYOFYEAR('20071211') FROM mytable"},
|
||||
{query: "SELECT YEARWEEK('0000-01-01')"},
|
||||
{query: "SELECT YEARWEEK('9999-12-31')"},
|
||||
{query: "SELECT YEARWEEK('2008-02-20', 1)"},
|
||||
{query: "SELECT YEARWEEK('1987-01-01')"},
|
||||
{query: "SELECT YEARWEEK('1987-01-01', 20), YEARWEEK('1987-01-01', 1), YEARWEEK('1987-01-01', 2), YEARWEEK('1987-01-01', 3), YEARWEEK('1987-01-01', 4), YEARWEEK('1987-01-01', 5), YEARWEEK('1987-01-01', 6), YEARWEEK('1987-01-01', 7)"},
|
||||
{query: "SELECT i FROM mytable WHERE i BETWEEN 1 AND 2"},
|
||||
{query: "SELECT i FROM mytable WHERE i NOT BETWEEN 1 AND 2"},
|
||||
{query: "SELECT i,v from stringandtable WHERE i"},
|
||||
{query: "SELECT i,v from stringandtable WHERE i AND i"},
|
||||
{query: "SELECT i,v from stringandtable WHERE i OR i"},
|
||||
{query: "SELECT i,v from stringandtable WHERE NOT i"},
|
||||
{query: "SELECT i,v from stringandtable WHERE NOT i AND NOT i"},
|
||||
{query: "SELECT i,v from stringandtable WHERE NOT i OR NOT i"},
|
||||
{query: "SELECT i,v from stringandtable WHERE i OR NOT i"},
|
||||
{query: "SELECT i,v from stringandtable WHERE v"},
|
||||
{query: "SELECT i,v from stringandtable WHERE v AND v"},
|
||||
{query: "SELECT i,v from stringandtable WHERE v OR v"},
|
||||
{query: "SELECT i,v from stringandtable WHERE NOT v"},
|
||||
{query: "SELECT i,v from stringandtable WHERE NOT v AND NOT v"},
|
||||
{query: "SELECT i,v from stringandtable WHERE NOT v OR NOT v"},
|
||||
{query: "SELECT i,v from stringandtable WHERE v OR NOT v"},
|
||||
{query: "SELECT i, i2, s2 FROM mytable INNER JOIN othertable ON i = i2 ORDER BY i"},
|
||||
{query: "SELECT s2, i2, i FROM mytable INNER JOIN othertable ON i = i2 ORDER BY i"},
|
||||
{query: "SELECT i, i2, s2 FROM othertable JOIN mytable ON i = i2 ORDER BY i"},
|
||||
{query: "SELECT s2, i2, i FROM othertable JOIN mytable ON i = i2 ORDER BY i"},
|
||||
{query: "SELECT substring(s2, 1), substring(s2, 2), substring(s2, 3) FROM othertable ORDER BY i2"},
|
||||
{query: `SELECT substring("first", 1), substring("second", 2), substring("third", 3)`},
|
||||
{query: "SELECT substring(s2, -1), substring(s2, -2), substring(s2, -3) FROM othertable ORDER BY i2"},
|
||||
{query: `SELECT substring("first", -1), substring("second", -2), substring("third", -3)`},
|
||||
{query: "SELECT s FROM mytable INNER JOIN othertable ON substring(s2, 1, 2) != '' AND i = i2 ORDER BY 1"},
|
||||
{query: `SELECT i FROM mytable NATURAL JOIN tabletest`},
|
||||
{query: `SELECT i FROM mytable AS t NATURAL JOIN tabletest AS test`},
|
||||
// TODO: (from enginetest) this should work: either table alias should be usable in the select clause
|
||||
// {query: `SELECT t.i, test.s FROM mytable AS t NATURAL JOIN tabletest AS test`},
|
||||
{query: "SELECT CAST(-3 AS UNSIGNED) FROM mytable"},
|
||||
{query: "SELECT CONVERT(-3, UNSIGNED) FROM mytable"},
|
||||
{query: "SELECT '3' > 2 FROM tabletest"},
|
||||
{query: "SELECT s > 2 FROM tabletest"},
|
||||
{query: "SELECT * FROM tabletest WHERE s > 0"},
|
||||
{query: "SELECT * FROM tabletest WHERE s = 0"},
|
||||
{query: "SELECT * FROM tabletest WHERE s = 'first row'"},
|
||||
{query: "SELECT s FROM mytable WHERE i IN (1, 2, 5)"},
|
||||
{query: "SELECT s FROM mytable WHERE i NOT IN (1, 2, 5)"},
|
||||
{query: "SELECT 1 + 2"},
|
||||
{query: `SELECT i AS foo FROM mytable WHERE foo NOT IN (1, 2, 5)`},
|
||||
{query: `SELECT * FROM tabletest, mytable mt INNER JOIN othertable ot ON mt.i = ot.i2`},
|
||||
{query: `SELECT split(s," ") FROM mytable`},
|
||||
{query: `SELECT split(s,"s") FROM mytable`},
|
||||
// todo: (from enginetest) data alignment issue?
|
||||
//{query: `SELECT * FROM mytable mt INNER JOIN othertable ot ON mt.i = ot.i2 AND mt.i > 2`},
|
||||
{query: `SELECT i AS foo FROM mytable ORDER BY i DESC`},
|
||||
{query: `SELECT CONCAT("a", "b", "c")`},
|
||||
{query: `SELECT COALESCE(NULL, NULL, NULL, 'example', NULL, 1234567890)`},
|
||||
{query: `SELECT COALESCE(NULL, NULL, NULL, COALESCE(NULL, 1234567890))`},
|
||||
{query: "SELECT concat(s, i) FROM mytable"},
|
||||
{query: "SELECT version()"},
|
||||
{query: `SELECT RAND(100)`},
|
||||
{query: `SELECT RAND(100) = RAND(100)`},
|
||||
{query: `SELECT RAND() = RAND()`},
|
||||
{query: `SELECT s FROM mytable WHERE s LIKE '%d row'`},
|
||||
{query: `SELECT s FROM mytable WHERE s NOT LIKE '%d row'`},
|
||||
// todo: multi-db queries
|
||||
//{query: `SELECT * FROM foo.other_table`}, // error: "database not found: foo"
|
||||
{query: "SELECT i FROM mytable WHERE NULL > 10;"},
|
||||
{query: "SELECT i FROM mytable WHERE NULL IN (10);"},
|
||||
{query: "SELECT i FROM mytable WHERE NULL IN (NULL, NULL);"},
|
||||
{query: "SELECT i FROM mytable WHERE NOT NULL NOT IN (NULL);"},
|
||||
{query: "SELECT i FROM mytable WHERE NOT (NULL) <> 10;"},
|
||||
{query: "SELECT i FROM mytable WHERE NOT NULL <> NULL;"},
|
||||
{query: "SELECT substring(s, 1, 1) FROM mytable ORDER BY substring(s, 1, 1)"},
|
||||
{query: "SELECT left(s, 1) as l FROM mytable ORDER BY l"},
|
||||
{query: "SELECT left(s, 2) as l FROM mytable ORDER BY l"},
|
||||
{query: "SELECT left(s, 0) as l FROM mytable ORDER BY l"},
|
||||
{query: "SELECT left(s, NULL) as l FROM mytable ORDER BY l"},
|
||||
{query: "SELECT left(s, 100) as l FROM mytable ORDER BY l"},
|
||||
{query: "SELECT instr(s, 'row') as l FROM mytable ORDER BY i"},
|
||||
{query: "SELECT instr(s, 'first') as l FROM mytable ORDER BY i"},
|
||||
{query: "SELECT instr(s, 'o') as l FROM mytable ORDER BY i"},
|
||||
{query: "SELECT instr(s, NULL) as l FROM mytable ORDER BY l"},
|
||||
{query: `SELECT i AS i FROM mytable ORDER BY i`},
|
||||
{query: `SELECT i AS foo FROM mytable ORDER BY mytable.i`},
|
||||
{query: "SELECT i, i2, s2 FROM mytable LEFT JOIN othertable ON i = i2 - 1"},
|
||||
{query: "SELECT i, i2, s2 FROM mytable RIGHT JOIN othertable ON i = i2 - 1"},
|
||||
{query: "SELECT i, i2, s2 FROM mytable LEFT OUTER JOIN othertable ON i = i2 - 1"},
|
||||
{query: "SELECT i, i2, s2 FROM mytable RIGHT OUTER JOIN othertable ON i = i2 - 1"},
|
||||
{query: "SELECT i FROM mytable WHERE NOT s ORDER BY 1 DESC"},
|
||||
{query: "SELECT i FROM mytable WHERE NOT(NOT i) ORDER BY 1 DESC"},
|
||||
{query: `SELECT * FROM mytable WHERE NULL AND i = 3`},
|
||||
{query: `SELECT FIRST(i) FROM (SELECT i FROM mytable ORDER BY i) t`},
|
||||
{query: `SELECT LAST(i) FROM (SELECT i FROM mytable ORDER BY i) t`},
|
||||
{query: "SELECT * FROM newlinetable WHERE s LIKE '%text%'"},
|
||||
{query: `SELECT i FROM mytable WHERE i = (SELECT 1)`},
|
||||
{query: `SELECT i FROM mytable WHERE i IN (SELECT i FROM mytable)`},
|
||||
{query: `SELECT i FROM mytable WHERE i NOT IN (SELECT i FROM mytable ORDER BY i ASC LIMIT 2)`},
|
||||
{query: `SELECT (SELECT i FROM mytable ORDER BY i ASC LIMIT 1) AS x`},
|
||||
{query: `SELECT DISTINCT n FROM bigtable ORDER BY t`},
|
||||
{query: "SELECT pk,pk1,pk2 FROM one_pk, two_pk ORDER BY 1,2,3"},
|
||||
{query: "SELECT t1.c1,t2.c2 FROM one_pk t1, two_pk t2 WHERE pk1=1 AND pk2=1 ORDER BY 1,2"},
|
||||
{query: "SELECT t1.c1,t2.c2 FROM one_pk t1, two_pk t2 WHERE t2.pk1=1 AND t2.pk2=1 ORDER BY 1,2"},
|
||||
{query: "SELECT t1.c1,t2.c2 FROM one_pk t1, two_pk t2 WHERE pk1=1 OR pk2=1 ORDER BY 1,2"},
|
||||
{query: "SELECT pk,pk2 FROM one_pk t1, two_pk t2 WHERE pk=1 AND pk2=1 ORDER BY 1,2"},
|
||||
{query: "SELECT pk,pk1,pk2 FROM one_pk,two_pk WHERE pk=0 AND pk1=0 OR pk2=1 ORDER BY 1,2,3"},
|
||||
{query: "SELECT pk,pk1,pk2 FROM one_pk,two_pk WHERE one_pk.c1=two_pk.c1 ORDER BY 1,2,3"},
|
||||
{query: "SELECT one_pk.c5,pk1,pk2 FROM one_pk,two_pk WHERE pk=pk1 ORDER BY 1,2,3"},
|
||||
{query: "SELECT opk.c5,pk1,pk2 FROM one_pk opk, two_pk tpk WHERE pk=pk1 ORDER BY 1,2,3"},
|
||||
{query: "SELECT one_pk.c5,pk1,pk2 FROM one_pk JOIN two_pk ON pk=pk1 ORDER BY 1,2,3"},
|
||||
{query: "SELECT opk.c5,pk1,pk2 FROM one_pk opk JOIN two_pk tpk ON pk=pk1 ORDER BY 1,2,3"},
|
||||
{query: "SELECT opk.c5,pk1,pk2 FROM one_pk opk JOIN two_pk tpk ON opk.pk=tpk.pk1 ORDER BY 1,2,3"},
|
||||
{query: "SELECT pk,pk1,pk2 FROM one_pk JOIN two_pk ON one_pk.c1=two_pk.c1 WHERE pk=1 ORDER BY 1,2,3"},
|
||||
{query: "SELECT pk,pk1,pk2 FROM one_pk JOIN two_pk ON one_pk.pk=two_pk.pk1 AND one_pk.pk=two_pk.pk2 ORDER BY 1,2,3"},
|
||||
{query: "SELECT pk,pk1,pk2 FROM one_pk opk JOIN two_pk tpk ON opk.pk=tpk.pk1 AND opk.pk=tpk.pk2 ORDER BY 1,2,3"},
|
||||
{query: "SELECT pk,pk1,pk2 FROM one_pk opk JOIN two_pk tpk ON pk=tpk.pk1 AND pk=tpk.pk2 ORDER BY 1,2,3"},
|
||||
{query: "SELECT pk,pk1,pk2 FROM one_pk LEFT JOIN two_pk ON one_pk.pk=two_pk.pk1 AND one_pk.pk=two_pk.pk2 ORDER BY 1,2,3"},
|
||||
{query: "SELECT pk,pk1,pk2 FROM one_pk RIGHT JOIN two_pk ON one_pk.pk=two_pk.pk1 AND one_pk.pk=two_pk.pk2 ORDER BY 1,2,3"},
|
||||
{query: "SELECT i,pk1,pk2 FROM mytable JOIN two_pk ON i-1=pk1 AND i-2=pk2 ORDER BY 1,2,3"},
|
||||
{query: "SELECT a.pk1,a.pk2,b.pk1,b.pk2 FROM two_pk a JOIN two_pk b ON a.pk1=b.pk2 AND a.pk2=b.pk1 ORDER BY 1,2,3"},
|
||||
{query: "SELECT a.pk1,a.pk2,b.pk1,b.pk2 FROM two_pk a JOIN two_pk b ON a.pk1=b.pk1 AND a.pk2=b.pk2 ORDER BY 1,2,3"},
|
||||
{query: "SELECT a.pk1,a.pk2,b.pk1,b.pk2 FROM two_pk a, two_pk b WHERE a.pk1=b.pk1 AND a.pk2=b.pk2 ORDER BY 1,2,3"},
|
||||
{query: "SELECT a.pk1,a.pk2,b.pk1,b.pk2 FROM two_pk a JOIN two_pk b ON b.pk1=a.pk1 AND a.pk2=b.pk2 ORDER BY 1,2,3"},
|
||||
{query: "SELECT a.pk1,a.pk2,b.pk1,b.pk2 FROM two_pk a JOIN two_pk b ON a.pk1+1=b.pk1 AND a.pk2+1=b.pk2 ORDER BY 1,2,3"},
|
||||
{query: "SELECT pk,pk1,pk2 FROM one_pk LEFT JOIN two_pk ON pk=pk1 ORDER BY 1,2,3"},
|
||||
{query: "SELECT pk,i2,f FROM one_pk LEFT JOIN niltable ON pk=i2 ORDER BY 1"},
|
||||
{query: "SELECT pk,i2,f FROM one_pk RIGHT JOIN niltable ON pk=i2 ORDER BY 2,3"},
|
||||
{query: "SELECT pk,i2,f FROM one_pk LEFT JOIN niltable ON pk=i2 AND f IS NOT NULL ORDER BY 1"}, // AND clause causes right table join mis},
|
||||
{query: "SELECT pk,i2,f FROM one_pk RIGHT JOIN niltable ON pk=i2 and pk > 0 ORDER BY 2,3"}, // > 0 clause in join condition is ignore},
|
||||
{query: "SELECT pk,i2,f FROM one_pk LEFT JOIN niltable ON pk=i WHERE i2 IS NOT NULL ORDER BY 1"},
|
||||
{query: "SELECT pk,i2,f FROM one_pk RIGHT JOIN niltable ON pk=i WHERE f IS NOT NULL ORDER BY 2,3"},
|
||||
{query: "SELECT pk,i2,f FROM one_pk LEFT JOIN niltable ON pk=i2 WHERE pk > 1 ORDER BY 1"},
|
||||
{query: "SELECT pk,i2,f FROM one_pk RIGHT JOIN niltable ON pk=i2 WHERE pk > 0 ORDER BY 2,3"},
|
||||
{query: "SELECT GREATEST(CAST(i AS CHAR), CAST(b AS CHAR)) FROM niltable order by i"},
|
||||
{query: "SELECT pk,pk1,pk2,one_pk.c1 AS foo, two_pk.c1 AS bar FROM one_pk JOIN two_pk ON one_pk.c1=two_pk.c1 ORDER BY 1,2,3"},
|
||||
{query: "SELECT pk,pk1,pk2,one_pk.c1 AS foo,two_pk.c1 AS bar FROM one_pk JOIN two_pk ON one_pk.c1=two_pk.c1 WHERE one_pk.c1=10"},
|
||||
{query: "SELECT pk,pk1,pk2 FROM one_pk JOIN two_pk ON pk1-pk>0 AND pk2<1"},
|
||||
{query: "SELECT pk,pk1,pk2 FROM one_pk JOIN two_pk ORDER BY 1,2,3"},
|
||||
{query: "SELECT a.pk,b.pk FROM one_pk a JOIN one_pk b ON a.pk = b.pk order by a.pk"},
|
||||
{query: "SELECT a.pk,b.pk FROM one_pk a, one_pk b WHERE a.pk = b.pk order by a.pk"},
|
||||
{query: "SELECT one_pk.pk,b.pk FROM one_pk JOIN one_pk b ON one_pk.pk = b.pk order by one_pk.pk"},
|
||||
{query: "SELECT one_pk.pk,b.pk FROM one_pk, one_pk b WHERE one_pk.pk = b.pk order by one_pk.pk"},
|
||||
{query: "SELECT (CASE WHEN i THEN i ELSE 0 END) as cases_i from mytable"},
|
||||
var engineQueryTests = [][]enginetest.QueryTest{
|
||||
enginetest.QueryTests,
|
||||
enginetest.ViewTests,
|
||||
}
|
||||
|
||||
var engineTestUnionQueries = []engineTestQuery{
|
||||
{query: "SELECT i FROM mytable UNION SELECT i+10 FROM mytable;"},
|
||||
{query: "SELECT i FROM mytable UNION DISTINCT SELECT i+10 FROM mytable;"},
|
||||
{query: "SELECT i FROM mytable UNION SELECT i FROM mytable;"},
|
||||
{query: "SELECT i FROM mytable UNION DISTINCT SELECT i FROM mytable;"},
|
||||
{query: "SELECT i FROM mytable UNION SELECT s FROM mytable;"},
|
||||
var engineTestSkipSet = []string{
|
||||
// query diff doesn't handle mutlidb queries
|
||||
`SELECT * FROM foo.other_table`,
|
||||
|
||||
"SELECT i FROM mytable WHERE i > 2;",
|
||||
"SELECT i FROM mytable WHERE 2 < i;",
|
||||
"SELECT i FROM mytable WHERE i < 2;",
|
||||
"SELECT i FROM mytable WHERE 2 > i;",
|
||||
|
||||
"SELECT i FROM mytable WHERE i >= 2 ORDER BY 1",
|
||||
"SELECT i FROM mytable WHERE 2 <= i ORDER BY 1",
|
||||
"SELECT i FROM mytable WHERE i <= 2 ORDER BY 1",
|
||||
"SELECT i FROM mytable WHERE 2 >= i ORDER BY 1",
|
||||
"SELECT i FROM mytable WHERE i > 2",
|
||||
"SELECT i FROM mytable WHERE i < 2",
|
||||
"SELECT i FROM mytable WHERE i >= 2 OR i = 1 ORDER BY 1",
|
||||
}
|
||||
|
||||
var engineTestAggregateQueries = []engineTestQuery{
|
||||
{query: "SELECT * FROM mytable GROUP BY i,s;"},
|
||||
{query: "SELECT COUNT(*) FROM mytable;"},
|
||||
{query: "SELECT COUNT(*) FROM mytable LIMIT 1;"},
|
||||
{query: "SELECT COUNT(*) AS c FROM mytable;"},
|
||||
{query: `
|
||||
SELECT COUNT(*) AS cnt, fi FROM (
|
||||
SELECT tbl.s AS fi
|
||||
FROM mytable tbl
|
||||
) t
|
||||
GROUP BY fi`,
|
||||
},
|
||||
{query: `
|
||||
SELECT fi, COUNT(*) FROM (
|
||||
SELECT tbl.s AS fi
|
||||
FROM mytable tbl
|
||||
) t
|
||||
GROUP BY fi
|
||||
ORDER BY COUNT(*) ASC`,
|
||||
},
|
||||
{query: `
|
||||
SELECT COUNT(*), fi FROM (
|
||||
SELECT tbl.s AS fi
|
||||
FROM mytable tbl
|
||||
) t
|
||||
GROUP BY fi
|
||||
ORDER BY COUNT(*) ASC`,
|
||||
},
|
||||
{query: `
|
||||
SELECT COUNT(*) AS cnt, fi FROM (
|
||||
SELECT tbl.s AS fi
|
||||
FROM mytable tbl
|
||||
) t
|
||||
GROUP BY 2`,
|
||||
},
|
||||
{query: `SELECT COUNT(*) AS cnt, s AS fi FROM mytable GROUP BY fi`},
|
||||
{query: `SELECT COUNT(*) AS cnt, s AS fi FROM mytable GROUP BY 2`},
|
||||
{query: `SELECT COUNT(*) c, i AS foo FROM mytable GROUP BY i ORDER BY i DESC`},
|
||||
{query: `SELECT COUNT(*) c, i AS foo FROM mytable GROUP BY 2 ORDER BY 2 DESC`},
|
||||
{query: `SELECT COUNT(*) c, i AS foo FROM mytable GROUP BY i ORDER BY foo DESC`},
|
||||
{query: `SELECT COUNT(*) c, i AS foo FROM mytable GROUP BY 2 ORDER BY foo DESC`},
|
||||
{query: `SELECT COUNT(*) c, i AS i FROM mytable GROUP BY 2`},
|
||||
{query: `SELECT i AS i FROM mytable GROUP BY 1`},
|
||||
{query: "SELECT SUM(i) + 1, i FROM mytable GROUP BY i ORDER BY i"},
|
||||
{query: "SELECT SUM(i), i FROM mytable GROUP BY i ORDER BY 1+SUM(i) ASC"},
|
||||
{query: "SELECT i, SUM(i) FROM mytable GROUP BY i ORDER BY SUM(i) DESC"},
|
||||
{query: `SELECT AVG(23.222000)`},
|
||||
{query: "SELECT substring(s, 1, 1), count(*) FROM mytable GROUP BY substring(s, 1, 1)"},
|
||||
{query: `
|
||||
SELECT
|
||||
i,
|
||||
foo
|
||||
FROM (
|
||||
SELECT
|
||||
i,
|
||||
COUNT(s) AS foo
|
||||
FROM mytable
|
||||
GROUP BY i
|
||||
) AS q
|
||||
ORDER BY foo DESC
|
||||
`,
|
||||
},
|
||||
{query: "SELECT n, COUNT(n) FROM bigtable GROUP BY n HAVING COUNT(n) > 2"},
|
||||
{query: "SELECT n, MAX(n) FROM bigtable GROUP BY n HAVING COUNT(n) > 2"},
|
||||
{query: "SELECT substring(mytable.s, 1, 5) AS s FROM mytable INNER JOIN othertable ON (substring(mytable.s, 1, 5) = SUBSTRING(othertable.s2, 1, 5)) GROUP BY 1 HAVING s = \"secon\""},
|
||||
{query: "SELECT s, i FROM mytable GROUP BY i ORDER BY SUBSTRING(s, 1, 1) DESC"},
|
||||
{query: "SELECT s, i FROM mytable GROUP BY i HAVING count(*) > 0 ORDER BY SUBSTRING(s, 1, 1) DESC"},
|
||||
{query: `SELECT t.date_col FROM (SELECT CONVERT('2019-06-06 00:00:00', DATETIME) as date_col) t GROUP BY t.date_col`},
|
||||
{query: "SELECT i, COUNT(i) AS `COUNT(i)` FROM (SELECT i FROM mytable) t GROUP BY i ORDER BY i, `COUNT(i)` DESC"},
|
||||
{query: `SELECT 1 FROM mytable GROUP BY i HAVING i > 1`},
|
||||
{query: `SELECT avg(i) FROM mytable GROUP BY i HAVING avg(i) > 1`},
|
||||
{query: `
|
||||
SELECT s AS s, COUNT(*) AS count, AVG(i) AS ` + "`AVG(i)`" + `
|
||||
FROM (
|
||||
SELECT * FROM mytable
|
||||
) AS expr_qry
|
||||
GROUP BY s
|
||||
HAVING ((AVG(i) > 0))
|
||||
ORDER BY count DESC
|
||||
LIMIT 10000`,
|
||||
},
|
||||
{query: `
|
||||
SELECT
|
||||
table_schema,
|
||||
table_name,
|
||||
CASE
|
||||
WHEN table_type = 'BASE TABLE' THEN
|
||||
CASE
|
||||
WHEN table_schema = 'mysql'
|
||||
OR table_schema = 'performance_schema' THEN 'SYSTEM TABLE'
|
||||
ELSE 'TABLE'
|
||||
END
|
||||
WHEN table_type = 'TEMPORARY' THEN 'LOCAL_TEMPORARY'
|
||||
ELSE table_type
|
||||
END AS TABLE_TYPE
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = 'mydb'
|
||||
AND table_name = 'mytable'
|
||||
HAVING table_type IN ('TABLE', 'VIEW')
|
||||
ORDER BY table_type, table_schema, table_name`,
|
||||
},
|
||||
{query: "SELECT substring(mytable.s, 1, 5) AS s FROM mytable INNER JOIN othertable ON (substring(mytable.s, 1, 5) = SUBSTRING(othertable.s2, 1, 5)) GROUP BY 1"},
|
||||
{query: `SELECT SUBSTRING_INDEX(mytable.s, "d", 1) AS s FROM mytable INNER JOIN othertable ON (SUBSTRING_INDEX(mytable.s, "d", 1) = SUBSTRING_INDEX(othertable.s2, "d", 1)) GROUP BY 1 HAVING s = 'secon'`},
|
||||
{query: `SELECT SUBSTRING(s, -3, 3) AS s FROM mytable WHERE s LIKE '%d row' GROUP BY 1`},
|
||||
{query: `SELECT SUM(i) FROM mytable`},
|
||||
}
|
||||
|
||||
// runs a subset of SELECT queries from enginetest/queries.go as a sanity check
|
||||
func engineTestQueryDiffer(t *testing.T, test engineTestQuery, dEnv *env.DoltEnv) {
|
||||
ctx := context.Background()
|
||||
fromRoot, err := dEnv.HeadRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
toRoot, err := dEnv.WorkingRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
qd, err := querydiff.MakeQueryDiffer(ctx, dEnv, fromRoot, toRoot, test.query)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error creating QueryDiffer: %s", err.Error())
|
||||
func skipEngineTest(test enginetest.QueryTest) bool {
|
||||
h := det.DoltHarness{}
|
||||
if h.SkipQueryTest(test.Query) {
|
||||
return true
|
||||
}
|
||||
|
||||
qd.Start()
|
||||
from, to, err := qd.NextDiff()
|
||||
assert.Nil(t, from)
|
||||
assert.Nil(t, to)
|
||||
assert.Equal(t, io.EOF, err)
|
||||
lowerQuery := strings.ToLower(test.Query)
|
||||
|
||||
if strings.Contains(lowerQuery, "myview1") {
|
||||
// todo: support for history table
|
||||
return true
|
||||
}
|
||||
|
||||
for _, q := range engineTestSkipSet {
|
||||
if lowerQuery == strings.ToLower(q) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func TestEngineTestQueryDifferBefore(t *testing.T) {
|
||||
inner := func(t *testing.T, test enginetest.QueryTest, dEnv *env.DoltEnv) {
|
||||
ctx := context.Background()
|
||||
fromRoot, err := dEnv.StagedRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
toRoot, err := dEnv.WorkingRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
qd, err := querydiff.MakeQueryDiffer(ctx, dEnv, fromRoot, toRoot, test.Query)
|
||||
if err != nil {
|
||||
if _, ok := err.(querydiff.QueryDiffError); ok {
|
||||
return
|
||||
}
|
||||
t.Fatalf("unexpected error creating QueryDiffer: %s", err.Error())
|
||||
}
|
||||
|
||||
qd.Start()
|
||||
var actual []sql.Row
|
||||
for {
|
||||
_, to, err := qd.NextDiff()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
actual = append(actual, to)
|
||||
}
|
||||
actual = sortExpectedResults(qd.Schema(), actual)
|
||||
test.Expected = sortExpectedResults(qd.Schema(), test.Expected)
|
||||
require.Equal(t, len(test.Expected), len(actual))
|
||||
|
||||
for i, exp := range test.Expected {
|
||||
act := actual[i]
|
||||
exp, act = enginetest.WidenRow(exp), enginetest.WidenRow(act)
|
||||
assert.Equal(t, exp, act)
|
||||
}
|
||||
}
|
||||
|
||||
// engineTestQueries are read-only, sharing a dEnv speeds up tests
|
||||
dEnv := setupEngineTests(t)
|
||||
for _, testSet := range engineQueryTests {
|
||||
for _, test := range testSet {
|
||||
if skipEngineTest(test) {
|
||||
continue
|
||||
}
|
||||
t.Run(test.Query, func(t *testing.T) {
|
||||
inner(t, test, dEnv)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func EngineTestQueryDifferAfter(t *testing.T) {
|
||||
inner := func(t *testing.T, test enginetest.QueryTest, dEnv *env.DoltEnv) {
|
||||
ctx := context.Background()
|
||||
fromRoot, err := dEnv.StagedRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
toRoot, err := dEnv.WorkingRoot(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
qd, err := querydiff.MakeQueryDiffer(ctx, dEnv, fromRoot, toRoot, test.Query)
|
||||
if err != nil {
|
||||
if _, ok := err.(querydiff.QueryDiffError); ok {
|
||||
return
|
||||
}
|
||||
t.Fatalf("unexpected error creating QueryDiffer: %s", err.Error())
|
||||
}
|
||||
|
||||
qd.Start()
|
||||
from, to, err := qd.NextDiff()
|
||||
assert.Nil(t, from)
|
||||
assert.Nil(t, to)
|
||||
assert.Equal(t, io.EOF, err)
|
||||
}
|
||||
|
||||
// engineTestQueries are read-only, sharing a dEnv speeds up tests
|
||||
dEnv := setupEngineTests(t)
|
||||
commitData(t, dEnv)
|
||||
for _, testSet := range engineQueryTests {
|
||||
for _, test := range testSet {
|
||||
if skipEngineTest(test) {
|
||||
continue
|
||||
}
|
||||
t.Run(test.Query, func(t *testing.T) {
|
||||
inner(t, test, dEnv)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func sortExpectedResults(sch sql.Schema, rows []sql.Row) []sql.Row {
|
||||
order := make([]plan.SortField, len(sch))
|
||||
for i, col := range sch {
|
||||
order[i] = plan.SortField{
|
||||
Column: expression.NewGetField(i, col.Type, col.Name, col.Nullable),
|
||||
}
|
||||
}
|
||||
s := &plan.Sorter{
|
||||
SortFields: order,
|
||||
Rows: rows,
|
||||
Ctx: sql.NewContext(context.Background()),
|
||||
}
|
||||
sort.Stable(s)
|
||||
return s.Rows
|
||||
}
|
||||
|
||||
@@ -29,21 +29,21 @@ import (
|
||||
"github.com/liquidata-inc/dolt/go/libraries/doltcore/sqle/dfunctions"
|
||||
)
|
||||
|
||||
type doltHarness struct {
|
||||
type DoltHarness struct {
|
||||
t *testing.T
|
||||
session *sqle.DoltSession
|
||||
mrEnv env.MultiRepoEnv
|
||||
}
|
||||
|
||||
var _ enginetest.Harness = (*doltHarness)(nil)
|
||||
var _ enginetest.SkippingHarness = (*doltHarness)(nil)
|
||||
var _ enginetest.IndexHarness = (*doltHarness)(nil)
|
||||
var _ enginetest.VersionedDBHarness = (*doltHarness)(nil)
|
||||
var _ enginetest.Harness = (*DoltHarness)(nil)
|
||||
var _ enginetest.SkippingHarness = (*DoltHarness)(nil)
|
||||
var _ enginetest.IndexHarness = (*DoltHarness)(nil)
|
||||
var _ enginetest.VersionedDBHarness = (*DoltHarness)(nil)
|
||||
|
||||
func newDoltHarness(t *testing.T) *doltHarness {
|
||||
func newDoltHarness(t *testing.T) *DoltHarness {
|
||||
session, err := sqle.NewDoltSession(context.Background(), enginetest.NewBaseSession(), "test", "email@test.com")
|
||||
require.NoError(t, err)
|
||||
return &doltHarness{
|
||||
return &DoltHarness{
|
||||
t: t,
|
||||
session: session,
|
||||
mrEnv: make(env.MultiRepoEnv),
|
||||
@@ -51,7 +51,7 @@ func newDoltHarness(t *testing.T) *doltHarness {
|
||||
}
|
||||
|
||||
// Logic to skip unsupported queries
|
||||
func (d *doltHarness) SkipQueryTest(query string) bool {
|
||||
func (d *DoltHarness) SkipQueryTest(query string) bool {
|
||||
lowerQuery := strings.ToLower(query)
|
||||
return strings.Contains(lowerQuery, "typestable") || // we don't support all the required types
|
||||
strings.Contains(lowerQuery, "show full columns") || // we set extra comment info
|
||||
@@ -59,11 +59,11 @@ func (d *doltHarness) SkipQueryTest(query string) bool {
|
||||
strings.Contains(lowerQuery, "show create table") // we set extra comment info
|
||||
}
|
||||
|
||||
func (d *doltHarness) Parallelism() int {
|
||||
func (d *DoltHarness) Parallelism() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
func (d *doltHarness) NewContext() *sql.Context {
|
||||
func (d *DoltHarness) NewContext() *sql.Context {
|
||||
return sql.NewContext(
|
||||
context.Background(),
|
||||
sql.WithSession(d.session),
|
||||
@@ -71,11 +71,11 @@ func (d *doltHarness) NewContext() *sql.Context {
|
||||
)
|
||||
}
|
||||
|
||||
func (d *doltHarness) SupportsNativeIndexCreation() bool {
|
||||
func (d *DoltHarness) SupportsNativeIndexCreation() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (d *doltHarness) NewDatabase(name string) sql.Database {
|
||||
func (d *DoltHarness) NewDatabase(name string) sql.Database {
|
||||
dEnv := dtestutils.CreateTestEnv()
|
||||
root, err := dEnv.WorkingRoot(enginetest.NewContext(d))
|
||||
require.NoError(d.t, err)
|
||||
@@ -87,7 +87,7 @@ func (d *doltHarness) NewDatabase(name string) sql.Database {
|
||||
return db
|
||||
}
|
||||
|
||||
func (d *doltHarness) NewTable(db sql.Database, name string, schema sql.Schema) (sql.Table, error) {
|
||||
func (d *DoltHarness) NewTable(db sql.Database, name string, schema sql.Schema) (sql.Table, error) {
|
||||
doltDatabase := db.(sqle.Database)
|
||||
err := doltDatabase.CreateTable(enginetest.NewContext(d).WithCurrentDB(db.Name()), name, schema)
|
||||
if err != nil {
|
||||
@@ -103,7 +103,7 @@ func (d *doltHarness) NewTable(db sql.Database, name string, schema sql.Schema)
|
||||
|
||||
// Dolt doesn't version tables per se, just the entire database. So ignore the name and schema and just create a new
|
||||
// branch with the given name.
|
||||
func (d *doltHarness) NewTableAsOf(db sql.VersionedDatabase, name string, schema sql.Schema, asOf interface{}) sql.Table {
|
||||
func (d *DoltHarness) NewTableAsOf(db sql.VersionedDatabase, name string, schema sql.Schema, asOf interface{}) sql.Table {
|
||||
table, err := d.NewTable(db, name, schema)
|
||||
if err != nil {
|
||||
require.True(d.t, sql.ErrTableAlreadyExists.Is(err))
|
||||
@@ -118,7 +118,7 @@ func (d *doltHarness) NewTableAsOf(db sql.VersionedDatabase, name string, schema
|
||||
|
||||
// Dolt doesn't version tables per se, just the entire database. So ignore the name and schema and just create a new
|
||||
// branch with the given name.
|
||||
func (d *doltHarness) SnapshotTable(db sql.VersionedDatabase, name string, asOf interface{}) error {
|
||||
func (d *DoltHarness) SnapshotTable(db sql.VersionedDatabase, name string, asOf interface{}) error {
|
||||
ddb := db.(sqle.Database)
|
||||
e := enginetest.NewEngineWithDbs(d.t, d, []sql.Database{db}, nil)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user