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

refactor builtin #250

Merged
merged 7 commits into from
Sep 23, 2015
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
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
3 changes: 2 additions & 1 deletion expression/between_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (

. "github.com/pingcap/check"

"github.com/pingcap/tidb/expression/builtin"
"github.com/pingcap/tidb/util/types"
)

Expand Down Expand Up @@ -47,7 +48,7 @@ func (s *testBetweenSuite) TestBetween(c *C) {
}

m := map[interface{}]interface{}{
ExprEvalArgAggEmpty: struct{}{},
builtin.ExprEvalArgAggEmpty: struct{}{},
}

for _, t := range table {
Expand Down
3 changes: 2 additions & 1 deletion expression/binop_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (

. "github.com/pingcap/check"

"github.com/pingcap/tidb/expression/builtin"
"github.com/pingcap/tidb/model"
mysql "github.com/pingcap/tidb/mysqldef"
"github.com/pingcap/tidb/parser/opcode"
Expand Down Expand Up @@ -171,7 +172,7 @@ func (s *testBinOpSuite) TestIdentRelOp(c *C) {
}

m := map[interface{}]interface{}{
ExprEvalArgAggEmpty: struct{}{},
builtin.ExprEvalArgAggEmpty: struct{}{},
}

for _, t := range tbl {
Expand Down
54 changes: 29 additions & 25 deletions expression/builtin.go → expression/builtin/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,39 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package expression
package builtin

import (
"fmt"
"strings"
import "github.com/juju/errors"

"github.com/juju/errors"
const (
// ExprEvalFn is the key saving Call expression.
ExprEvalFn = "$fn"
// ExprEvalArgCtx is the key saving Context for a Call expression.
ExprEvalArgCtx = "$ctx"
// ExprAggDone is the key indicating that aggregate function is done.
ExprAggDone = "$aggDone"
// ExprEvalArgAggEmpty is the key to evaluate the aggregate function for empty table.
ExprEvalArgAggEmpty = "$agg0"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$agg0 is not good, rename another value?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is the old code, change it in another PR if possible.

// ExprAggDistinct is the key saving a distinct aggregate.
ExprAggDistinct = "$aggDistinct"
)

var builtin = map[string]struct {
f func([]interface{}, map[interface{}]interface{}) (interface{}, error)
minArgs int
maxArgs int
isStatic bool
isAggregate bool
}{
// Func is for a builtin function.
type Func struct {
// F is the specific calling function.
F func([]interface{}, map[interface{}]interface{}) (interface{}, error)
// MinArgs is the minimal arguments needed,
MinArgs int
// MaxArgs is the maximal arguments needed, -1 for infinity.
MaxArgs int
// IsStatic shows whether this function can be called statically.
IsStatic bool
// IsAggregate represents whether this function is an aggregate function or not.
IsAggregate bool
}

// Funcs holds all registered builtin functions.
var Funcs = map[string]Func{
// common functions
"coalesce": {builtinCoalesce, 1, -1, true, false},

Expand Down Expand Up @@ -83,19 +100,6 @@ var builtin = map[string]struct {
"user": {builtinUser, 0, 0, false, false},
}

func badNArgs(min int, s string, args []interface{}) error {
a := []string{}
for _, v := range args {
a = append(a, fmt.Sprintf("%v", v))
}
switch len(args) < min {
case true:
return errors.Errorf("missing argument to %s(%s)", s, strings.Join(a, ", "))
default: //case false:
return errors.Errorf("too many arguments to %s(%s)", s, strings.Join(a, ", "))
}
}

func invArg(arg interface{}, s string) error {
return errors.Errorf("invalid argument %v (type %T) for %s", arg, arg, s)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,21 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package expression
package builtin

import (
"testing"

. "github.com/pingcap/check"
)

var _ = Suite(&testBuiltinSuite{})

type testBuiltinSuite struct {
func TestT(t *testing.T) {
TestingT(t)
}

func (s *testBuiltinSuite) TestBadNArgs(c *C) {
err := badNArgs(1, "", nil)
c.Assert(err, NotNil)
var _ = Suite(&testBuiltinSuite{})

err = badNArgs(0, "", []interface{}{1})
c.Assert(err, NotNil)
type testBuiltinSuite struct {
}

func (s *testBuiltinSuite) TestCoalesce(c *C) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package expression
package builtin

import "github.com/pingcap/tidb/util/types"

Expand Down
82 changes: 82 additions & 0 deletions expression/builtin/control_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Copyright 2015 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 builtin

import (
"errors"

. "github.com/pingcap/check"
)

func (s *testBuiltinSuite) TestIf(c *C) {
tbl := []struct {
Arg1 interface{}
Arg2 interface{}
Arg3 interface{}
Ret interface{}
}{
{1, 1, 2, 1},
{nil, 1, 2, 2},
{0, 1, 2, 2},
}

for _, t := range tbl {
v, err := builtinIf([]interface{}{t.Arg1, t.Arg2, t.Arg3}, nil)
c.Assert(err, IsNil)
c.Assert(v, DeepEquals, t.Ret)
}

_, err := builtinIf([]interface{}{errors.New("must error"), 1, 2}, nil)
c.Assert(err, NotNil)
}

func (s *testBuiltinSuite) TestIfNull(c *C) {
tbl := []struct {
Arg1 interface{}
Arg2 interface{}
Ret interface{}
}{
{1, 2, 1},
{nil, 2, 2},
{nil, nil, nil},
}

for _, t := range tbl {
v, err := builtinIfNull([]interface{}{t.Arg1, t.Arg2}, nil)
c.Assert(err, IsNil)
c.Assert(v, DeepEquals, t.Ret)
}
}

func (s *testBuiltinSuite) TestNullIf(c *C) {
tbl := []struct {
Arg1 interface{}
Arg2 interface{}
Ret interface{}
}{
{1, 1, nil},
{nil, 2, nil},
{1, nil, 1},
{1, 2, 1},
}

for _, t := range tbl {
v, err := builtinNullIf([]interface{}{t.Arg1, t.Arg2}, nil)
c.Assert(err, IsNil)
c.Assert(v, DeepEquals, t.Ret)
}

_, err := builtinNullIf([]interface{}{errors.New("must error"), 1}, nil)
c.Assert(err, NotNil)
}
48 changes: 18 additions & 30 deletions expression/builtin_groupby.go → expression/builtin/groupby.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package expression
package builtin

import (
"bytes"
Expand All @@ -30,18 +30,21 @@ import (

// see https://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html

type aggregateDistinct struct {
// now we have to use memkv Temp, later may be use map directly
// AggregateDistinct handles distinct data for aggregate function: count, sum, avg, and group_concat.
type AggregateDistinct struct {
// Distinct is a memory key-value map.
// Now we have to use memkv Temp, later may be use map directly
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment end of .
Line 31, use See: xxxx

Distinct memkv.Temp
}

func (c *Call) createDistinct() *aggregateDistinct {
a := new(aggregateDistinct)
// CreateAggregateDistinct creates a distinct for function f.
func CreateAggregateDistinct(f string, distinct bool) *AggregateDistinct {
a := &AggregateDistinct{}

switch strings.ToLower(c.F) {
switch strings.ToLower(f) {
case "count", "sum", "avg", "group_concat":
// only these aggregate functions support distinct
if c.Distinct {
if distinct {
a.Distinct, _ = memkv.CreateTemp(true)
}
}
Expand All @@ -50,7 +53,7 @@ func (c *Call) createDistinct() *aggregateDistinct {
}

// check whether v is distinct or not, return true for distinct
func (a *aggregateDistinct) isDistinct(v ...interface{}) (bool, error) {
func (a *AggregateDistinct) isDistinct(v ...interface{}) (bool, error) {
// no distinct flag
if a.Distinct == nil {
return true, nil
Expand All @@ -74,7 +77,7 @@ func (a *aggregateDistinct) isDistinct(v ...interface{}) (bool, error) {
return true, nil
}

func (a *aggregateDistinct) clear() {
func (a *AggregateDistinct) clear() {
if a.Distinct == nil {
return
}
Expand All @@ -86,30 +89,15 @@ func (a *aggregateDistinct) clear() {
a.Distinct, _ = memkv.CreateTemp(true)
}

func getDistinct(ctx map[interface{}]interface{}, fn interface{}) *aggregateDistinct {
c, ok := fn.(*Call)
func getDistinct(ctx map[interface{}]interface{}, fn interface{}) *AggregateDistinct {
v, ok := ctx[ExprAggDistinct]
if !ok {
// if fn is not a Call, maybe error
// but now we just return a dummpy aggregate distinct
return new(aggregateDistinct)
// here maybe an error, but now we just return a dummpy aggregate distinct
return new(AggregateDistinct)
}

// we may have multi aggregate function in one query
// e.g, select sum(c1) + count(*) from t
// so here we use a map to keep all aggregate disctinct
m := map[interface{}]interface{}{}
if v, ok := ctx[ExprAggDistinct]; ok {
m = v.(map[interface{}]interface{})
}

if d, ok := m[c]; ok {
return d.(*aggregateDistinct)
}

d := c.createDistinct()
m[c] = d

ctx[ExprAggDistinct] = m
// must be AggregateDistinct
d := v.(*AggregateDistinct)
return d
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package expression
package builtin

import (
. "github.com/pingcap/check"

mysql "github.com/pingcap/tidb/mysqldef"
"github.com/pingcap/tidb/util/types"
)
Expand Down Expand Up @@ -54,22 +53,23 @@ func (s *testBuiltinSuite) TestGroupBy(c *C) {
}

for _, t := range tbl {
// create a call and use dummy args.
e, err := NewCall(t.F, []Expression{Value{nil}}, t.Distinct)
c.Assert(err, IsNil)

call, ok := e.(*Call)
f, ok := Funcs[t.F]
c.Assert(ok, IsTrue)

m := map[interface{}]interface{}{}

m[ExprEvalFn] = new(Func)
m[ExprAggDistinct] = CreateAggregateDistinct(t.F, t.Distinct)

for _, arg := range t.RoundArgs {
call.Args = []Expression{Value{arg}}
args := []interface{}{arg}

_, err = call.Eval(nil, m)
_, err := f.F(args, m)
c.Assert(err, IsNil)
}

m[ExprAggDone] = struct{}{}
v, err := e.Eval(nil, m)
v, err := f.F(nil, m)
c.Assert(err, IsNil)
switch v.(type) {
case nil:
Expand Down Expand Up @@ -98,23 +98,24 @@ func (s *testBuiltinSuite) TestGroupConcat(c *C) {
{[]interface{}{1, nil, 1}, true, "1"},
}

f := builtinGroupConcat

for _, t := range tbl {
// create a call and use dummy args.
e, err := NewCall("group_concat", []Expression{Value{nil}}, t.Distinct)
c.Assert(err, IsNil)

call, ok := e.(*Call)
c.Assert(ok, IsTrue)

m := map[interface{}]interface{}{}
m[ExprEvalFn] = new(Func)
m[ExprAggDistinct] = CreateAggregateDistinct("group_concat", t.Distinct)

for _, arg := range t.RoundArgs {
call.Args = []Expression{Value{arg}}
args := []interface{}{arg}

_, err = call.Eval(nil, m)
_, err := f(args, m)
c.Assert(err, IsNil)
}

m[ExprAggDone] = struct{}{}
v, err := e.Eval(nil, m)
v, err := f(nil, m)
c.Assert(err, IsNil)
c.Assert(v, DeepEquals, t.Ret)
}
Expand Down
Loading