Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SET planning #6487

Merged
merged 19 commits into from
Aug 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions go/vt/sqlparser/expression_converter.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,28 @@ import (
"vitess.io/vitess/go/vt/vtgate/evalengine"
)

var ExprNotSupported = fmt.Errorf("Expr Not Supported")
// ErrExprNotSupported signals that the expression cannot be handled by expression evaluation engine.
var ErrExprNotSupported = fmt.Errorf("Expr Not Supported")

//Convert converts between AST expressions and executable expressions
func Convert(e Expr) (evalengine.Expr, error) {
switch node := e.(type) {
case *SQLVal:
switch node.Type {
case IntVal:
return evalengine.NewLiteralInt(node.Val)
return evalengine.NewLiteralIntFromBytes(node.Val)
case FloatVal:
return evalengine.NewLiteralFloat(node.Val)
case ValArg:
return &evalengine.BindVariable{Key: string(node.Val[1:])}, nil
return evalengine.NewBindVar(string(node.Val[1:])), nil
case StrVal:
return evalengine.NewLiteralString(node.Val)
return evalengine.NewLiteralString(node.Val), nil
}
case BoolVal:
if node {
return evalengine.NewLiteralIntFromBytes([]byte("1"))
}
return evalengine.NewLiteralIntFromBytes([]byte("0"))
case *BinaryExpr:
var op evalengine.BinaryExpr
switch node.Operator {
Expand All @@ -50,7 +56,7 @@ func Convert(e Expr) (evalengine.Expr, error) {
case DivStr:
op = &evalengine.Division{}
default:
return nil, ExprNotSupported
return nil, ErrExprNotSupported
}
left, err := Convert(node.Left)
if err != nil {
Expand All @@ -67,5 +73,5 @@ func Convert(e Expr) (evalengine.Expr, error) {
}, nil

}
return nil, ExprNotSupported
return nil, ErrExprNotSupported
}
14 changes: 11 additions & 3 deletions go/vt/sqlparser/set_normalizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,13 @@ func (n *setNormalizer) normalizeSetExpr(in *SetExpr) (*SetExpr, error) {
}
switch {
case strings.HasPrefix(in.Name.Lowered(), "session."):
in.Name = NewColIdent(in.Name.Lowered()[8:])
in.Name = createColumn(in.Name.Lowered()[8:])
in.Scope = SessionStr
case strings.HasPrefix(in.Name.Lowered(), "global."):
in.Name = NewColIdent(in.Name.Lowered()[7:])
in.Name = createColumn(in.Name.Lowered()[7:])
in.Scope = GlobalStr
case strings.HasPrefix(in.Name.Lowered(), "vitess_metadata."):
in.Name = NewColIdent(in.Name.Lowered()[16:])
in.Name = createColumn(in.Name.Lowered()[16:])
in.Scope = VitessMetadataStr
default:
in.Name.at = NoAt
Expand All @@ -79,3 +79,11 @@ func (n *setNormalizer) normalizeSetExpr(in *SetExpr) (*SetExpr, error) {
}
panic("this should never happen")
}

func createColumn(str string) ColIdent {
size := len(str)
if str[0] == '`' && str[size-1] == '`' {
str = str[1 : size-1]
}
return NewColIdent(str)
}
14 changes: 10 additions & 4 deletions go/vt/sqlparser/set_normalizer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ func TestNormalizeSetExpr(t *testing.T) {
tests := []struct {
in, expected, err string
}{{
in: "@@foo = 42",
expected: "session foo = 42",
in: "@@session.x.foo = 42",
expected: "session `x.foo` = 42",
}, {
in: "@@session.foo = 42",
expected: "session foo = 42",
Expand Down Expand Up @@ -57,8 +57,14 @@ func TestNormalizeSetExpr(t *testing.T) {
in: "@@x.foo = 42",
expected: "session `x.foo` = 42",
}, {
in: "@@session.x.foo = 42",
expected: "session `x.foo` = 42",
in: "@@session.`foo` = 1",
expected: "session foo = 1",
}, {
in: "@@global.`foo` = 1",
expected: "global foo = 1",
}, {
in: "@@vitess_metadata.`foo` = 1",
expected: "vitess_metadata foo = 1",
//}, { TODO: we should support local scope as well
// in: "local foo = 42",
// expected: "session foo = 42",
Expand Down
56 changes: 52 additions & 4 deletions go/vt/vtgate/engine/fake_vcursor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,31 @@ func (t noopVCursor) Session() SessionActions {
return t
}

func (t noopVCursor) SetTarget(target string) error {
func (t noopVCursor) SetAutocommit(bool) error {
panic("implement me")
}

func (t noopVCursor) SetClientFoundRows(bool) {
panic("implement me")
}

func (t noopVCursor) SetSkipQueryPlanCache(bool) {
panic("implement me")
}

func (t noopVCursor) SetSQLSelectLimit(int64) {
panic("implement me")
}

func (t noopVCursor) SetTransactionMode(vtgatepb.TransactionMode) {
panic("implement me")
}

func (t noopVCursor) SetWorkload(querypb.ExecuteOptions_Workload) {
panic("implement me")
}

func (t noopVCursor) SetTarget(string) error {
panic("implement me")
}

Expand Down Expand Up @@ -192,7 +216,7 @@ func (f *loggingVCursor) ShardSession() []*srvtopo.ResolvedShard {
return nil
}

func (f *loggingVCursor) ExecuteVSchema(keyspace string, vschemaDDL *sqlparser.DDL) error {
func (f *loggingVCursor) ExecuteVSchema(string, *sqlparser.DDL) error {
panic("implement me")
}

Expand All @@ -209,7 +233,7 @@ func (f *loggingVCursor) Context() context.Context {
return context.Background()
}

func (f *loggingVCursor) SetContextTimeout(timeout time.Duration) context.CancelFunc {
func (f *loggingVCursor) SetContextTimeout(time.Duration) context.CancelFunc {
return func() {}
}

Expand All @@ -221,7 +245,7 @@ func (f *loggingVCursor) RecordWarning(warning *querypb.QueryWarning) {
f.warnings = append(f.warnings, warning)
}

func (f *loggingVCursor) Execute(method string, query string, bindvars map[string]*querypb.BindVariable, rollbackOnError bool, co vtgatepb.CommitOrder) (*sqltypes.Result, error) {
func (f *loggingVCursor) Execute(_ string, query string, bindvars map[string]*querypb.BindVariable, rollbackOnError bool, co vtgatepb.CommitOrder) (*sqltypes.Result, error) {
name := "Unknown"
switch co {
case vtgatepb.CommitOrder_NORMAL:
Expand Down Expand Up @@ -351,6 +375,30 @@ func (f *loggingVCursor) Rewind() {
f.warnings = nil
}

func (f *loggingVCursor) SetAutocommit(bool) error {
panic("implement me")
}

func (f *loggingVCursor) SetClientFoundRows(bool) {
panic("implement me")
}

func (f *loggingVCursor) SetSkipQueryPlanCache(bool) {
panic("implement me")
}

func (f *loggingVCursor) SetSQLSelectLimit(int64) {
panic("implement me")
}

func (f *loggingVCursor) SetTransactionMode(vtgatepb.TransactionMode) {
panic("implement me")
}

func (f *loggingVCursor) SetWorkload(querypb.ExecuteOptions_Workload) {
panic("implement me")
}

func (f *loggingVCursor) nextResult() (*sqltypes.Result, error) {
if f.results == nil || f.curResult >= len(f.results) {
return &sqltypes.Result{}, f.resultErr
Expand Down
7 changes: 7 additions & 0 deletions go/vt/vtgate/engine/primitive.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,13 @@ type (

// ShardSession returns shard info about open connections
ShardSession() []*srvtopo.ResolvedShard

SetAutocommit(bool) error
SetClientFoundRows(bool)
SetSkipQueryPlanCache(bool)
SetSQLSelectLimit(int64)
SetTransactionMode(vtgatepb.TransactionMode)
SetWorkload(querypb.ExecuteOptions_Workload)
}

// Plan represents the execution strategy for a given query.
Expand Down
129 changes: 123 additions & 6 deletions go/vt/vtgate/engine/set.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import (
"bytes"
"encoding/json"
"fmt"
"strings"

vtgatepb "vitess.io/vitess/go/vt/proto/vtgate"

"vitess.io/vitess/go/vt/log"

Expand Down Expand Up @@ -78,13 +81,11 @@ type (
Expr string
}

// SysVarSetSpecial implements the SetOp interface and will write the changes variable into the session
// SysVarSetAware implements the SetOp interface and will write the changes variable into the session
// The special part is that these settings change the sessions behaviour in different ways
SysVarSetSpecial struct {
Name string
Keyspace *vindexes.Keyspace
TargetDestination key.Destination `json:",omitempty"`
Expr string
SysVarSetAware struct {
Name string
Expr evalengine.Expr
}
)

Expand Down Expand Up @@ -338,3 +339,119 @@ func (svs *SysVarSet) checkAndUpdateSysVar(vcursor VCursor, res evalengine.Expre
vcursor.Session().NeedsReservedConn()
return true, nil
}

var _ SetOp = (*SysVarSetAware)(nil)

// System variables that needs special handling
const (
Autocommit = "autocommit"
ClientFoundRows = "client_found_rows"
SkipQueryPlanCache = "skip_query_plan_cache"
TxReadOnly = "tx_read_only"
TransactionReadOnly = "transaction_read_only"
SQLSelectLimit = "sql_select_limit"
TransactionMode = "transaction_mode"
Workload = "workload"
Charset = "charset"
Names = "names"
)

//MarshalJSON provides the type to SetOp for plan json
func (svss *SysVarSetAware) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Type string
Name string
Expr string
}{
Type: "SysVarAware",
Name: svss.Name,
Expr: svss.Expr.String(),
})
}

//Execute implements the SetOp interface method
func (svss *SysVarSetAware) Execute(vcursor VCursor, env evalengine.ExpressionEnv) error {
switch svss.Name {
// These are all the boolean values we need to handle
case Autocommit, ClientFoundRows, SkipQueryPlanCache, TxReadOnly, TransactionReadOnly:
value, err := svss.Expr.Evaluate(env)
if err != nil {
return err
}
boolValue, err := value.ToBooleanStrict()
if err != nil {
return vterrors.Wrapf(err, "System setting '%s' can't be set to this value", svss.Name)
}
switch svss.Name {
case Autocommit:
vcursor.Session().SetAutocommit(boolValue)
case ClientFoundRows:
vcursor.Session().SetClientFoundRows(boolValue)
case SkipQueryPlanCache:
vcursor.Session().SetSkipQueryPlanCache(boolValue)
case TxReadOnly, TransactionReadOnly:
// TODO (4127): This is a dangerous NOP.
}

case SQLSelectLimit:
value, err := svss.Expr.Evaluate(env)
if err != nil {
return err
}

v := value.Value()
if !v.IsIntegral() {
return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected value type for sql_select_limit: %T", value.Value().Type().String())
}
intValue, err := v.ToInt64()
if err != nil {
return err
}
vcursor.Session().SetSQLSelectLimit(intValue)

// String settings
case TransactionMode, Workload, Charset, Names:
value, err := svss.Expr.Evaluate(env)
if err != nil {
return err
}
v := value.Value()
if !v.IsText() && !v.IsBinary() {
return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected value type for %s: %s", svss.Name, value.Value().Type().String())
}

str := v.ToString()
switch svss.Name {
case TransactionMode:
out, ok := vtgatepb.TransactionMode_value[strings.ToUpper(str)]
if !ok {
return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "invalid transaction_mode: %s", str)
}
vcursor.Session().SetTransactionMode(vtgatepb.TransactionMode(out))
case Workload:
out, ok := querypb.ExecuteOptions_Workload_value[strings.ToUpper(str)]
if !ok {
return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "invalid workload: %s", str)
}
vcursor.Session().SetWorkload(querypb.ExecuteOptions_Workload(out))
case Charset, Names:
switch strings.ToLower(str) {
case "", "utf8", "utf8mb4", "latin1", "default":
// do nothing
break
default:
return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected value for charset/names: %v", str)
}
}

default:
return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "unsupported construct")
}

return nil
}

//VariableName implements the SetOp interface method
func (svss *SysVarSetAware) VariableName() string {
return svss.Name
}
11 changes: 2 additions & 9 deletions go/vt/vtgate/engine/set_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package engine

import (
"fmt"
"strconv"
"testing"

"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -75,12 +74,6 @@ func TestSetTable(t *testing.T) {
expectedError string
}

intExpr := func(i int) evalengine.Expr {
s := strconv.FormatInt(int64(i), 10)
e, _ := evalengine.NewLiteralInt([]byte(s))
return e
}

tests := []testCase{
{
testName: "nil set ops",
Expand All @@ -91,7 +84,7 @@ func TestSetTable(t *testing.T) {
setOps: []SetOp{
&UserDefinedVariable{
Name: "x",
Expr: intExpr(42),
Expr: evalengine.NewLiteralInt(42),
},
},
expectedQueryLog: []string{
Expand Down Expand Up @@ -173,7 +166,7 @@ func TestSetTable(t *testing.T) {
setOps: []SetOp{
&UserDefinedVariable{
Name: "x",
Expr: intExpr(1),
Expr: evalengine.NewLiteralInt(1),
},
&SysVarIgnore{
Name: "y",
Expand Down
Loading