Skip to content

Commit

Permalink
Merge pull request #250 from pingcap/siddontang/refactor-builtin
Browse files Browse the repository at this point in the history
refactor builtin
  • Loading branch information
qiuyesuifeng committed Sep 23, 2015
2 parents 413995a + 6d5b187 commit 2ca339d
Show file tree
Hide file tree
Showing 23 changed files with 232 additions and 189 deletions.
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"
// 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)
}
52 changes: 20 additions & 32 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 @@ -28,29 +28,32 @@ import (
"github.com/pingcap/tidb/util/types"
)

// see https://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html
// 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
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)
}
}

return a
}

// check whether v is distinct or not, return true for distinct
func (a *aggregateDistinct) isDistinct(v ...interface{}) (bool, error) {
// Check whether v is distinct or not, return true for distinct
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
Loading

0 comments on commit 2ca339d

Please sign in to comment.