Skip to content

Commit

Permalink
support pushing bitwise operators and LogicXor down to kv (#1846)
Browse files Browse the repository at this point in the history
  • Loading branch information
XuHuaiyu authored Oct 27, 2016
1 parent f93029c commit d38cc3f
Show file tree
Hide file tree
Showing 10 changed files with 471 additions and 30 deletions.
8 changes: 6 additions & 2 deletions distsql/xeval/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,16 @@ func (e *Evaluator) Eval(expr *tipb.Expr) (types.Datum, error) {
tipb.ExprType_NullEQ, tipb.ExprType_Like, tipb.ExprType_In:
return e.evalCompareOps(expr)
// logic operator
case tipb.ExprType_And, tipb.ExprType_Or, tipb.ExprType_Not:
case tipb.ExprType_And, tipb.ExprType_Or, tipb.ExprType_Xor, tipb.ExprType_Not:
return e.evalLogicOps(expr)
// arithmetic operator
case tipb.ExprType_Plus, tipb.ExprType_Div, tipb.ExprType_Minus,
tipb.ExprType_Mul, tipb.ExprType_IntDiv, tipb.ExprType_Mod:
return e.evalArithmeticOps(expr)
// bit operator
case tipb.ExprType_BitAnd, tipb.ExprType_BitOr, tipb.ExprType_BitNeg,
tipb.ExprType_BitXor, tipb.ExprType_LeftShift, tipb.ExprType_RighShift:
return e.evalBitOps(expr)
case tipb.ExprType_Case:
return e.evalCaseWhen(expr)
case tipb.ExprType_Coalesce:
Expand All @@ -76,7 +80,7 @@ func (e *Evaluator) Eval(expr *tipb.Expr) (types.Datum, error) {

func (e *Evaluator) evalTwoChildren(expr *tipb.Expr) (left, right types.Datum, err error) {
if len(expr.Children) != 2 {
err = ErrInvalid.Gen("need 2 operands but got %d", len(expr.Children))
err = ErrInvalid.Gen("%s need 2 operands but got %d", tipb.ExprType_name[int32(expr.GetTp())], len(expr.Children))
return
}
left, err = e.Eval(expr.Children[0])
Expand Down
80 changes: 80 additions & 0 deletions distsql/xeval/eval_bit_ops.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

package xeval

import (
"github.com/juju/errors"
"github.com/pingcap/tidb/util/types"
"github.com/pingcap/tipb/go-tipb"
)

func (e *Evaluator) evalBitOps(expr *tipb.Expr) (types.Datum, error) {
var result types.Datum
if expr.GetTp() == tipb.ExprType_BitNeg {
if len(expr.Children) != 1 {
err := ErrInvalid.Gen("BitNeg(~) need 1 operand but got %d", len(expr.Children))
return result, err
}
operand, err := e.Eval(expr.Children[0])
if err != nil {
return types.Datum{}, errors.Trace(err)
}
a, err := types.CoerceArithmetic(operand)
if err != nil {
return result, errors.Trace(err)
}
return types.ComputeBitNeg(a)
}
left, right, err := e.evalTwoChildren(expr)
if err != nil {
return result, errors.Trace(err)
}
return ComputeBit(expr.GetTp(), left, right)
}

// ComputeBit computes the bitwise operation on two datums.
func ComputeBit(op tipb.ExprType, left, right types.Datum) (types.Datum, error) {
var result types.Datum
a, err := types.CoerceArithmetic(left)
if err != nil {
return result, errors.Trace(err)
}

b, err := types.CoerceArithmetic(right)
if err != nil {
return result, errors.Trace(err)
}
a, b, err = types.CoerceDatum(a, b)
if err != nil {
return result, errors.Trace(err)
}
if a.IsNull() || b.IsNull() {
return result, nil
}

switch op {
case tipb.ExprType_BitAnd:
return types.ComputeBitAnd(a, b)
case tipb.ExprType_BitOr:
return types.ComputeBitOr(a, b)
case tipb.ExprType_BitXor:
return types.ComputeBitXor(a, b)
case tipb.ExprType_LeftShift:
return types.ComputeLeftShift(a, b)
case tipb.ExprType_RighShift:
return types.ComputeRightShift(a, b)
default:
return result, errors.Errorf("Unknown binop type: %v", op)
}
}
4 changes: 2 additions & 2 deletions distsql/xeval/eval_compare_ops.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ func containsAlphabet(s string) bool {

func (e *Evaluator) evalIn(expr *tipb.Expr) (types.Datum, error) {
if len(expr.Children) != 2 {
return types.Datum{}, ErrInvalid.Gen("IN need 2 operand, got %d", len(expr.Children))
return types.Datum{}, ErrInvalid.Gen("IN need 2 operands, got %d", len(expr.Children))
}
target, err := e.Eval(expr.Children[0])
if err != nil {
Expand All @@ -243,7 +243,7 @@ func (e *Evaluator) evalIn(expr *tipb.Expr) (types.Datum, error) {
}
valueListExpr := expr.Children[1]
if valueListExpr.GetTp() != tipb.ExprType_ValueList {
return types.Datum{}, ErrInvalid.Gen("the second children should be value list type")
return types.Datum{}, ErrInvalid.Gen("the second child should be value list type")
}
decoded, err := e.decodeValueList(valueListExpr)
if err != nil {
Expand Down
17 changes: 17 additions & 0 deletions distsql/xeval/eval_logic_ops.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ func (e *Evaluator) evalLogicOps(expr *tipb.Expr) (types.Datum, error) {
return e.evalAnd(leftBool, rightBool)
case tipb.ExprType_Or:
return e.evalOr(leftBool, rightBool)
case tipb.ExprType_Xor:
return e.evalXor(leftBool, rightBool)
default:
return types.Datum{}, errors.Errorf("Unknown binop type: %v", op)
}
Expand Down Expand Up @@ -68,6 +70,21 @@ func (e *Evaluator) evalOr(leftBool, rightBool int64) (types.Datum, error) {
return d, nil
}

// evalXor computes result of (X XOR Y).
func (e *Evaluator) evalXor(leftBool, rightBool int64) (types.Datum, error) {
var d types.Datum
if leftBool == compareResultNull || rightBool == compareResultNull {
d.SetNull()
return d, nil
}
if leftBool == rightBool {
d.SetInt64(0)
return d, nil
}
d.SetInt64(1)
return d, nil
}

// evalNot computes result of (!X).
func (e *Evaluator) evalNot(expr *tipb.Expr) (types.Datum, error) {
if len(expr.Children) != 1 {
Expand Down
5 changes: 5 additions & 0 deletions mysql/mydecimal.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,11 @@ type MyDecimal struct {
wordBuf [maxWordBufLen]int32
}

// IsNegative returns whether a decimal is negative.
func (d *MyDecimal) IsNegative() bool {
return d.negative
}

// String returns the decimal string representation rounded to resultFrac.
func (d *MyDecimal) String() string {
tmp := *d
Expand Down
57 changes: 56 additions & 1 deletion plan/expr_to_pb.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,10 @@ func scalarFuncToPBExpr(client kv.Client, expr *expression.ScalarFunction) *tipb
return compareOpsToPBExpr(client, expr)
case ast.Plus, ast.Minus, ast.Mul, ast.Div, ast.Mod, ast.IntDiv:
return arithmeticalOpsToPBExpr(client, expr)
case ast.AndAnd, ast.OrOr, ast.UnaryNot:
case ast.AndAnd, ast.OrOr, ast.UnaryNot, ast.LogicXor:
return logicalOpsToPBExpr(client, expr)
case ast.And, ast.Or, ast.BitNeg, ast.Xor, ast.LeftShift, ast.RightShift:
return bitwiseFuncToPBExpr(client, expr)
case ast.Case, ast.Coalesce:
return builtinFuncToPBExpr(client, expr)
default:
Expand Down Expand Up @@ -230,9 +232,47 @@ func logicalOpsToPBExpr(client kv.Client, expr *expression.ScalarFunction) *tipb
tp = tipb.ExprType_And
case ast.OrOr:
tp = tipb.ExprType_Or
case ast.LogicXor:
tp = tipb.ExprType_Xor
case ast.UnaryNot:
return notToPBExpr(client, expr)
}
if !client.SupportRequestType(kv.ReqTypeSelect, int64(tp)) {
return nil
}

expr0 := exprToPB(client, expr.Args[0])
if expr0 == nil {
return nil
}
expr1 := exprToPB(client, expr.Args[1])
if expr1 == nil {
return nil
}
return &tipb.Expr{
Tp: tp,
Children: []*tipb.Expr{expr0, expr1}}
}

func bitwiseFuncToPBExpr(client kv.Client, expr *expression.ScalarFunction) *tipb.Expr {
var tp tipb.ExprType
switch expr.FuncName.L {
case ast.And:
tp = tipb.ExprType_BitAnd
case ast.Or:
tp = tipb.ExprType_BitOr
case ast.Xor:
tp = tipb.ExprType_BitXor
case ast.LeftShift:
tp = tipb.ExprType_LeftShift
case ast.RightShift:
tp = tipb.ExprType_RighShift
case ast.BitNeg:
return bitNegToPBExpr(client, expr)
}
if !client.SupportRequestType(kv.ReqTypeSelect, int64(tp)) {
return nil
}
expr0 := exprToPB(client, expr.Args[0])
if expr0 == nil {
return nil
Expand Down Expand Up @@ -278,6 +318,21 @@ func notToPBExpr(client kv.Client, expr *expression.ScalarFunction) *tipb.Expr {
Children: []*tipb.Expr{child}}
}

func bitNegToPBExpr(client kv.Client, expr *expression.ScalarFunction) *tipb.Expr {
if !client.SupportRequestType(kv.ReqTypeSelect, int64(tipb.ExprType_BitNeg)) {
return nil
}

child := exprToPB(client, expr.Args[0])
if child == nil {
return nil
}

return &tipb.Expr{
Tp: tipb.ExprType_BitNeg,
Children: []*tipb.Expr{child}}
}

func constListToPB(client kv.Client, list []expression.Expression) *tipb.Expr {
if !client.SupportRequestType(kv.ReqTypeSelect, int64(tipb.ExprType_ValueList)) {
return nil
Expand Down
59 changes: 47 additions & 12 deletions plan/plan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,20 +118,30 @@ func mockResolve(node ast.Node) error {

func supportExpr(exprType tipb.ExprType) bool {
switch exprType {
case tipb.ExprType_Null, tipb.ExprType_Int64, tipb.ExprType_Uint64, tipb.ExprType_Float32,
tipb.ExprType_Float64, tipb.ExprType_String, tipb.ExprType_Bytes,
tipb.ExprType_MysqlDuration, tipb.ExprType_MysqlDecimal, tipb.ExprType_MysqlTime,
tipb.ExprType_ColumnRef,
tipb.ExprType_And, tipb.ExprType_Or,
tipb.ExprType_LT, tipb.ExprType_LE, tipb.ExprType_EQ, tipb.ExprType_NE,
// data type
case tipb.ExprType_Null, tipb.ExprType_Int64, tipb.ExprType_Uint64,
tipb.ExprType_Float32, tipb.ExprType_Float64, tipb.ExprType_String,
tipb.ExprType_Bytes, tipb.ExprType_MysqlDuration, tipb.ExprType_MysqlDecimal,
tipb.ExprType_MysqlTime, tipb.ExprType_ColumnRef:
return true
// logic operators
case tipb.ExprType_And, tipb.ExprType_Or, tipb.ExprType_Not, tipb.ExprType_Xor:
return true
// compare operators
case tipb.ExprType_LT, tipb.ExprType_LE, tipb.ExprType_EQ, tipb.ExprType_NE,
tipb.ExprType_GE, tipb.ExprType_GT, tipb.ExprType_NullEQ,
tipb.ExprType_In, tipb.ExprType_ValueList,
tipb.ExprType_Not,
tipb.ExprType_Like:
tipb.ExprType_In, tipb.ExprType_ValueList, tipb.ExprType_Like:
return true
case tipb.ExprType_Plus, tipb.ExprType_Div:
// arithmetic operators
case tipb.ExprType_Plus, tipb.ExprType_Div, tipb.ExprType_Minus,
tipb.ExprType_Mul, tipb.ExprType_IntDiv, tipb.ExprType_Mod:
return true
case tipb.ExprType_Count, tipb.ExprType_First, tipb.ExprType_Sum, tipb.ExprType_Avg, tipb.ExprType_Max, tipb.ExprType_Min:
// aggregate functions
case tipb.ExprType_Count, tipb.ExprType_First, tipb.ExprType_Sum,
tipb.ExprType_Avg, tipb.ExprType_Max, tipb.ExprType_Min:
return true
// bitwise operators
case tipb.ExprType_BitAnd, tipb.ExprType_BitOr, tipb.ExprType_BitXor, tipb.ExprType_BitNeg:
return true
case tipb.ExprType_Case, tipb.ExprType_Coalesce:
return true
Expand Down Expand Up @@ -276,7 +286,7 @@ func (s *testPlanSuite) TestTopnPushDown(c *C) {
}

// TestLogicOpsPushDown tests whether logic operators been pushed down successfully.
func (s *testPlanSuite) TestLogicOpsPushDown(c *C) {
func (s *testPlanSuite) TestOperatorsPushDown(c *C) {
defer testleak.AfterTest(c)()
cases := []struct {
sql string
Expand All @@ -303,6 +313,31 @@ func (s *testPlanSuite) TestLogicOpsPushDown(c *C) {
cond: "not(test.t.a)",
exprPB: "\b\xe9\a\x1a\r\b\xc9\x01\x12\b\x80\x00\x00\x00\x00\x00\x00\x01",
},
{
sql: "a xor b",
cond: "xor(test.t.a, test.t.b)",
exprPB: "\b\xff\x11\x1a\r\b\xc9\x01\x12\b\x80\x00\x00\x00\x00\x00\x00\x01\x1a\r\b\xc9\x01\x12\b\x80\x00\x00\x00\x00\x00\x00\x02",
},
{
sql: "a & b",
cond: "bitand(test.t.a, test.t.b)",
exprPB: "\b\xb5\x10\x1a\r\b\xc9\x01\x12\b\x80\x00\x00\x00\x00\x00\x00\x01\x1a\r\b\xc9\x01\x12\b\x80\x00\x00\x00\x00\x00\x00\x02",
},
{
sql: "a | b",
cond: "bitor(test.t.a, test.t.b)",
exprPB: "\b\xb6\x10\x1a\r\b\xc9\x01\x12\b\x80\x00\x00\x00\x00\x00\x00\x01\x1a\r\b\xc9\x01\x12\b\x80\x00\x00\x00\x00\x00\x00\x02",
},
{
sql: "a ^ b",
cond: "bitxor(test.t.a, test.t.b)",
exprPB: "\b\xb7\x10\x1a\r\b\xc9\x01\x12\b\x80\x00\x00\x00\x00\x00\x00\x01\x1a\r\b\xc9\x01\x12\b\x80\x00\x00\x00\x00\x00\x00\x02",
},
{
sql: "~a",
cond: "bitneg(test.t.a)",
exprPB: "\b\xeb\a\x1a\r\b\xc9\x01\x12\b\x80\x00\x00\x00\x00\x00\x00\x01",
},
}
for _, ca := range cases {
sql := "select * from t where " + ca.sql
Expand Down
33 changes: 21 additions & 12 deletions store/localstore/local_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,21 +51,30 @@ func (c *dbClient) SupportRequestType(reqType, subType int64) bool {

func supportExpr(exprType tipb.ExprType) bool {
switch exprType {
case tipb.ExprType_Null, tipb.ExprType_Int64, tipb.ExprType_Uint64, tipb.ExprType_Float32,
tipb.ExprType_Float64, tipb.ExprType_String, tipb.ExprType_Bytes,
tipb.ExprType_MysqlDuration, tipb.ExprType_MysqlDecimal, tipb.ExprType_MysqlTime,
tipb.ExprType_ColumnRef,
tipb.ExprType_And, tipb.ExprType_Or,
tipb.ExprType_LT, tipb.ExprType_LE, tipb.ExprType_EQ, tipb.ExprType_NE,
// data type.
case tipb.ExprType_Null, tipb.ExprType_Int64, tipb.ExprType_Uint64,
tipb.ExprType_Float32, tipb.ExprType_Float64, tipb.ExprType_String,
tipb.ExprType_Bytes, tipb.ExprType_MysqlDuration, tipb.ExprType_MysqlDecimal,
tipb.ExprType_MysqlTime, tipb.ExprType_ColumnRef:
return true
// logic operators.
case tipb.ExprType_And, tipb.ExprType_Or, tipb.ExprType_Not, tipb.ExprType_Xor:
return true
// compare operators.
case tipb.ExprType_LT, tipb.ExprType_LE, tipb.ExprType_EQ, tipb.ExprType_NE,
tipb.ExprType_GE, tipb.ExprType_GT, tipb.ExprType_NullEQ,
tipb.ExprType_In, tipb.ExprType_ValueList,
tipb.ExprType_Not,
tipb.ExprType_Like:
tipb.ExprType_In, tipb.ExprType_ValueList, tipb.ExprType_Like:
return true
// arithmetic operators.
case tipb.ExprType_Plus, tipb.ExprType_Div, tipb.ExprType_Minus,
tipb.ExprType_Mul, tipb.ExprType_IntDiv, tipb.ExprType_Mod:
return true
case tipb.ExprType_Plus, tipb.ExprType_Div, tipb.ExprType_Minus, tipb.ExprType_Mul,
tipb.ExprType_IntDiv, tipb.ExprType_Mod:
// aggregate functions.
case tipb.ExprType_Count, tipb.ExprType_First, tipb.ExprType_Sum,
tipb.ExprType_Avg, tipb.ExprType_Max, tipb.ExprType_Min:
return true
case tipb.ExprType_Count, tipb.ExprType_First, tipb.ExprType_Sum, tipb.ExprType_Avg, tipb.ExprType_Max, tipb.ExprType_Min:
// bitwise operators.
case tipb.ExprType_BitAnd, tipb.ExprType_BitOr, tipb.ExprType_BitXor, tipb.ExprType_BitNeg:
return true
case tipb.ExprType_Case, tipb.ExprType_Coalesce:
return true
Expand Down
Loading

0 comments on commit d38cc3f

Please sign in to comment.