зеркало из https://github.com/github/vitess-gh.git
1211 строки
38 KiB
Go
1211 строки
38 KiB
Go
// Copyright 2015, Google Inc. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package tabletserver
|
|
|
|
import (
|
|
"fmt"
|
|
"math/rand"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/youtube/vitess/go/mysql"
|
|
"github.com/youtube/vitess/go/sqldb"
|
|
"github.com/youtube/vitess/go/sqltypes"
|
|
"github.com/youtube/vitess/go/vt/callerid"
|
|
"github.com/youtube/vitess/go/vt/callinfo"
|
|
topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
|
|
"github.com/youtube/vitess/go/vt/tableacl"
|
|
"github.com/youtube/vitess/go/vt/tableacl/simpleacl"
|
|
"github.com/youtube/vitess/go/vt/tabletserver/fakecacheservice"
|
|
"github.com/youtube/vitess/go/vt/tabletserver/planbuilder"
|
|
"github.com/youtube/vitess/go/vt/tabletserver/proto"
|
|
"github.com/youtube/vitess/go/vt/vttest/fakesqldb"
|
|
"golang.org/x/net/context"
|
|
|
|
querypb "github.com/youtube/vitess/go/vt/proto/query"
|
|
tableaclpb "github.com/youtube/vitess/go/vt/proto/tableacl"
|
|
)
|
|
|
|
func TestQueryExecutorPlanDDL(t *testing.T) {
|
|
db := setUpQueryExecutorTest()
|
|
query := "alter table test_table add zipcode int"
|
|
want := &sqltypes.Result{
|
|
Rows: [][]sqltypes.Value{},
|
|
}
|
|
db.AddQuery(query, want)
|
|
ctx := context.Background()
|
|
tsv := newTestTabletServer(ctx, enableRowCache|enableStrict, db)
|
|
qre := newTestQueryExecutor(ctx, tsv, query, 0)
|
|
defer tsv.StopService()
|
|
checkPlanID(t, planbuilder.PlanDDL, qre.plan.PlanID)
|
|
got, err := qre.Execute()
|
|
if err != nil {
|
|
t.Fatalf("qre.Execute() = %v, want nil", err)
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("got: %v, want: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestQueryExecutorPlanPassDmlStrictMode(t *testing.T) {
|
|
db := setUpQueryExecutorTest()
|
|
query := "update test_table set pk = foo()"
|
|
want := &sqltypes.Result{
|
|
Rows: [][]sqltypes.Value{},
|
|
}
|
|
db.AddQuery(query, want)
|
|
ctx := context.Background()
|
|
// non strict mode
|
|
tsv := newTestTabletServer(ctx, noFlags, db)
|
|
qre := newTestQueryExecutor(ctx, tsv, query, newTransaction(tsv))
|
|
checkPlanID(t, planbuilder.PlanPassDML, qre.plan.PlanID)
|
|
got, err := qre.Execute()
|
|
if err != nil {
|
|
t.Fatalf("qre.Execute() = %v, want nil", err)
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("got: %v, want: %v", got, want)
|
|
}
|
|
testCommitHelper(t, tsv, qre)
|
|
tsv.StopService()
|
|
|
|
// strict mode
|
|
tsv = newTestTabletServer(ctx, enableRowCache|enableStrict, db)
|
|
qre = newTestQueryExecutor(ctx, tsv, query, newTransaction(tsv))
|
|
defer tsv.StopService()
|
|
defer testCommitHelper(t, tsv, qre)
|
|
checkPlanID(t, planbuilder.PlanPassDML, qre.plan.PlanID)
|
|
got, err = qre.Execute()
|
|
if err == nil {
|
|
t.Fatal("qre.Execute() = nil, want error")
|
|
}
|
|
tabletError, ok := err.(*TabletError)
|
|
if !ok {
|
|
t.Fatalf("got: %v, want: a TabletError", tabletError)
|
|
}
|
|
if tabletError.ErrorType != ErrFail {
|
|
t.Fatalf("got: %s, want: ErrFail", getTabletErrorString(ErrFail))
|
|
}
|
|
}
|
|
|
|
func TestQueryExecutorPlanPassDmlStrictModeAutoCommit(t *testing.T) {
|
|
db := setUpQueryExecutorTest()
|
|
query := "update test_table set pk = foo()"
|
|
want := &sqltypes.Result{
|
|
Rows: [][]sqltypes.Value{},
|
|
}
|
|
db.AddQuery(query, want)
|
|
// non strict mode
|
|
ctx := context.Background()
|
|
tsv := newTestTabletServer(ctx, noFlags, db)
|
|
qre := newTestQueryExecutor(ctx, tsv, query, 0)
|
|
checkPlanID(t, planbuilder.PlanPassDML, qre.plan.PlanID)
|
|
got, err := qre.Execute()
|
|
if err != nil {
|
|
t.Fatalf("qre.Execute() = %v, want nil", err)
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("got: %v, want: %v", got, want)
|
|
}
|
|
tsv.StopService()
|
|
|
|
// strict mode
|
|
// update should fail because strict mode is not enabled
|
|
tsv = newTestTabletServer(ctx, enableRowCache|enableStrict, db)
|
|
qre = newTestQueryExecutor(ctx, tsv, query, 0)
|
|
defer tsv.StopService()
|
|
checkPlanID(t, planbuilder.PlanPassDML, qre.plan.PlanID)
|
|
_, err = qre.Execute()
|
|
if err == nil {
|
|
t.Fatal("got: nil, want: error")
|
|
}
|
|
tabletError, ok := err.(*TabletError)
|
|
if !ok {
|
|
t.Fatalf("got: %v, want: *TabletError", tabletError)
|
|
}
|
|
if tabletError.ErrorType != ErrFail {
|
|
t.Fatalf("got: %s, want: ErrFail", getTabletErrorString(ErrFail))
|
|
}
|
|
}
|
|
|
|
func TestQueryExecutorPlanInsertPk(t *testing.T) {
|
|
db := setUpQueryExecutorTest()
|
|
db.AddQuery("insert into test_table values (1) /* _stream test_table (pk ) (1 ); */", &sqltypes.Result{})
|
|
want := &sqltypes.Result{
|
|
Rows: make([][]sqltypes.Value, 0),
|
|
}
|
|
query := "insert into test_table values(1)"
|
|
ctx := context.Background()
|
|
tsv := newTestTabletServer(ctx, enableRowCache|enableStrict, db)
|
|
qre := newTestQueryExecutor(ctx, tsv, query, 0)
|
|
defer tsv.StopService()
|
|
checkPlanID(t, planbuilder.PlanInsertPK, qre.plan.PlanID)
|
|
got, err := qre.Execute()
|
|
if err != nil {
|
|
t.Fatalf("qre.Execute() = %v, want nil", err)
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("got: %v, want: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestQueryExecutorPlanInsertSubQueryAutoCommmit(t *testing.T) {
|
|
db := setUpQueryExecutorTest()
|
|
query := "insert into test_table(pk) select pk from test_table where pk = 1 limit 1000"
|
|
want := &sqltypes.Result{
|
|
Rows: [][]sqltypes.Value{},
|
|
}
|
|
db.AddQuery(query, want)
|
|
selectQuery := "select pk from test_table where pk = 1 limit 1000"
|
|
db.AddQuery(selectQuery, &sqltypes.Result{
|
|
RowsAffected: 1,
|
|
Rows: [][]sqltypes.Value{
|
|
[]sqltypes.Value{sqltypes.MakeTrusted(sqltypes.Int32, []byte("2"))},
|
|
},
|
|
})
|
|
|
|
insertQuery := "insert into test_table(pk) values (2) /* _stream test_table (pk ) (2 ); */"
|
|
|
|
db.AddQuery(insertQuery, &sqltypes.Result{})
|
|
ctx := context.Background()
|
|
tsv := newTestTabletServer(ctx, enableRowCache|enableStrict, db)
|
|
qre := newTestQueryExecutor(ctx, tsv, query, 0)
|
|
defer tsv.StopService()
|
|
checkPlanID(t, planbuilder.PlanInsertSubquery, qre.plan.PlanID)
|
|
got, err := qre.Execute()
|
|
if err != nil {
|
|
t.Fatalf("qre.Execute() = %v, want nil", err)
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("got: %v, want: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestQueryExecutorPlanInsertSubQuery(t *testing.T) {
|
|
db := setUpQueryExecutorTest()
|
|
query := "insert into test_table(pk) select pk from test_table where pk = 1 limit 1000"
|
|
want := &sqltypes.Result{
|
|
Rows: [][]sqltypes.Value{},
|
|
}
|
|
db.AddQuery(query, want)
|
|
selectQuery := "select pk from test_table where pk = 1 limit 1000"
|
|
db.AddQuery(selectQuery, &sqltypes.Result{
|
|
RowsAffected: 1,
|
|
Rows: [][]sqltypes.Value{
|
|
[]sqltypes.Value{sqltypes.MakeTrusted(sqltypes.Int32, []byte("2"))},
|
|
},
|
|
})
|
|
|
|
insertQuery := "insert into test_table(pk) values (2) /* _stream test_table (pk ) (2 ); */"
|
|
|
|
db.AddQuery(insertQuery, &sqltypes.Result{})
|
|
ctx := context.Background()
|
|
tsv := newTestTabletServer(ctx, enableRowCache|enableStrict, db)
|
|
qre := newTestQueryExecutor(ctx, tsv, query, newTransaction(tsv))
|
|
|
|
defer tsv.StopService()
|
|
defer testCommitHelper(t, tsv, qre)
|
|
checkPlanID(t, planbuilder.PlanInsertSubquery, qre.plan.PlanID)
|
|
got, err := qre.Execute()
|
|
if err != nil {
|
|
t.Fatalf("qre.Execute() = %v, want nil", err)
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("got: %v, want: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestQueryExecutorPlanUpsertPk(t *testing.T) {
|
|
db := setUpQueryExecutorTest()
|
|
db.AddQuery("insert into test_table values (1) /* _stream test_table (pk ) (1 ); */", &sqltypes.Result{})
|
|
want := &sqltypes.Result{
|
|
Rows: make([][]sqltypes.Value, 0),
|
|
}
|
|
query := "insert into test_table values(1) on duplicate key update val=1"
|
|
ctx := context.Background()
|
|
tsv := newTestTabletServer(ctx, enableRowCache|enableStrict, db)
|
|
qre := newTestQueryExecutor(ctx, tsv, query, 0)
|
|
defer tsv.StopService()
|
|
checkPlanID(t, planbuilder.PlanUpsertPK, qre.plan.PlanID)
|
|
got, err := qre.Execute()
|
|
if err != nil {
|
|
t.Fatalf("qre.Execute() = %v, want nil", err)
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("got: %v, want: %v", got, want)
|
|
}
|
|
|
|
db.AddRejectedQuery("insert into test_table values (1) /* _stream test_table (pk ) (1 ); */", errRejected)
|
|
_, err = qre.Execute()
|
|
wantErr := "error: rejected"
|
|
if err == nil || err.Error() != wantErr {
|
|
t.Fatalf("qre.Execute() = %v, want %v", err, wantErr)
|
|
}
|
|
|
|
db.AddRejectedQuery(
|
|
"insert into test_table values (1) /* _stream test_table (pk ) (1 ); */",
|
|
sqldb.NewSQLError(mysql.ErrDupEntry, "err"),
|
|
)
|
|
db.AddQuery("update test_table set val = 1 where pk in (1) /* _stream test_table (pk ) (1 ); */", &sqltypes.Result{})
|
|
_, err = qre.Execute()
|
|
wantErr = "error: err (errno 1062)"
|
|
if err == nil || err.Error() != wantErr {
|
|
t.Fatalf("qre.Execute() = %v, want %v", err, wantErr)
|
|
}
|
|
|
|
db.AddRejectedQuery(
|
|
"insert into test_table values (1) /* _stream test_table (pk ) (1 ); */",
|
|
sqldb.NewSQLError(mysql.ErrDupEntry, "ERROR 1062 (23000): Duplicate entry '2' for key 'PRIMARY'"),
|
|
)
|
|
db.AddQuery(
|
|
"update test_table set val = 1 where pk in (1) /* _stream test_table (pk ) (1 ); */",
|
|
&sqltypes.Result{RowsAffected: 1},
|
|
)
|
|
got, err = qre.Execute()
|
|
if err != nil {
|
|
t.Fatalf("qre.Execute() = %v, want nil", err)
|
|
}
|
|
want = &sqltypes.Result{
|
|
RowsAffected: 2,
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("got: %v, want: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestQueryExecutorPlanDmlPk(t *testing.T) {
|
|
db := setUpQueryExecutorTest()
|
|
query := "update test_table set name = 2 where pk in (1) /* _stream test_table (pk ) (1 ); */"
|
|
want := &sqltypes.Result{}
|
|
db.AddQuery(query, want)
|
|
ctx := context.Background()
|
|
tsv := newTestTabletServer(ctx, enableRowCache|enableStrict, db)
|
|
qre := newTestQueryExecutor(ctx, tsv, query, newTransaction(tsv))
|
|
defer tsv.StopService()
|
|
defer testCommitHelper(t, tsv, qre)
|
|
checkPlanID(t, planbuilder.PlanDMLPK, qre.plan.PlanID)
|
|
got, err := qre.Execute()
|
|
if err != nil {
|
|
t.Fatalf("qre.Execute() = %v, want nil", err)
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("got: %v, want: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestQueryExecutorPlanDmlAutoCommit(t *testing.T) {
|
|
db := setUpQueryExecutorTest()
|
|
query := "update test_table set name = 2 where pk in (1) /* _stream test_table (pk ) (1 ); */"
|
|
want := &sqltypes.Result{}
|
|
db.AddQuery(query, want)
|
|
ctx := context.Background()
|
|
tsv := newTestTabletServer(ctx, enableRowCache|enableStrict, db)
|
|
qre := newTestQueryExecutor(ctx, tsv, query, 0)
|
|
defer tsv.StopService()
|
|
checkPlanID(t, planbuilder.PlanDMLPK, qre.plan.PlanID)
|
|
got, err := qre.Execute()
|
|
if err != nil {
|
|
t.Fatalf("qre.Execute() = %v, want nil", err)
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("got: %v, want: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestQueryExecutorPlanDmlSubQuery(t *testing.T) {
|
|
db := setUpQueryExecutorTest()
|
|
query := "update test_table set addr = 3 where name = 1 limit 1000"
|
|
expandedQuery := "select pk from test_table where name = 1 limit 1000 for update"
|
|
want := &sqltypes.Result{}
|
|
db.AddQuery(query, want)
|
|
db.AddQuery(expandedQuery, want)
|
|
ctx := context.Background()
|
|
tsv := newTestTabletServer(ctx, enableRowCache|enableStrict, db)
|
|
qre := newTestQueryExecutor(ctx, tsv, query, newTransaction(tsv))
|
|
defer tsv.StopService()
|
|
defer testCommitHelper(t, tsv, qre)
|
|
checkPlanID(t, planbuilder.PlanDMLSubquery, qre.plan.PlanID)
|
|
got, err := qre.Execute()
|
|
if err != nil {
|
|
t.Fatalf("qre.Execute() = %v, want nil", err)
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("got: %v, want: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestQueryExecutorPlanDmlSubQueryAutoCommit(t *testing.T) {
|
|
db := setUpQueryExecutorTest()
|
|
query := "update test_table set addr = 3 where name = 1 limit 1000"
|
|
expandedQuery := "select pk from test_table where name = 1 limit 1000 for update"
|
|
want := &sqltypes.Result{}
|
|
db.AddQuery(query, want)
|
|
db.AddQuery(expandedQuery, want)
|
|
ctx := context.Background()
|
|
tsv := newTestTabletServer(ctx, enableRowCache|enableStrict, db)
|
|
qre := newTestQueryExecutor(ctx, tsv, query, 0)
|
|
defer tsv.StopService()
|
|
checkPlanID(t, planbuilder.PlanDMLSubquery, qre.plan.PlanID)
|
|
got, err := qre.Execute()
|
|
if err != nil {
|
|
t.Fatalf("qre.Execute() = %v, want nil", err)
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("got: %v, want: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestQueryExecutorPlanOtherWithinATransaction(t *testing.T) {
|
|
db := setUpQueryExecutorTest()
|
|
query := "show test_table"
|
|
want := &sqltypes.Result{
|
|
Fields: getTestTableFields(),
|
|
RowsAffected: 0,
|
|
Rows: [][]sqltypes.Value{},
|
|
}
|
|
db.AddQuery(query, want)
|
|
ctx := context.Background()
|
|
tsv := newTestTabletServer(ctx, enableRowCache|enableSchemaOverrides|enableStrict, db)
|
|
qre := newTestQueryExecutor(ctx, tsv, query, newTransaction(tsv))
|
|
defer tsv.StopService()
|
|
defer testCommitHelper(t, tsv, qre)
|
|
checkPlanID(t, planbuilder.PlanOther, qre.plan.PlanID)
|
|
got, err := qre.Execute()
|
|
if err != nil {
|
|
t.Fatalf("qre.Execute() = %v, want nil", err)
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("got: %v, want: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestQueryExecutorPlanPassSelectWithInATransaction(t *testing.T) {
|
|
db := setUpQueryExecutorTest()
|
|
fields := []*querypb.Field{
|
|
&querypb.Field{Name: "addr", Type: sqltypes.Int32},
|
|
}
|
|
query := "select addr from test_table where pk = 1 limit 1000"
|
|
want := &sqltypes.Result{
|
|
Fields: fields,
|
|
RowsAffected: 1,
|
|
Rows: [][]sqltypes.Value{
|
|
[]sqltypes.Value{sqltypes.MakeString([]byte("123"))},
|
|
},
|
|
}
|
|
db.AddQuery(query, want)
|
|
db.AddQuery("select addr from test_table where 1 != 1", &sqltypes.Result{
|
|
Fields: fields,
|
|
})
|
|
ctx := context.Background()
|
|
tsv := newTestTabletServer(ctx, enableStrict, db)
|
|
qre := newTestQueryExecutor(ctx, tsv, query, newTransaction(tsv))
|
|
defer tsv.StopService()
|
|
defer testCommitHelper(t, tsv, qre)
|
|
checkPlanID(t, planbuilder.PlanPassSelect, qre.plan.PlanID)
|
|
got, err := qre.Execute()
|
|
if err != nil {
|
|
t.Fatalf("qre.Execute() = %v, want nil", err)
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("got: %v, want: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestQueryExecutorPlanPassSelectWithLockOutsideATransaction(t *testing.T) {
|
|
db := setUpQueryExecutorTest()
|
|
query := "select * from test_table for update"
|
|
want := &sqltypes.Result{
|
|
Fields: getTestTableFields(),
|
|
Rows: [][]sqltypes.Value{},
|
|
}
|
|
db.AddQuery(query, want)
|
|
db.AddQuery("select * from test_table where 1 != 1", &sqltypes.Result{
|
|
Fields: getTestTableFields(),
|
|
})
|
|
ctx := context.Background()
|
|
tsv := newTestTabletServer(ctx, enableRowCache|enableSchemaOverrides|enableStrict, db)
|
|
qre := newTestQueryExecutor(ctx, tsv, query, 0)
|
|
defer tsv.StopService()
|
|
checkPlanID(t, planbuilder.PlanPassSelect, qre.plan.PlanID)
|
|
_, err := qre.Execute()
|
|
if err == nil {
|
|
t.Fatal("got: nil, want: error")
|
|
}
|
|
got, ok := err.(*TabletError)
|
|
if !ok {
|
|
t.Fatalf("got: %v, want: *TabletError", err)
|
|
}
|
|
if got.ErrorType != ErrFail {
|
|
t.Fatalf("got: %s, want: ErrFail", getTabletErrorString(got.ErrorType))
|
|
}
|
|
}
|
|
|
|
func TestQueryExecutorPlanPassSelect(t *testing.T) {
|
|
db := setUpQueryExecutorTest()
|
|
query := "select * from test_table limit 1000"
|
|
want := &sqltypes.Result{
|
|
Fields: getTestTableFields(),
|
|
Rows: [][]sqltypes.Value{},
|
|
}
|
|
db.AddQuery(query, want)
|
|
db.AddQuery("select * from test_table where 1 != 1", &sqltypes.Result{
|
|
Fields: getTestTableFields(),
|
|
})
|
|
ctx := context.Background()
|
|
tsv := newTestTabletServer(ctx, enableRowCache|enableSchemaOverrides|enableStrict, db)
|
|
qre := newTestQueryExecutor(ctx, tsv, query, 0)
|
|
defer tsv.StopService()
|
|
checkPlanID(t, planbuilder.PlanPassSelect, qre.plan.PlanID)
|
|
got, err := qre.Execute()
|
|
if err != nil {
|
|
t.Fatalf("qre.Execute() = %v, want nil", err)
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("got: %v, want: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestQueryExecutorPlanPKIn(t *testing.T) {
|
|
db := setUpQueryExecutorTest()
|
|
query := "select * from test_table where pk in (1, 2, 3) limit 1000"
|
|
expandedQuery := "select pk, name, addr from test_table where pk in (1, 2, 3)"
|
|
want := &sqltypes.Result{
|
|
Fields: getTestTableFields(),
|
|
RowsAffected: 1,
|
|
Rows: [][]sqltypes.Value{
|
|
[]sqltypes.Value{
|
|
sqltypes.MakeTrusted(sqltypes.Int32, []byte("1")),
|
|
sqltypes.MakeTrusted(sqltypes.Int32, []byte("20")),
|
|
sqltypes.MakeTrusted(sqltypes.Int32, []byte("30")),
|
|
},
|
|
},
|
|
}
|
|
db.AddQuery(query, want)
|
|
db.AddQuery(expandedQuery, want)
|
|
db.AddQuery("select * from test_table where 1 != 1", &sqltypes.Result{
|
|
Fields: getTestTableFields(),
|
|
})
|
|
ctx := context.Background()
|
|
tsv := newTestTabletServer(ctx, enableRowCache|enableSchemaOverrides|enableStrict, db)
|
|
qre := newTestQueryExecutor(ctx, tsv, query, 0)
|
|
defer tsv.StopService()
|
|
checkPlanID(t, planbuilder.PlanPKIn, qre.plan.PlanID)
|
|
got, err := qre.Execute()
|
|
if err != nil {
|
|
t.Fatalf("qre.Execute() = %v, want nil", err)
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("got: %v, want: %v", got, want)
|
|
}
|
|
|
|
cachedQuery := "select pk, name, addr from test_table where pk in (1)"
|
|
db.AddQuery(cachedQuery, &sqltypes.Result{
|
|
Fields: getTestTableFields(),
|
|
RowsAffected: 1,
|
|
Rows: [][]sqltypes.Value{
|
|
[]sqltypes.Value{
|
|
sqltypes.MakeTrusted(sqltypes.Int32, []byte("1")),
|
|
sqltypes.MakeTrusted(sqltypes.Int32, []byte("20")),
|
|
sqltypes.MakeTrusted(sqltypes.Int32, []byte("30")),
|
|
},
|
|
},
|
|
})
|
|
|
|
nonCachedQuery := "select pk, name, addr from test_table where pk in (2, 3)"
|
|
db.AddQuery(nonCachedQuery, &sqltypes.Result{})
|
|
db.AddQuery(cachedQuery, want)
|
|
// run again, this time pk=1 should hit the rowcache
|
|
got, err = qre.Execute()
|
|
if err != nil {
|
|
t.Fatalf("qre.Execute() = %v, want nil", err)
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("got: %v, want: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestQueryExecutorPlanSelectSubQuery(t *testing.T) {
|
|
db := setUpQueryExecutorTest()
|
|
query := "select * from test_table where name = 1 limit 1000"
|
|
expandedQuery := "select pk from test_table use index (`index`) where name = 1 limit 1000"
|
|
want := &sqltypes.Result{
|
|
Fields: getTestTableFields(),
|
|
}
|
|
db.AddQuery(query, want)
|
|
db.AddQuery(expandedQuery, want)
|
|
|
|
db.AddQuery("select * from test_table where 1 != 1", &sqltypes.Result{
|
|
Fields: getTestTableFields(),
|
|
})
|
|
ctx := context.Background()
|
|
tsv := newTestTabletServer(ctx, enableRowCache|enableSchemaOverrides|enableStrict, db)
|
|
qre := newTestQueryExecutor(ctx, tsv, query, 0)
|
|
defer tsv.StopService()
|
|
checkPlanID(t, planbuilder.PlanSelectSubquery, qre.plan.PlanID)
|
|
got, err := qre.Execute()
|
|
if err != nil {
|
|
t.Fatalf("qre.Execute() = %v, want nil", err)
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("got: %v, want: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestQueryExecutorPlanSet(t *testing.T) {
|
|
db := setUpQueryExecutorTest()
|
|
setQuery := "set unknown_key = 1"
|
|
db.AddQuery(setQuery, &sqltypes.Result{})
|
|
ctx := context.Background()
|
|
tsv := newTestTabletServer(ctx, enableRowCache|enableStrict, db)
|
|
defer tsv.StopService()
|
|
qre := newTestQueryExecutor(ctx, tsv, setQuery, 0)
|
|
checkPlanID(t, planbuilder.PlanSet, qre.plan.PlanID)
|
|
// Query will be delegated to MySQL and both Fields and Rows should be
|
|
// empty arrays in this case.
|
|
want := &sqltypes.Result{
|
|
Rows: make([][]sqltypes.Value, 0),
|
|
}
|
|
got, err := qre.Execute()
|
|
if err != nil {
|
|
t.Fatalf("qre.Execute() = %v, want nil", err)
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("qre.Execute() = %v, want: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestQueryExecutorPlanOther(t *testing.T) {
|
|
db := setUpQueryExecutorTest()
|
|
query := "show test_table"
|
|
want := &sqltypes.Result{
|
|
Fields: getTestTableFields(),
|
|
RowsAffected: 0,
|
|
Rows: [][]sqltypes.Value{},
|
|
}
|
|
db.AddQuery(query, want)
|
|
ctx := context.Background()
|
|
tsv := newTestTabletServer(ctx, enableRowCache|enableSchemaOverrides|enableStrict, db)
|
|
qre := newTestQueryExecutor(ctx, tsv, query, 0)
|
|
defer tsv.StopService()
|
|
checkPlanID(t, planbuilder.PlanOther, qre.plan.PlanID)
|
|
got, err := qre.Execute()
|
|
if err != nil {
|
|
t.Fatalf("got: %v, want nil", err)
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("qre.Execute() = %v, want: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestQueryExecutorTableAcl(t *testing.T) {
|
|
aclName := fmt.Sprintf("simpleacl-test-%d", rand.Int63())
|
|
tableacl.Register(aclName, &simpleacl.Factory{})
|
|
tableacl.SetDefaultACL(aclName)
|
|
db := setUpQueryExecutorTest()
|
|
query := "select * from test_table limit 1000"
|
|
want := &sqltypes.Result{
|
|
Fields: getTestTableFields(),
|
|
RowsAffected: 0,
|
|
Rows: [][]sqltypes.Value{},
|
|
}
|
|
db.AddQuery(query, want)
|
|
db.AddQuery("select * from test_table where 1 != 1", &sqltypes.Result{
|
|
Fields: getTestTableFields(),
|
|
})
|
|
|
|
username := "u2"
|
|
callerID := &querypb.VTGateCallerID{
|
|
Username: username,
|
|
}
|
|
ctx := callerid.NewContext(context.Background(), nil, callerID)
|
|
config := &tableaclpb.Config{
|
|
TableGroups: []*tableaclpb.TableGroupSpec{{
|
|
Name: "group01",
|
|
TableNamesOrPrefixes: []string{"test_table"},
|
|
Readers: []string{username},
|
|
}},
|
|
}
|
|
if err := tableacl.InitFromProto(config); err != nil {
|
|
t.Fatalf("unable to load tableacl config, error: %v", err)
|
|
}
|
|
|
|
tsv := newTestTabletServer(ctx, enableRowCache|enableSchemaOverrides|enableStrict, db)
|
|
qre := newTestQueryExecutor(ctx, tsv, query, 0)
|
|
defer tsv.StopService()
|
|
checkPlanID(t, planbuilder.PlanPassSelect, qre.plan.PlanID)
|
|
got, err := qre.Execute()
|
|
if err != nil {
|
|
t.Fatalf("got: %v, want nil", err)
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("qre.Execute() = %v, want: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestQueryExecutorTableAclNoPermission(t *testing.T) {
|
|
aclName := fmt.Sprintf("simpleacl-test-%d", rand.Int63())
|
|
tableacl.Register(aclName, &simpleacl.Factory{})
|
|
tableacl.SetDefaultACL(aclName)
|
|
db := setUpQueryExecutorTest()
|
|
query := "select * from test_table limit 1000"
|
|
want := &sqltypes.Result{
|
|
Fields: getTestTableFields(),
|
|
RowsAffected: 0,
|
|
Rows: [][]sqltypes.Value{},
|
|
}
|
|
db.AddQuery(query, want)
|
|
db.AddQuery("select * from test_table where 1 != 1", &sqltypes.Result{
|
|
Fields: getTestTableFields(),
|
|
})
|
|
|
|
username := "u2"
|
|
callerID := &querypb.VTGateCallerID{
|
|
Username: username,
|
|
}
|
|
ctx := callerid.NewContext(context.Background(), nil, callerID)
|
|
config := &tableaclpb.Config{
|
|
TableGroups: []*tableaclpb.TableGroupSpec{{
|
|
Name: "group02",
|
|
TableNamesOrPrefixes: []string{"test_table"},
|
|
Readers: []string{"superuser"},
|
|
}},
|
|
}
|
|
|
|
if err := tableacl.InitFromProto(config); err != nil {
|
|
t.Fatalf("unable to load tableacl config, error: %v", err)
|
|
}
|
|
// without enabling Config.StrictTableAcl
|
|
tsv := newTestTabletServer(ctx, enableRowCache|enableSchemaOverrides|enableStrict, db)
|
|
qre := newTestQueryExecutor(ctx, tsv, query, 0)
|
|
checkPlanID(t, planbuilder.PlanPassSelect, qre.plan.PlanID)
|
|
got, err := qre.Execute()
|
|
if err != nil {
|
|
t.Fatalf("got: %v, want nil", err)
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("qre.Execute() = %v, want: %v", got, want)
|
|
}
|
|
tsv.StopService()
|
|
|
|
// enable Config.StrictTableAcl
|
|
tsv = newTestTabletServer(ctx, enableRowCache|enableSchemaOverrides|enableStrict|enableStrictTableAcl, db)
|
|
qre = newTestQueryExecutor(ctx, tsv, query, 0)
|
|
defer tsv.StopService()
|
|
checkPlanID(t, planbuilder.PlanPassSelect, qre.plan.PlanID)
|
|
// query should fail because current user do not have read permissions
|
|
_, err = qre.Execute()
|
|
if err == nil {
|
|
t.Fatal("got: nil, want: error")
|
|
}
|
|
tabletError, ok := err.(*TabletError)
|
|
if !ok {
|
|
t.Fatalf("got: %v, want: *TabletError", err)
|
|
}
|
|
if tabletError.ErrorType != ErrFail {
|
|
t.Fatalf("got: %s, want: ErrFail", getTabletErrorString(tabletError.ErrorType))
|
|
}
|
|
}
|
|
|
|
func TestQueryExecutorTableAclExemptACL(t *testing.T) {
|
|
aclName := fmt.Sprintf("simpleacl-test-%d", rand.Int63())
|
|
tableacl.Register(aclName, &simpleacl.Factory{})
|
|
tableacl.SetDefaultACL(aclName)
|
|
db := setUpQueryExecutorTest()
|
|
query := "select * from test_table limit 1000"
|
|
want := &sqltypes.Result{
|
|
Fields: getTestTableFields(),
|
|
RowsAffected: 0,
|
|
Rows: [][]sqltypes.Value{},
|
|
}
|
|
db.AddQuery(query, want)
|
|
db.AddQuery("select * from test_table where 1 != 1", &sqltypes.Result{
|
|
Fields: getTestTableFields(),
|
|
})
|
|
|
|
username := "u2"
|
|
callerID := &querypb.VTGateCallerID{
|
|
Username: username,
|
|
}
|
|
ctx := callerid.NewContext(context.Background(), nil, callerID)
|
|
|
|
config := &tableaclpb.Config{
|
|
TableGroups: []*tableaclpb.TableGroupSpec{{
|
|
Name: "group02",
|
|
TableNamesOrPrefixes: []string{"test_table"},
|
|
Readers: []string{"u1"},
|
|
}},
|
|
}
|
|
|
|
if err := tableacl.InitFromProto(config); err != nil {
|
|
t.Fatalf("unable to load tableacl config, error: %v", err)
|
|
}
|
|
|
|
// enable Config.StrictTableAcl
|
|
tsv := newTestTabletServer(ctx, enableRowCache|enableSchemaOverrides|enableStrict|enableStrictTableAcl, db)
|
|
qre := newTestQueryExecutor(ctx, tsv, query, 0)
|
|
defer tsv.StopService()
|
|
checkPlanID(t, planbuilder.PlanPassSelect, qre.plan.PlanID)
|
|
// query should fail because current user do not have read permissions
|
|
_, err := qre.Execute()
|
|
if err == nil {
|
|
t.Fatal("got: nil, want: error")
|
|
}
|
|
tabletError, ok := err.(*TabletError)
|
|
if !ok {
|
|
t.Fatalf("got: %v, want: *TabletError", err)
|
|
}
|
|
if tabletError.ErrorType != ErrFail {
|
|
t.Fatalf("got: %s, want: ErrFail", getTabletErrorString(tabletError.ErrorType))
|
|
}
|
|
if !strings.Contains(tabletError.Error(), "table acl error") {
|
|
t.Fatalf("got %s, want tablet errorL table acl error", tabletError.Error())
|
|
}
|
|
|
|
// table acl should be ignored since this is an exempt user.
|
|
username = "exempt-acl"
|
|
f, _ := tableacl.GetCurrentAclFactory()
|
|
if tsv.qe.exemptACL, err = f.New([]string{username}); err != nil {
|
|
t.Fatalf("Cannot load exempt ACL for Table ACL: %v", err)
|
|
}
|
|
callerID = &querypb.VTGateCallerID{
|
|
Username: username,
|
|
}
|
|
ctx = callerid.NewContext(context.Background(), nil, callerID)
|
|
|
|
qre = newTestQueryExecutor(ctx, tsv, query, 0)
|
|
_, err = qre.Execute()
|
|
if err != nil {
|
|
t.Fatal("qre.Execute: nil, want: error")
|
|
}
|
|
}
|
|
|
|
func TestQueryExecutorTableAclDryRun(t *testing.T) {
|
|
aclName := fmt.Sprintf("simpleacl-test-%d", rand.Int63())
|
|
tableacl.Register(aclName, &simpleacl.Factory{})
|
|
tableacl.SetDefaultACL(aclName)
|
|
db := setUpQueryExecutorTest()
|
|
query := "select * from test_table limit 1000"
|
|
want := &sqltypes.Result{
|
|
Fields: getTestTableFields(),
|
|
RowsAffected: 0,
|
|
Rows: [][]sqltypes.Value{},
|
|
}
|
|
db.AddQuery(query, want)
|
|
db.AddQuery("select * from test_table where 1 != 1", &sqltypes.Result{
|
|
Fields: getTestTableFields(),
|
|
})
|
|
|
|
username := "u2"
|
|
callerID := &querypb.VTGateCallerID{
|
|
Username: username,
|
|
}
|
|
ctx := callerid.NewContext(context.Background(), nil, callerID)
|
|
|
|
config := &tableaclpb.Config{
|
|
TableGroups: []*tableaclpb.TableGroupSpec{{
|
|
Name: "group02",
|
|
TableNamesOrPrefixes: []string{"test_table"},
|
|
Readers: []string{"u1"},
|
|
}},
|
|
}
|
|
|
|
if err := tableacl.InitFromProto(config); err != nil {
|
|
t.Fatalf("unable to load tableacl config, error: %v", err)
|
|
}
|
|
|
|
tableACLStatsKey := strings.Join([]string{
|
|
"test_table",
|
|
"group02",
|
|
planbuilder.PlanPassSelect.String(),
|
|
username,
|
|
}, ".")
|
|
// enable Config.StrictTableAcl
|
|
tsv := newTestTabletServer(ctx, enableRowCache|enableSchemaOverrides|enableStrict|enableStrictTableAcl, db)
|
|
tsv.qe.enableTableAclDryRun = true
|
|
qre := newTestQueryExecutor(ctx, tsv, query, 0)
|
|
defer tsv.StopService()
|
|
checkPlanID(t, planbuilder.PlanPassSelect, qre.plan.PlanID)
|
|
beforeCount := tsv.qe.tableaclPseudoDenied.Counters.Counts()[tableACLStatsKey]
|
|
// query should fail because current user do not have read permissions
|
|
_, err := qre.Execute()
|
|
if err != nil {
|
|
t.Fatalf("qre.Execute() = %v, want: nil", err)
|
|
}
|
|
afterCount := tsv.qe.tableaclPseudoDenied.Counters.Counts()[tableACLStatsKey]
|
|
if afterCount-beforeCount != 1 {
|
|
t.Fatalf("table acl pseudo denied count should increase by one. got: %d, want: %d", afterCount, beforeCount+1)
|
|
}
|
|
}
|
|
|
|
func TestQueryExecutorBlacklistQRFail(t *testing.T) {
|
|
db := setUpQueryExecutorTest()
|
|
query := "select * from test_table where name = 1 limit 1000"
|
|
expandedQuery := "select pk from test_table use index (`index`) where name = 1 limit 1000"
|
|
expected := &sqltypes.Result{
|
|
Fields: getTestTableFields(),
|
|
}
|
|
db.AddQuery(query, expected)
|
|
db.AddQuery(expandedQuery, expected)
|
|
|
|
db.AddQuery("select * from test_table where 1 != 1", &sqltypes.Result{
|
|
Fields: getTestTableFields(),
|
|
})
|
|
|
|
bannedAddr := "127.0.0.1"
|
|
bannedUser := "u2"
|
|
|
|
alterRule := NewQueryRule("disable update", "disable update", QRFail)
|
|
alterRule.SetIPCond(bannedAddr)
|
|
alterRule.SetUserCond(bannedUser)
|
|
alterRule.SetQueryCond("select.*")
|
|
alterRule.AddPlanCond(planbuilder.PlanSelectSubquery)
|
|
alterRule.AddTableCond("test_table")
|
|
|
|
rulesName := "blacklistedRulesQRFail"
|
|
rules := NewQueryRules()
|
|
rules.Add(alterRule)
|
|
|
|
callInfo := &fakeCallInfo{
|
|
remoteAddr: bannedAddr,
|
|
username: bannedUser,
|
|
}
|
|
ctx := callinfo.NewContext(context.Background(), callInfo)
|
|
tsv := newTestTabletServer(ctx, enableRowCache|enableStrict, db)
|
|
tsv.qe.schemaInfo.queryRuleSources.UnRegisterQueryRuleSource(rulesName)
|
|
tsv.qe.schemaInfo.queryRuleSources.RegisterQueryRuleSource(rulesName)
|
|
defer tsv.qe.schemaInfo.queryRuleSources.UnRegisterQueryRuleSource(rulesName)
|
|
|
|
if err := tsv.qe.schemaInfo.queryRuleSources.SetRules(rulesName, rules); err != nil {
|
|
t.Fatalf("failed to set rule, error: %v", err)
|
|
}
|
|
|
|
qre := newTestQueryExecutor(ctx, tsv, query, 0)
|
|
defer tsv.StopService()
|
|
|
|
checkPlanID(t, planbuilder.PlanSelectSubquery, qre.plan.PlanID)
|
|
// execute should fail because query has been blacklisted
|
|
_, err := qre.Execute()
|
|
if err == nil {
|
|
t.Fatal("got: nil, want: error")
|
|
}
|
|
got, ok := err.(*TabletError)
|
|
if !ok {
|
|
t.Fatalf("got: %v, want: *TabletError", err)
|
|
}
|
|
if got.ErrorType != ErrFail {
|
|
t.Fatalf("got: %s, want: ErrFail", getTabletErrorString(got.ErrorType))
|
|
}
|
|
}
|
|
|
|
func TestQueryExecutorBlacklistQRRetry(t *testing.T) {
|
|
db := setUpQueryExecutorTest()
|
|
query := "select * from test_table where name = 1 limit 1000"
|
|
expandedQuery := "select pk from test_table use index (`index`) where name = 1 limit 1000"
|
|
expected := &sqltypes.Result{
|
|
Fields: getTestTableFields(),
|
|
}
|
|
db.AddQuery(query, expected)
|
|
db.AddQuery(expandedQuery, expected)
|
|
|
|
db.AddQuery("select * from test_table where 1 != 1", &sqltypes.Result{
|
|
Fields: getTestTableFields(),
|
|
})
|
|
|
|
bannedAddr := "127.0.0.1"
|
|
bannedUser := "x"
|
|
|
|
alterRule := NewQueryRule("disable update", "disable update", QRFailRetry)
|
|
alterRule.SetIPCond(bannedAddr)
|
|
alterRule.SetUserCond(bannedUser)
|
|
alterRule.SetQueryCond("select.*")
|
|
alterRule.AddPlanCond(planbuilder.PlanSelectSubquery)
|
|
alterRule.AddTableCond("test_table")
|
|
|
|
rulesName := "blacklistedRulesQRRetry"
|
|
rules := NewQueryRules()
|
|
rules.Add(alterRule)
|
|
|
|
callInfo := &fakeCallInfo{
|
|
remoteAddr: bannedAddr,
|
|
username: bannedUser,
|
|
}
|
|
ctx := callinfo.NewContext(context.Background(), callInfo)
|
|
tsv := newTestTabletServer(ctx, enableRowCache|enableStrict, db)
|
|
tsv.qe.schemaInfo.queryRuleSources.UnRegisterQueryRuleSource(rulesName)
|
|
tsv.qe.schemaInfo.queryRuleSources.RegisterQueryRuleSource(rulesName)
|
|
defer tsv.qe.schemaInfo.queryRuleSources.UnRegisterQueryRuleSource(rulesName)
|
|
|
|
if err := tsv.qe.schemaInfo.queryRuleSources.SetRules(rulesName, rules); err != nil {
|
|
t.Fatalf("failed to set rule, error: %v", err)
|
|
}
|
|
|
|
qre := newTestQueryExecutor(ctx, tsv, query, 0)
|
|
defer tsv.StopService()
|
|
|
|
checkPlanID(t, planbuilder.PlanSelectSubquery, qre.plan.PlanID)
|
|
_, err := qre.Execute()
|
|
if err == nil {
|
|
t.Fatal("got: nil, want: error")
|
|
}
|
|
got, ok := err.(*TabletError)
|
|
if !ok {
|
|
t.Fatalf("got: %v, want: *TabletError", err)
|
|
}
|
|
if got.ErrorType != ErrRetry {
|
|
t.Fatalf("got: %s, want: ErrRetry", getTabletErrorString(got.ErrorType))
|
|
}
|
|
}
|
|
|
|
type executorFlags int64
|
|
|
|
const (
|
|
noFlags executorFlags = iota
|
|
enableRowCache = 1 << iota
|
|
enableSchemaOverrides
|
|
enableStrict
|
|
enableStrictTableAcl
|
|
)
|
|
|
|
// newTestQueryExecutor uses a package level variable testTabletServer defined in tabletserver_test.go
|
|
func newTestTabletServer(ctx context.Context, flags executorFlags, db *fakesqldb.DB) *TabletServer {
|
|
randID := rand.Int63()
|
|
config := DefaultQsConfig
|
|
config.StatsPrefix = fmt.Sprintf("Stats-%d-", randID)
|
|
config.DebugURLPrefix = fmt.Sprintf("/debug-%d-", randID)
|
|
config.RowCache.StatsPrefix = fmt.Sprintf("Stats-%d-", randID)
|
|
config.PoolNamePrefix = fmt.Sprintf("Pool-%d-", randID)
|
|
config.PoolSize = 100
|
|
config.TransactionCap = 100
|
|
config.SpotCheckRatio = 1.0
|
|
config.EnablePublishStats = false
|
|
config.EnableAutoCommit = true
|
|
|
|
if flags&enableStrict > 0 {
|
|
config.StrictMode = true
|
|
} else {
|
|
config.StrictMode = false
|
|
}
|
|
if flags&enableRowCache > 0 {
|
|
config.RowCache.Enabled = true
|
|
config.RowCache.Binary = "ls"
|
|
config.RowCache.Connections = 100
|
|
}
|
|
if flags&enableStrictTableAcl > 0 {
|
|
config.StrictTableAcl = true
|
|
} else {
|
|
config.StrictTableAcl = false
|
|
}
|
|
tsv := NewTabletServer(config)
|
|
testUtils := newTestUtils()
|
|
dbconfigs := testUtils.newDBConfigs(db)
|
|
schemaOverrides := []SchemaOverride{}
|
|
if flags&enableSchemaOverrides > 0 {
|
|
schemaOverrides = getTestTableSchemaOverrides()
|
|
}
|
|
target := querypb.Target{TabletType: topodatapb.TabletType_MASTER}
|
|
tsv.StartService(target, dbconfigs, schemaOverrides, testUtils.newMysqld(&dbconfigs))
|
|
return tsv
|
|
}
|
|
|
|
func newTransaction(tsv *TabletServer) int64 {
|
|
session := proto.Session{
|
|
SessionId: tsv.sessionID,
|
|
TransactionId: 0,
|
|
}
|
|
txInfo := proto.TransactionInfo{TransactionId: 0}
|
|
err := tsv.Begin(context.Background(), &tsv.target, &session, &txInfo)
|
|
if err != nil {
|
|
panic(fmt.Errorf("failed to start a transaction: %v", err))
|
|
}
|
|
return txInfo.TransactionId
|
|
}
|
|
|
|
func newTestQueryExecutor(ctx context.Context, tsv *TabletServer, sql string, txID int64) *QueryExecutor {
|
|
logStats := newLogStats("TestQueryExecutor", ctx)
|
|
return &QueryExecutor{
|
|
ctx: ctx,
|
|
query: sql,
|
|
bindVars: make(map[string]interface{}),
|
|
transactionID: txID,
|
|
plan: tsv.qe.schemaInfo.GetPlan(ctx, logStats, sql),
|
|
logStats: logStats,
|
|
qe: tsv.qe,
|
|
}
|
|
}
|
|
|
|
func testCommitHelper(t *testing.T, tsv *TabletServer, queryExecutor *QueryExecutor) {
|
|
session := proto.Session{
|
|
SessionId: tsv.sessionID,
|
|
TransactionId: queryExecutor.transactionID,
|
|
}
|
|
if err := tsv.Commit(queryExecutor.ctx, &tsv.target, &session); err != nil {
|
|
t.Fatalf("failed to commit transaction: %d, err: %v", queryExecutor.transactionID, err)
|
|
}
|
|
}
|
|
|
|
func setUpQueryExecutorTest() *fakesqldb.DB {
|
|
fakecacheservice.Register()
|
|
db := fakesqldb.Register()
|
|
initQueryExecutorTestDB(db)
|
|
return db
|
|
}
|
|
|
|
func initQueryExecutorTestDB(db *fakesqldb.DB) {
|
|
for query, result := range getQueryExecutorSupportedQueries() {
|
|
db.AddQuery(query, result)
|
|
}
|
|
}
|
|
|
|
func getTestTableFields() []*querypb.Field {
|
|
return []*querypb.Field{
|
|
&querypb.Field{Name: "pk", Type: sqltypes.Int32},
|
|
&querypb.Field{Name: "name", Type: sqltypes.Int32},
|
|
&querypb.Field{Name: "addr", Type: sqltypes.Int32},
|
|
}
|
|
}
|
|
|
|
func checkPlanID(
|
|
t *testing.T,
|
|
expectedPlanID planbuilder.PlanType,
|
|
actualPlanID planbuilder.PlanType) {
|
|
if expectedPlanID != actualPlanID {
|
|
t.Fatalf("expect to get PlanID: %s, but got %s",
|
|
expectedPlanID.String(), actualPlanID.String())
|
|
}
|
|
}
|
|
|
|
func getTestTableSchemaOverrides() []SchemaOverride {
|
|
return []SchemaOverride{
|
|
SchemaOverride{
|
|
Name: "test_table",
|
|
PKColumns: []string{"pk"},
|
|
Cache: &struct {
|
|
Type string
|
|
Table string
|
|
}{
|
|
Type: "RW",
|
|
Table: "test_table",
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func getQueryExecutorSupportedQueries() map[string]*sqltypes.Result {
|
|
return map[string]*sqltypes.Result{
|
|
// queries for schema info
|
|
"select unix_timestamp()": &sqltypes.Result{
|
|
RowsAffected: 1,
|
|
Rows: [][]sqltypes.Value{
|
|
[]sqltypes.Value{sqltypes.MakeTrusted(sqltypes.Int32, []byte("1427325875"))},
|
|
},
|
|
},
|
|
"select @@global.sql_mode": &sqltypes.Result{
|
|
RowsAffected: 1,
|
|
Rows: [][]sqltypes.Value{
|
|
[]sqltypes.Value{sqltypes.MakeString([]byte("STRICT_TRANS_TABLES"))},
|
|
},
|
|
},
|
|
baseShowTables: &sqltypes.Result{
|
|
RowsAffected: 1,
|
|
Rows: [][]sqltypes.Value{
|
|
[]sqltypes.Value{
|
|
sqltypes.MakeString([]byte("test_table")),
|
|
sqltypes.MakeString([]byte("USER TABLE")),
|
|
sqltypes.MakeTrusted(sqltypes.Int32, []byte("1427325875")),
|
|
sqltypes.MakeString([]byte("")),
|
|
sqltypes.MakeTrusted(sqltypes.Int32, []byte("1")),
|
|
sqltypes.MakeTrusted(sqltypes.Int32, []byte("2")),
|
|
sqltypes.MakeTrusted(sqltypes.Int32, []byte("3")),
|
|
sqltypes.MakeTrusted(sqltypes.Int32, []byte("4")),
|
|
},
|
|
},
|
|
},
|
|
"select * from `test_table` where 1 != 1": &sqltypes.Result{
|
|
Fields: []*querypb.Field{{
|
|
Name: "pk",
|
|
Type: sqltypes.Int32,
|
|
}, {
|
|
Name: "name",
|
|
Type: sqltypes.Int32,
|
|
}, {
|
|
Name: "addr",
|
|
Type: sqltypes.Int32,
|
|
}},
|
|
},
|
|
"describe `test_table`": &sqltypes.Result{
|
|
RowsAffected: 3,
|
|
Rows: [][]sqltypes.Value{
|
|
[]sqltypes.Value{
|
|
sqltypes.MakeString([]byte("pk")),
|
|
sqltypes.MakeString([]byte("int")),
|
|
sqltypes.MakeString([]byte{}),
|
|
sqltypes.MakeString([]byte{}),
|
|
sqltypes.MakeString([]byte("1")),
|
|
sqltypes.MakeString([]byte{}),
|
|
},
|
|
[]sqltypes.Value{
|
|
sqltypes.MakeString([]byte("name")),
|
|
sqltypes.MakeString([]byte("int")),
|
|
sqltypes.MakeString([]byte{}),
|
|
sqltypes.MakeString([]byte{}),
|
|
sqltypes.MakeString([]byte("1")),
|
|
sqltypes.MakeString([]byte{}),
|
|
},
|
|
[]sqltypes.Value{
|
|
sqltypes.MakeString([]byte("addr")),
|
|
sqltypes.MakeString([]byte("int")),
|
|
sqltypes.MakeString([]byte{}),
|
|
sqltypes.MakeString([]byte{}),
|
|
sqltypes.MakeString([]byte("1")),
|
|
sqltypes.MakeString([]byte{}),
|
|
},
|
|
},
|
|
},
|
|
// for SplitQuery because it needs a primary key column
|
|
"show index from `test_table`": &sqltypes.Result{
|
|
RowsAffected: 2,
|
|
Rows: [][]sqltypes.Value{
|
|
[]sqltypes.Value{
|
|
sqltypes.MakeString([]byte{}),
|
|
sqltypes.MakeString([]byte{}),
|
|
sqltypes.MakeString([]byte("PRIMARY")),
|
|
sqltypes.MakeString([]byte{}),
|
|
sqltypes.MakeString([]byte("pk")),
|
|
sqltypes.MakeString([]byte{}),
|
|
sqltypes.MakeString([]byte("300")),
|
|
},
|
|
[]sqltypes.Value{
|
|
sqltypes.MakeString([]byte{}),
|
|
sqltypes.MakeString([]byte{}),
|
|
sqltypes.MakeString([]byte("index")),
|
|
sqltypes.MakeString([]byte{}),
|
|
sqltypes.MakeString([]byte("name")),
|
|
sqltypes.MakeString([]byte{}),
|
|
sqltypes.MakeString([]byte("300")),
|
|
},
|
|
},
|
|
},
|
|
"begin": &sqltypes.Result{},
|
|
"commit": &sqltypes.Result{},
|
|
baseShowTables + " and table_name = 'test_table'": &sqltypes.Result{
|
|
RowsAffected: 1,
|
|
Rows: [][]sqltypes.Value{
|
|
[]sqltypes.Value{
|
|
sqltypes.MakeString([]byte("test_table")),
|
|
sqltypes.MakeString([]byte("USER TABLE")),
|
|
sqltypes.MakeTrusted(sqltypes.Int32, []byte("1427325875")),
|
|
sqltypes.MakeString([]byte("")),
|
|
sqltypes.MakeTrusted(sqltypes.Int32, []byte("1")),
|
|
sqltypes.MakeTrusted(sqltypes.Int32, []byte("2")),
|
|
sqltypes.MakeTrusted(sqltypes.Int32, []byte("3")),
|
|
sqltypes.MakeTrusted(sqltypes.Int32, []byte("4")),
|
|
},
|
|
},
|
|
},
|
|
"rollback": &sqltypes.Result{},
|
|
}
|
|
}
|