Merge branch 'master' into qiuyesuifeng/exists-subquery

This commit is contained in:
qiuyesuifeng
2015-09-14 16:54:46 +08:00
12 changed files with 943 additions and 147 deletions

View File

@ -36,8 +36,8 @@ parser:
sed -i -e 's|//line.*||' -e 's/yyEofCode/yyEOFCode/' parser/parser.go; \
elif [ $(ARCH) = $(MAC) ]; \
then \
sed -i "" 's|//line.*||' parser/parser.go; \
sed -i "" 's/yyEofCode/yyEOFCode/' parser/parser.go; \
/usr/bin/sed -i "" 's|//line.*||' parser/parser.go; \
/usr/bin/sed -i "" 's/yyEofCode/yyEOFCode/' parser/parser.go; \
fi
golex -o parser/scanner.go parser/scanner.l

View File

@ -21,7 +21,6 @@ import (
"encoding/json"
"fmt"
"strings"
"sync"
"github.com/juju/errors"
"github.com/ngaut/log"
@ -63,7 +62,6 @@ type DDL interface {
type ddl struct {
store kv.Storage
mu sync.Mutex
infoHandle *infoschema.Handle
}

View File

@ -416,20 +416,16 @@ func (o *BinaryOperation) evalPlus(a interface{}, b interface{}) (interface{}, e
case int64:
switch y := b.(type) {
case int64:
return x + y, nil
return types.AddInt64(x, y)
case uint64:
// For MySQL, if any is unsigned, return unsigned
// TODO: check overflow
return uint64(x) + y, nil
return types.AddInteger(y, x)
}
case uint64:
switch y := b.(type) {
case int64:
// For MySQL, if any is unsigned, return unsigned
// TODO: check overflow
return x + uint64(y), nil
return types.AddInteger(x, y)
case uint64:
return x + y, nil
return types.AddUint64(x, y)
}
case float64:
switch y := b.(type) {
@ -452,21 +448,16 @@ func (o *BinaryOperation) evalMinus(a interface{}, b interface{}) (interface{},
case int64:
switch y := b.(type) {
case int64:
return x - y, nil
return types.SubInt64(x, y)
case uint64:
// For MySQL, if any is unsigned, return unsigned
// TODO: check overflow
return uint64(x) - y, nil
return types.SubIntWithUint(x, y)
}
case uint64:
switch y := b.(type) {
case int64:
// TODO: check overflow
return x - uint64(y), nil
return types.SubUintWithInt(x, y)
case uint64:
// For MySQL, if any is unsigned, return unsigned
// TODO: check overflow
return x - y, nil
return types.SubUint64(x, y)
}
case float64:
switch y := b.(type) {
@ -489,22 +480,16 @@ func (o *BinaryOperation) evalMul(a interface{}, b interface{}) (interface{}, er
case int64:
switch y := b.(type) {
case int64:
return x * y, nil
return types.MulInt64(x, y)
case uint64:
// For MySQL, if any is unsigned, return unsigned
// TODO: check overflow and negative number
// if a negative int64 * uint64, MySQL may throw "BIGINT UNSIGNED value is out of range" error
// we skip it now and handle it later.
return uint64(x) * y, nil
return types.MulInteger(y, x)
}
case uint64:
switch y := b.(type) {
case int64:
// For MySQL, if any is unsigned, return unsigned
// TODO: check overflow
return x * uint64(y), nil
return types.MulInteger(x, y)
case uint64:
return x * y, nil
return types.MulUint64(x, y)
}
case float64:
switch y := b.(type) {

View File

@ -27,15 +27,6 @@ import (
"github.com/pingcap/tidb/sessionctx/variable"
)
// Builin functions entry key with name conflict with keywords.
const (
// BuiltinFuncDatabase is the keyword for Database function.
BuiltinFuncDatabase = "database"
// BuiltinFuncIf is the keyword for If function.
BuiltinFuncIf = "if"
BuiltinFuncLeft = "left"
)
var builtin = map[string]struct {
f func([]interface{}, map[interface{}]interface{}) (interface{}, error)
minArgs int
@ -44,8 +35,8 @@ var builtin = map[string]struct {
isAggregate bool
}{
// common functions
BuiltinFuncDatabase: {builtinDatabase, 0, 0, false, false},
"coalesce": {builtinCoalesce, 1, -1, true, false},
"database": {builtinDatabase, 0, 0, false, false},
"coalesce": {builtinCoalesce, 1, -1, true, false},
// math functions
"abs": {builtinAbs, 1, 1, true, false},
@ -77,16 +68,16 @@ var builtin = map[string]struct {
"yearweek": {builtinYearWeek, 1, 2, true, false},
// control functions
BuiltinFuncIf: {builtinIf, 3, 3, true, false},
"ifnull": {builtinIfNull, 2, 2, true, false},
"nullif": {builtinNullIf, 2, 2, true, false},
"if": {builtinIf, 3, 3, true, false},
"ifnull": {builtinIfNull, 2, 2, true, false},
"nullif": {builtinNullIf, 2, 2, true, false},
// string functions
"concat": {builtinConcat, 1, -1, true, false},
"concat_ws": {builtinConcatWS, 2, -1, true, false},
BuiltinFuncLeft: {builtinLeft, 2, 2, true, false},
"length": {builtinLength, 1, 1, true, false},
"repeat": {builtinRepeat, 2, 2, true, false},
"concat": {builtinConcat, 1, -1, true, false},
"concat_ws": {builtinConcatWS, 2, -1, true, false},
"left": {builtinLeft, 2, 2, true, false},
"length": {builtinLength, 1, 1, true, false},
"repeat": {builtinRepeat, 2, 2, true, false},
// information functions
"found_rows": {builtinFoundRows, 0, 0, false, false},

View File

@ -347,11 +347,6 @@ func staticExpr(e expression.Expression) (expression.Expression, error) {
return e, nil
}
type exprTab struct {
expr expression.Expression
table string
}
// IsCurrentTimeExpr returns whether e is CurrentTimeExpr.
func IsCurrentTimeExpr(e expression.Expression) bool {
x, ok := e.(*Ident)

View File

@ -58,11 +58,11 @@ import (
/*yy:token "1.%d" */ floatLit "floating-point literal"
/*yy:token "%c" */ identifier "identifier"
/*yy:token "%di" */ imaginaryLit "imaginary literal"
/*yy:token "%d" */ intLit "integer literal"
/*yy:token "\"%c\"" */ stringLit "string literal"
abs "ABS"
add "ADD"
after "AFTER"
all "ALL"
@ -74,6 +74,7 @@ import (
as "AS"
asc "ASC"
autoIncrement "AUTO_INCREMENT"
avg "AVG"
begin "BEGIN"
between "BETWEEN"
by "BY"
@ -82,16 +83,24 @@ import (
cast "CAST"
character "CHARACTER"
charsetKwd "CHARSET"
coalesce "COALESCE"
collation "COLLATE"
column "COLUMN"
columns "COLUMNS"
commit "COMMIT"
concat "CONCAT"
concatWs "CONCAT_WS"
constraint "CONSTRAINT"
convert "CONVERT"
count "COUNT"
create "CREATE"
cross "CROSS"
database "DATABASE"
databases "DATABASES"
day "DAY"
dayofmonth "DAYOFMONTH"
dayofweek "DAYOFWEEK"
dayofyear "DAYOFYEAR"
deallocate "DEALLOCATE"
defaultKwd "DEFAULT"
delayed "DELAYED"
@ -115,16 +124,20 @@ import (
first "FIRST"
foreign "FOREIGN"
forKwd "FOR"
foundRows "FOUND_ROWS"
from "FROM"
full "FULL"
fulltext "FULLTEXT"
ge ">="
global "GLOBAL"
group "GROUP"
groupConcat "GROUP_CONCAT"
having "HAVING"
highPriority "HIGH_PRIORITY"
hour "HOUR"
ignore "IGNORE"
ifKwd "IF"
ifNull "IFNULL"
in "IN"
index "INDEX"
inner "INNER"
@ -135,19 +148,26 @@ import (
key "KEY"
le "<="
left "LEFT"
length "LENGTH"
like "LIKE"
limit "LIMIT"
local "LOCAL"
lock "LOCK"
lowPriority "LOW_PRIORITY"
lsh "<<"
max "MAX"
microsecond "MICROSECOND"
min "MIN"
minute "MINUTE"
mod "MOD"
mode "MODE"
month "MONTH"
names "NAMES"
neq "!="
neqSynonym "<>"
not "NOT"
null "NULL"
nullIf "NULLIF"
offset "OFFSET"
on "ON"
or "OR"
@ -161,14 +181,15 @@ import (
quick "QUICK"
references "REFERENCES"
regexp "REGEXP"
repeat "REPEAT"
right "RIGHT"
rlike "RLIKE"
rollback "ROLLBACK"
row "ROW"
rsh ">>"
runeType "rune"
schema "SCHEMA"
schemas "SCHEMAS"
second "SECOND"
selectKwd "SELECT"
session "SESSION"
set "SET"
@ -179,6 +200,7 @@ import (
start "START"
stringType "string"
substring "SUBSTRING"
sum "SUM"
sysVar "SYS_VAR"
tableKwd "TABLE"
tables "TABLES"
@ -198,9 +220,13 @@ import (
values "VALUES"
variables "VARIABLES"
warnings "WARNINGS"
week "WEEK"
weekday "WEEKDAY"
weekofyear "WEEKOFYEAR"
when "WHEN"
where "WHERE"
xor "XOR"
yearweek "YEARWEEK"
zerofill "ZEROFILL"
calcFoundRows "SQL_CALC_FOUND_ROWS"
@ -262,7 +288,6 @@ import (
parseExpression "parse expression prefix"
%type <item>
AggAllOpt "All option in aggregate function"
AlterTableStmt "Alter table statement"
AlterSpecification "Alter table specification"
AlterSpecificationList "Alter table specification list"
@ -307,6 +332,7 @@ import (
DefaultKwdOpt "optional DEFAULT keyword"
DefaultValueExpr "DefaultValueExpr(Now or Signed Literal)"
DeleteFromStmt "DELETE FROM statement"
DistinctOpt "Distinct option"
DoStmt "Do statement"
DropDatabaseStmt "DROP DATABASE statement"
DropIndexStmt "DROP INDEX statement"
@ -328,8 +354,11 @@ import (
FieldList "field expression list"
FromClause "From clause"
Function "function expr"
FunctionCall "function call post part"
FunctionCallArgList "function call optional argument list"
FunctionCallAgg "Function call on aggregate data"
FunctionCallConflict "Function call with reserved keyword as function name"
FunctionCallKeyword "Function call with keyword as function name"
FunctionCallNonKeyword "Function call with nonkeyword as function name"
FunctionNameConflict "Built-in function call names which are conflict with keywords"
GlobalScope "The scope of variable"
GroupByClause "GROUP BY clause"
GroupByList "GROUP BY list"
@ -463,7 +492,6 @@ import (
NUM "numbers"
LengthNum "Field length num(uint64)"
FunctionNameConflict "Built-in function call names which are conflict with keywords"
%token tableRefPriority
@ -471,6 +499,9 @@ import (
%precedence lowerThanCalcFoundRows
%precedence calcFoundRows
%precedence lowerThanInsertValues
%precedence insertValues
%left join inner cross left right full
/* A dummy token to force the priority of TableRef production in a join. */
%left tableRefPriority
@ -657,39 +688,6 @@ BeginTransactionStmt:
$$ = &stmts.BeginStmt{}
}
FunctionCall:
'(' AggAllOpt FunctionCallArgList ')'
{
$$ = []interface{}{false, $3}
}
| '(' "DISTINCT" AggAllOpt ExpressionList')'
{
/* Distinct must have expression list, can not empty and '*' */
$$ = []interface{}{true, $4}
}
AggAllOpt:
{
}
| "ALL"
{
/* TODO: not all functions support ALL, so later we will distinguish to handle them */
/* ALL has nothing to do. */
}
FunctionCallArgList:
/* EMPTY */
{
$$ = []expression.Expression{}
}
/* select count(*) from table */
| '*'
{
$$ = []expression.Expression{ expressions.Value{Val: expressions.TypeStar("*")} }
}
| ExpressionList
ColumnDef:
ColumnName Type ConstraintOpts
{
@ -1593,14 +1591,16 @@ Identifier:
identifier | UnReservedKeyword | NotKeywordToken
UnReservedKeyword:
"AUTO_INCREMENT" | "AFTER" | "BEGIN" | "BIT" | "BOOL" | "BOOLEAN" | "CHARSET" | "COLUMNS" | "COMMIT"
"AUTO_INCREMENT" | "AFTER" | "AVG" | "BEGIN" | "BIT" | "BOOL" | "BOOLEAN" | "CHARSET" | "COLUMNS" | "COMMIT"
| "DATE" | "DATETIME" | "DEALLOCATE" | "DO" | "END" | "ENGINE" | "ENGINES" | "EXECUTE" | "FIRST" | "FULL"
| "LOCAL" | "NAMES" | "OFFSET" | "PASSWORD" %prec lowerThanEq | "PREPARE" | "QUICK" | "ROLLBACK" | "SESSION" | "SIGNED"
| "START" | "GLOBAL" | "TABLES"| "TEXT" | "TIME" | "TIMESTAMP" | "TRANSACTION" | "TRUNCATE" | "UNKNOWN"
| "VALUE" | "WARNINGS" | "YEAR" | "NOW" | "MODE"
| "VALUE" | "WARNINGS" | "YEAR" | "MODE" | "WEEK" | "ANY" | "SOME"
NotKeywordToken:
"SQL_CALC_FOUND_ROWS" | "SUBSTRING" %prec lowerThanLeftParen
"ABS" | "COALESCE" | "CONCAT" | "CONCAT_WS" | "COUNT" | "DAY" | "DAYOFMONTH" | "DAYOFWEEK" | "DAYOFYEAR" | "FOUND_ROWS" | "GROUP_CONCAT"
| "HOUR" | "IFNULL" | "LENGTH" | "MAX" | "MICROSECOND" | "MIN" | "MINUTE" | "NULLIF" | "MONTH" | "NOW" | "SECOND" | "SQL_CALC_FOUND_ROWS"
| "SUBSTRING" %prec lowerThanLeftParen | "SUM" | "WEEKDAY" | "WEEKOFYEAR" | "YEARWEEK"
/************************************************************************************
*
@ -1641,7 +1641,7 @@ InsertRest:
{
$$ = &stmts.InsertIntoStmt{ColNames: $2.([]string), Sel: $4.(*stmts.SelectStmt)}
}
| ValueSym ExpressionListList
| ValueSym ExpressionListList %prec insertValues
{
$$ = &stmts.InsertIntoStmt{Lists: $2.([][]expression.Expression)}
}
@ -1723,7 +1723,6 @@ Literal:
$$ = int64(1)
}
| floatLit
| imaginaryLit
| intLit
| stringLit
@ -1844,30 +1843,51 @@ PrimaryExpression:
}
Function:
PrimaryExpression FunctionCall
FunctionCallKeyword
| FunctionCallNonKeyword
| FunctionCallConflict
| FunctionCallAgg
FunctionNameConflict:
"DATABASE" | "SCHEMA" | "IF" | "LEFT" | "REPEAT"
FunctionCallConflict:
FunctionNameConflict '(' ExpressionList ')'
{
x := yylex.(*lexer)
f, ok := $1.(*expressions.Ident)
if !ok {
x.err("", "expected identifier or qualified identifier")
return 1
}
var err error
args := $2.([]interface{})
if $$, err = expressions.NewCall(f.O, args[1].([]expression.Expression), args[0].(bool)); err != nil {
$$, err = expressions.NewCall($1.(string), $3.([]expression.Expression), false)
if err != nil {
x.err("", "%v", err)
return 1
}
}
| FunctionNameConflict FunctionCall
DistinctOpt:
{
$$ = false
}
| "ALL"
{
$$ = false
}
| "DISTINCT"
{
$$ = true
}
| "DISTINCT" "ALL"
{
$$ = true
}
FunctionCallKeyword:
"AVG" '(' DistinctOpt ExpressionList ')'
{
x := yylex.(*lexer)
var err error
args := $2.([]interface{})
$$, err = expressions.NewCall($1.(string), args[1].([]expression.Expression), false)
$$, err = expressions.NewCall($1.(string), $4.([]expression.Expression), $3.(bool))
if err != nil {
x.err("", "%v", err)
l := yylex.(*lexer)
l.err("", "%v", err)
return 1
}
}
@ -1890,11 +1910,6 @@ Function:
}
$$ = x
}
| "VALUES" '(' Identifier ')'
{
// TODO: support qualified identifier for column_name
$$ = &expressions.Values{CIStr: model.NewCIStr($3.(string))}
}
| "CONVERT" '(' Expression "USING" CharsetName ')'
{
// See: https://dev.mysql.com/doc/refman/5.7/en/cast-functions.html#function_convert
@ -1912,6 +1927,237 @@ Function:
IsConvert: true,
}
}
| "DATE" '(' Expression ')'
{
args := []expression.Expression{$3.(expression.Expression)}
var err error
$$, err = expressions.NewCall($1.(string), args, false)
if err != nil {
l := yylex.(*lexer)
l.err("", "%v", err)
return 1
}
}
| "VALUES" '(' Identifier ')' %prec lowerThanInsertValues
{
// TODO: support qualified identifier for column_name
$$ = &expressions.Values{CIStr: model.NewCIStr($3.(string))}
}
| "WEEK" '(' ExpressionList ')'
{
var err error
$$, err = expressions.NewCall($1.(string), $3.([]expression.Expression), false)
if err != nil {
l := yylex.(*lexer)
l.err("", "%v", err)
return 1
}
}
| "YEAR" '(' Expression ')'
{
args := []expression.Expression{$3.(expression.Expression)}
var err error
$$, err = expressions.NewCall($1.(string), args, false)
if err != nil {
l := yylex.(*lexer)
l.err("", "%v", err)
return 1
}
}
FunctionCallNonKeyword:
"COALESCE" '(' ExpressionList ')'
{
var err error
$$, err = expressions.NewCall($1.(string), $3.([]expression.Expression), false)
if err != nil {
l := yylex.(*lexer)
l.err("", "%v", err)
return 1
}
}
| "ABS" '(' Expression ')'
{
args := []expression.Expression{$3.(expression.Expression)}
var err error
$$, err = expressions.NewCall($1.(string), args, false)
if err != nil {
l := yylex.(*lexer)
l.err("", "%v", err)
return 1
}
}
| "CONCAT" '(' ExpressionList ')'
{
var err error
$$, err = expressions.NewCall($1.(string), $3.([]expression.Expression), false)
if err != nil {
l := yylex.(*lexer)
l.err("", "%v", err)
return 1
}
}
| "CONCAT_WS" '(' ExpressionList ')'
{
var err error
$$, err = expressions.NewCall($1.(string), $3.([]expression.Expression), false)
if err != nil {
l := yylex.(*lexer)
l.err("", "%v", err)
return 1
}
}
| "DAY" '(' Expression ')'
{
args := []expression.Expression{$3.(expression.Expression)}
var err error
$$, err = expressions.NewCall($1.(string), args, false)
if err != nil {
l := yylex.(*lexer)
l.err("", "%v", err)
return 1
}
}
| "DAYOFWEEK" '(' Expression ')'
{
args := []expression.Expression{$3.(expression.Expression)}
var err error
$$, err = expressions.NewCall($1.(string), args, false)
if err != nil {
l := yylex.(*lexer)
l.err("", "%v", err)
return 1
}
}
| "DAYOFMONTH" '(' Expression ')'
{
args := []expression.Expression{$3.(expression.Expression)}
var err error
$$, err = expressions.NewCall($1.(string), args, false)
if err != nil {
l := yylex.(*lexer)
l.err("", "%v", err)
return 1
}
}
| "DAYOFYEAR" '(' Expression ')'
{
args := []expression.Expression{$3.(expression.Expression)}
var err error
$$, err = expressions.NewCall($1.(string), args, false)
if err != nil {
l := yylex.(*lexer)
l.err("", "%v", err)
return 1
}
}
| "FOUND_ROWS" '(' ')'
{
args := []expression.Expression{}
var err error
$$, err = expressions.NewCall($1.(string), args, false)
if err != nil {
l := yylex.(*lexer)
l.err("", "%v", err)
return 1
}
}
| "HOUR" '(' Expression ')'
{
args := []expression.Expression{$3.(expression.Expression)}
var err error
$$, err = expressions.NewCall($1.(string), args, false)
if err != nil {
l := yylex.(*lexer)
l.err("", "%v", err)
return 1
}
}
| "IFNULL" '(' ExpressionList ')'
{
var err error
$$, err = expressions.NewCall($1.(string), $3.([]expression.Expression), false)
if err != nil {
l := yylex.(*lexer)
l.err("", "%v", err)
return 1
}
}
| "LENGTH" '(' Expression ')'
{
args := []expression.Expression{$3.(expression.Expression)}
var err error
$$, err = expressions.NewCall($1.(string), args, false)
if err != nil {
l := yylex.(*lexer)
l.err("", "%v", err)
return 1
}
}
| "MICROSECOND" '(' Expression ')'
{
args := []expression.Expression{$3.(expression.Expression)}
var err error
$$, err = expressions.NewCall($1.(string), args, false)
if err != nil {
l := yylex.(*lexer)
l.err("", "%v", err)
return 1
}
}
| "MINUTE" '(' Expression ')'
{
args := []expression.Expression{$3.(expression.Expression)}
var err error
$$, err = expressions.NewCall($1.(string), args, false)
if err != nil {
l := yylex.(*lexer)
l.err("", "%v", err)
return 1
}
}
| "MONTH" '(' Expression ')'
{
args := []expression.Expression{$3.(expression.Expression)}
var err error
$$, err = expressions.NewCall($1.(string), args, false)
if err != nil {
l := yylex.(*lexer)
l.err("", "%v", err)
return 1
}
}
| "NOW" '(' ExpressionList ')'
{
var err error
$$, err = expressions.NewCall($1.(string), $3.([]expression.Expression),false)
if err != nil {
l := yylex.(*lexer)
l.err("", "%v", err)
return 1
}
}
| "NULLIF" '(' ExpressionList ')'
{
var err error
$$, err = expressions.NewCall($1.(string), $3.([]expression.Expression), false)
if err != nil {
l := yylex.(*lexer)
l.err("", "%v", err)
return 1
}
}
| "SECOND" '(' Expression ')'
{
args := []expression.Expression{$3.(expression.Expression)}
var err error
$$, err = expressions.NewCall($1.(string), args, false)
if err != nil {
l := yylex.(*lexer)
l.err("", "%v", err)
return 1
}
}
| "SUBSTRING" '(' Expression ',' Expression ')'
{
$$ = &expressions.FunctionSubstring{
@ -1942,7 +2188,104 @@ Function:
Len: $7.(expression.Expression),
}
}
| "WEEKDAY" '(' Expression ')'
{
args := []expression.Expression{$3.(expression.Expression)}
var err error
$$, err = expressions.NewCall($1.(string), args, false)
if err != nil {
l := yylex.(*lexer)
l.err("", "%v", err)
return 1
}
}
| "WEEKOFYEAR" '(' Expression ')'
{
args := []expression.Expression{$3.(expression.Expression)}
var err error
$$, err = expressions.NewCall($1.(string), args, false)
if err != nil {
l := yylex.(*lexer)
l.err("", "%v", err)
return 1
}
}
| "YEARWEEK" '(' ExpressionList ')'
{
var err error
$$, err = expressions.NewCall($1.(string), $3.([]expression.Expression),false)
if err != nil {
l := yylex.(*lexer)
l.err("", "%v", err)
return 1
}
}
FunctionCallAgg:
"COUNT" '(' DistinctOpt ExpressionList ')'
{
var err error
$$, err = expressions.NewCall($1.(string), $4.([]expression.Expression), $3.(bool))
if err != nil {
l := yylex.(*lexer)
l.err("", "%v", err)
return 1
}
}
| "COUNT" '(' DistinctOpt '*' ')'
{
var err error
args := []expression.Expression{ expressions.Value{Val: expressions.TypeStar("*")} }
$$, err = expressions.NewCall($1.(string), args, $3.(bool))
if err != nil {
l := yylex.(*lexer)
l.err("", "%v", err)
return 1
}
}
| "GROUP_CONCAT" '(' DistinctOpt ExpressionList ')'
{
var err error
$$, err = expressions.NewCall($1.(string), $4.([]expression.Expression),$3.(bool))
if err != nil {
l := yylex.(*lexer)
l.err("", "%v", err)
return 1
}
}
| "MAX" '(' DistinctOpt Expression ')'
{
args := []expression.Expression{$4.(expression.Expression)}
var err error
$$, err = expressions.NewCall($1.(string), args, $3.(bool))
if err != nil {
l := yylex.(*lexer)
l.err("", "%v", err)
return 1
}
}
| "MIN" '(' DistinctOpt Expression ')'
{
args := []expression.Expression{$4.(expression.Expression)}
var err error
$$, err = expressions.NewCall($1.(string), args, $3.(bool))
if err != nil {
l := yylex.(*lexer)
l.err("", "%v", err)
return 1
}
}
| "SUM" '(' DistinctOpt Expression ')'
{
args := []expression.Expression{$4.(expression.Expression)}
var err error
$$, err = expressions.NewCall($1.(string), args, $3.(bool))
if err != nil {
l := yylex.(*lexer)
l.err("", "%v", err)
return 1
}
}
ExpressionOpt:
{
@ -2035,24 +2378,6 @@ CastType:
}
FunctionNameConflict:
"DATABASE"
{
$$ = expressions.BuiltinFuncDatabase
}
| "SCHEMA"
{
$$ = expressions.BuiltinFuncDatabase
}
| "IF"
{
$$ = expressions.BuiltinFuncIf
}
| "LEFT"
{
$$ = expressions.BuiltinFuncLeft
}
PrimaryFactor:
PrimaryFactor '|' PrimaryFactor %prec '|'
{

View File

@ -317,7 +317,7 @@ func (s *testParserSuite) TestParser0(c *C) {
"date", "datetime", "deallocate", "do", "end", "engine", "engines", "execute", "first", "full",
"local", "names", "offset", "password", "prepare", "quick", "rollback", "session", "signed",
"start", "global", "tables", "text", "time", "timestamp", "transaction", "truncate", "unknown",
"value", "warnings", "year", "now", "substring", "mode",
"value", "warnings", "year", "now", "substring", "mode", "any", "some",
}
for _, kw := range unreservedKws {
src := fmt.Sprintf("SELECT %s FROM tbl;", kw)

View File

@ -224,6 +224,7 @@ x [xX]
y [yY]
z [zZ]
abs {a}{b}{s}
add {a}{d}{d}
after {a}{f}{t}{e}{r}
all {a}{l}{l}
@ -233,6 +234,7 @@ any {a}{n}{y}
as {a}{s}
asc {a}{s}{c}
auto_increment {a}{u}{t}{o}_{i}{n}{c}{r}{e}{m}{e}{n}{t}
avg {a}{v}{g}
begin {b}{e}{g}{i}{n}
between {b}{e}{t}{w}{e}{e}{n}
by {b}{y}
@ -240,16 +242,24 @@ case {c}{a}{s}{e}
cast {c}{a}{s}{t}
character {c}{h}{a}{r}{a}{c}{t}{e}{r}
charset {c}{h}{a}{r}{s}{e}{t}
coalesce {c}{o}{a}{l}{e}{s}{c}{e}
collate {c}{o}{l}{l}{a}{t}{e}
column {c}{o}{l}{u}{m}{n}
columns {c}{o}{l}{u}{m}{n}{s}
commit {c}{o}{m}{m}{i}{t}
concat {c}{o}{n}{c}{a}{t}
concat_ws {c}{o}{n}{c}{a}{t}_{w}{s}
constraint {c}{o}{n}{s}{t}{r}{a}{i}{n}{t}
convert {c}{o}{n}{v}{e}{r}{t}
count {c}{o}{u}{n}{t}
create {c}{r}{e}{a}{t}{e}
cross {c}{r}{o}{s}{s}
database {d}{a}{t}{a}{b}{a}{s}{e}
databases {d}{a}{t}{a}{b}{a}{s}{e}{s}
day {d}{a}{y}
dayofweek {d}{a}{y}{o}{f}{w}{e}{e}{k}
dayofmonth {d}{a}{y}{o}{f}{m}{o}{n}{t}{h}
dayofyear {d}{a}{y}{o}{f}{y}{e}{a}{r}
deallocate {d}{e}{a}{l}{l}{o}{c}{a}{t}{e}
default {d}{e}{f}{a}{u}{l}{t}
delayed {d}{e}{l}{a}{y}{e}{d}
@ -271,14 +281,18 @@ explain {e}{x}{p}{l}{a}{i}{n}
first {f}{i}{r}{s}{t}
for {f}{o}{r}
foreign {f}{o}{r}{e}{i}{g}{n}
found_rows {f}{o}{u}{n}{d}_{r}{o}{w}{s}
from {f}{r}{o}{m}
full {f}{u}{l}{l}
fulltext {f}{u}{l}{l}{t}{e}{x}{t}
global {g}{l}{o}{b}{a}{l}
group {g}{r}{o}{u}{p}
group_concat {g}{r}{o}{u}{p}_{c}{o}{n}{c}{a}{t}
having {h}{a}{v}{i}{n}{g}
high_priority {h}{i}{g}{h}_{p}{r}{i}{o}{r}{i}{t}{y}
hour {h}{o}{u}{r}
if {i}{f}
ifnull {i}{f}{n}{u}{l}{l}
ignore {i}{g}{n}{o}{r}{e}
in {i}{n}
index {i}{n}{d}{e}{x}
@ -289,13 +303,17 @@ is {i}{s}
join {j}{o}{i}{n}
key {k}{e}{y}
left {l}{e}{f}{t}
length {l}{e}{n}{g}{t}{h}
like {l}{i}{k}{e}
limit {l}{i}{m}{i}{t}
local {l}{o}{c}{a}{l}
lock {l}{o}{c}{k}
low_priority {l}{o}{w}_{p}{r}{i}{o}{r}{i}{t}{y}
microsecond {m}{i}{c}{r}{o}{s}{e}{c}{o}{n}{d}
minute {m}{i}{n}{u}{t}{e}
mod {m}{o}{d}
mode {m}{o}{d}{e}
month {m}{o}{n}{t}{h}
names {n}{a}{m}{e}{s}
not {n}{o}{t}
offset {o}{f}{f}{s}{e}{t}
@ -307,6 +325,7 @@ password {p}{a}{s}{s}{w}{o}{r}{d}
prepare {p}{r}{e}{p}{a}{r}{e}
primary {p}{r}{i}{m}{a}{r}{y}
quick {q}{u}{i}{c}{k}
repeat {r}{e}{p}{e}{a}{t}
references {r}{e}{f}{e}{r}{e}{n}{c}{e}{s}
regexp {r}{e}{g}{e}{x}{p}
right {r}{i}{g}{h}{t}
@ -315,6 +334,7 @@ rollback {r}{o}{l}{l}{b}{a}{c}{k}
row {r}{o}{w}
schema {s}{c}{h}{e}{m}{a}
schemas {s}{c}{h}{e}{m}{a}{s}
second {s}{e}{c}{o}{n}{d}
select {s}{e}{l}{e}{c}{t}
session {s}{e}{s}{s}{i}{o}{n}
set {s}{e}{t}
@ -323,22 +343,30 @@ show {s}{h}{o}{w}
some {s}{o}{m}{e}
start {s}{t}{a}{r}{t}
substring {s}{u}{b}{s}{t}{r}{i}{n}{g}
sum {s}{u}{m}
table {t}{a}{b}{l}{e}
tables {t}{a}{b}{l}{e}{s}
then {t}{h}{e}{n}
transaction {t}{r}{a}{n}{s}{a}{c}{t}{i}{o}{n}
truncate {t}{r}{u}{n}{c}{a}{t}{e}
max {m}{a}{x}
min {m}{i}{n}
unknown {u}{n}{k}{n}{o}{w}{n}
union {u}{n}{i}{o}{n}
unique {u}{n}{i}{q}{u}{e}
nullif {n}{u}{l}{l}{i}{f}
update {u}{p}{d}{a}{t}{e}
value {v}{a}{l}{u}{e}
values {v}{a}{l}{u}{e}{s}
variables {v}{a}{r}{i}{a}{b}{l}{e}{s}
warnings {w}{a}{r}{n}{i}{n}{g}{s}
week {w}{e}{e}{k}
weekday {w}{e}{e}{k}{d}{a}{y}
weekofyear {w}{e}{e}{k}{o}{f}{y}{e}{a}{r}
where {w}{h}{e}{r}{e}
when {w}{h}{e}{n}
xor {x}{o}{r}
yearweek {y}{e}{a}{r}{w}{e}{e}{k}
null {n}{u}{l}{l}
false {f}{a}{l}{s}{e}
@ -455,6 +483,8 @@ sys_var "@@"(({global}".")|({session}".")|{local}".")?{ident}
"?" return placeholder
{abs} lval.item = string(l.val)
return abs
{add} return add
{after} lval.item = string(l.val)
return after
@ -467,6 +497,8 @@ sys_var "@@"(({global}".")|({session}".")|{local}".")?{ident}
{as} return as
{auto_increment} lval.item = string(l.val)
return autoIncrement
{avg} lval.item = string(l.val)
return avg
{begin} lval.item = string(l.val)
return begin
{between} return between
@ -476,18 +508,35 @@ sys_var "@@"(({global}".")|({session}".")|{local}".")?{ident}
{character} return character
{charset} lval.item = string(l.val)
return charsetKwd
{coalesce} lval.item = string(l.val)
return coalesce
{collate} return collation
{column} return column
{columns} lval.item = string(l.val)
return columns
{commit} lval.item = string(l.val)
return commit
{concat} lval.item = string(l.val)
return concat
{concat_ws} lval.item = string(l.val)
return concatWs
{constraint} return constraint
{convert} return convert
{count} lval.item = string(l.val)
return count
{create} return create
{cross} return cross
{database} return database
{database} lval.item = string(l.val)
return database
{databases} return databases
{day} lval.item = string(l.val)
return day
{dayofweek} lval.item = string(l.val)
return dayofweek
{dayofmonth} lval.item = string(l.val)
return dayofmonth
{dayofyear} lval.item = string(l.val)
return dayofyear
{deallocate} lval.item = string(l.val)
return deallocate
{default} return defaultKwd
@ -517,14 +566,23 @@ sys_var "@@"(({global}".")|({session}".")|{local}".")?{ident}
return first
{for} return forKwd
{foreign} return foreign
{found_rows} lval.item = string(l.val)
return foundRows
{from} return from
{full} lval.item = string(l.val)
return full
{fulltext} return fulltext
{group} return group
{group_concat} lval.item = string(l.val)
return groupConcat
{having} return having
{high_priority} return highPriority
{if} return ifKwd
{hour} lval.item = string(l.val)
return hour
{if} lval.item = string(l.val)
return ifKwd
{ifnull} lval.item = string(l.val)
return ifNull
{ignore} return ignore
{index} return index
{inner} return inner
@ -534,16 +592,29 @@ sys_var "@@"(({global}".")|({session}".")|{local}".")?{ident}
{is} return is
{join} return join
{key} return key
{left} return left
{left} lval.item = string(l.val)
return left
{length} lval.item = string(l.val)
return length
{like} return like
{limit} return limit
{local} lval.item = string(l.val)
return local
{lock} return lock
{low_priority} return lowPriority
{max} lval.item = string(l.val)
return max
{microsecond} lval.item = string(l.val)
return microsecond
{min} lval.item = string(l.val)
return min
{minute} lval.item = string(l.val)
return minute
{mod} return mod
{mode} lval.item = string(l.val)
return mode
{month} lval.item = string(l.val)
return month
{names} lval.item = string(l.val)
return names
{not} return not
@ -565,7 +636,8 @@ sys_var "@@"(({global}".")|({session}".")|{local}".")?{ident}
return rollback
{row} lval.item = string(l.val)
return row
{schema} return schema
{schema} lval.item = string(l.val)
return schema
{schemas} return schemas
{session} lval.item = string(l.val)
return session
@ -575,6 +647,8 @@ sys_var "@@"(({global}".")|({session}".")|{local}".")?{ident}
return start
{global} lval.item = string(l.val)
return global
{repeat} lval.item = string(l.val)
return repeat
{regexp} return regexp
{references} return references
{rlike} return rlike
@ -584,7 +658,8 @@ sys_var "@@"(({global}".")|({session}".")|{local}".")?{ident}
{user_var} lval.item = string(l.val)
return userVar
{second} lval.item = string(l.val)
return second
{select} return selectKwd
{set} return set
@ -592,6 +667,8 @@ sys_var "@@"(({global}".")|({session}".")|{local}".")?{ident}
{show} return show
{substring} lval.item = string(l.val)
return substring
{sum} lval.item = string(l.val)
return sum
{table} return tableKwd
{tables} lval.item = string(l.val)
return tables
@ -600,11 +677,13 @@ sys_var "@@"(({global}".")|({session}".")|{local}".")?{ident}
return transaction
{truncate} lval.item = string(l.val)
return truncate
{update} return update
{union} return union
{unique} return unique
{unknown} lval.item = string(l.val)
return unknown
{nullif} lval.item = string(l.val)
return nullIf
{update} return update
{use} return use
{using} return using
{value} lval.item = string(l.val)
@ -614,10 +693,18 @@ sys_var "@@"(({global}".")|({session}".")|{local}".")?{ident}
return variables
{warnings} lval.item = string(l.val)
return warnings
{week} lval.item = string(l.val)
return week
{weekday} lval.item = string(l.val)
return weekday
{weekofyear} lval.item = string(l.val)
return weekofyear
{when} return when
{where} return where
{xor} return xor
{yearweek} lval.item = string(l.val)
return yearweek
{signed} lval.item = string(l.val)
return signed
{unsigned} return unsigned

View File

@ -40,7 +40,6 @@ type dbTxn struct {
store *dbStore // for commit
startTs time.Time
tID int64
opCnt int64
valid bool
snapshotVals map[string][]byte // origin version in snapshot
}

View File

@ -32,7 +32,6 @@ import (
"github.com/pingcap/tidb/meta/autoid"
"github.com/pingcap/tidb/model"
mysql "github.com/pingcap/tidb/mysqldef"
"github.com/pingcap/tidb/parser/coldef"
"github.com/pingcap/tidb/sessionctx/variable"
"github.com/pingcap/tidb/table"
"github.com/pingcap/tidb/util"
@ -45,7 +44,6 @@ type Table struct {
Name model.CIStr
Columns []*column.Col
opt *coldef.TableOption
indices []*column.IndexedCol
recordPrefix string
indexPrefix string

153
util/types/overflow.go Normal file
View File

@ -0,0 +1,153 @@
// 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 types
import (
"math"
"github.com/juju/errors"
)
// ErrArithOverflow is the error for arthimetic operation overflow.
var ErrArithOverflow = errors.New("operation overflow")
// AddUint64 adds uint64 a and b if no overflow, else, returns error.
func AddUint64(a uint64, b uint64) (uint64, error) {
if math.MaxUint64-a < b {
return 0, errors.Trace(ErrArithOverflow)
}
return a + b, nil
}
// AddInt64 adds int64 a and b if no overflow, otherwise, returns error.
func AddInt64(a int64, b int64) (int64, error) {
if (a > 0 && b > 0 && math.MaxInt64-a < b) ||
(a < 0 && b < 0 && math.MinInt64-a > b) {
return 0, errors.Trace(ErrArithOverflow)
}
return a + b, nil
}
// AddInteger adds uint64 a and int64 b and returns uint64 if no overflow error.
func AddInteger(a uint64, b int64) (uint64, error) {
if b >= 0 {
return AddUint64(a, uint64(b))
}
if uint64(-b) > a {
return 0, errors.Trace(ErrArithOverflow)
}
return a - uint64(-b), nil
}
// SubUint64 substracts uint64 a with b and returns uint64 if no overflow error.
func SubUint64(a uint64, b uint64) (uint64, error) {
if a < b {
return 0, errors.Trace(ErrArithOverflow)
}
return a - b, nil
}
// SubInt64 substracts int64 a with b and returns int64 if no overflow error.
func SubInt64(a int64, b int64) (int64, error) {
if (a > 0 && b < 0 && math.MaxInt64-a < -b) ||
(a < 0 && b > 0 && math.MinInt64-a > -b) ||
(a == 0 && b == math.MinInt64) {
return 0, errors.Trace(ErrArithOverflow)
}
return a - b, nil
}
// SubUintWithInt substracts uint64 a with int64 b and returns uint64 if no overflow error.
func SubUintWithInt(a uint64, b int64) (uint64, error) {
if b < 0 {
return AddUint64(a, uint64(-b))
}
return SubUint64(a, uint64(b))
}
// SubIntWithUint substracts int64 a with uint64 b and returns uint64 if no overflow error.
func SubIntWithUint(a int64, b uint64) (uint64, error) {
if a < 0 || uint64(a) < b {
return 0, errors.Trace(ErrArithOverflow)
}
return uint64(a) - b, nil
}
// MulUint64 multiplies uint64 a and b and returns uint64 if no overflow error.
func MulUint64(a uint64, b uint64) (uint64, error) {
if b > 0 && a > math.MaxUint64/b {
return 0, errors.Trace(ErrArithOverflow)
}
return a * b, nil
}
// MulInt64 multiplies int64 a and b and returns int64 if no overflow error.
func MulInt64(a int64, b int64) (int64, error) {
if a == 0 || b == 0 {
return 0, nil
}
var (
res uint64
err error
negative = false
)
if a > 0 && b > 0 {
res, err = MulUint64(uint64(a), uint64(b))
} else if a < 0 && b < 0 {
res, err = MulUint64(uint64(-a), uint64(-b))
} else if a < 0 && b > 0 {
negative = true
res, err = MulUint64(uint64(-a), uint64(b))
} else {
negative = true
res, err = MulUint64(uint64(a), uint64(-b))
}
if err != nil {
return 0, errors.Trace(err)
}
if negative {
// negative result
if res > math.MaxInt64+1 {
return 0, errors.Trace(ErrArithOverflow)
}
return -int64(res), nil
}
// positive result
if res > math.MaxInt64 {
return 0, errors.Trace(ErrArithOverflow)
}
return int64(res), nil
}
// MulInteger multiplies uint64 a and int64 b, and returns uint64 if no overflow error.
func MulInteger(a uint64, b int64) (uint64, error) {
if a == 0 || b == 0 {
return 0, nil
}
if b < 0 {
return 0, errors.Trace(ErrArithOverflow)
}
return MulUint64(a, uint64(b))
}

265
util/types/overflow_test.go Normal file
View File

@ -0,0 +1,265 @@
// 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 types
import (
"math"
. "github.com/pingcap/check"
)
var _ = Suite(&testOverflowSuite{})
type testOverflowSuite struct {
}
func (s *testOverflowSuite) TestAdd(c *C) {
tblUint64 := []struct {
lsh uint64
rsh uint64
ret uint64
overflow bool
}{
{math.MaxUint64, 1, 0, true},
{math.MaxUint64, 0, math.MaxUint64, false},
{1, 1, 2, false},
}
for _, t := range tblUint64 {
ret, err := AddUint64(t.lsh, t.rsh)
if t.overflow {
c.Assert(err, NotNil)
} else {
c.Assert(ret, Equals, t.ret)
}
}
tblInt64 := []struct {
lsh int64
rsh int64
ret int64
overflow bool
}{
{math.MaxInt64, 1, 0, true},
{math.MaxInt64, 0, math.MaxInt64, false},
{0, math.MinInt64, math.MinInt64, false},
{-1, math.MinInt64, 0, true},
{math.MaxInt64, math.MinInt64, -1, false},
{1, 1, 2, false},
{1, -1, 0, false},
}
for _, t := range tblInt64 {
ret, err := AddInt64(t.lsh, t.rsh)
if t.overflow {
c.Assert(err, NotNil)
} else {
c.Assert(ret, Equals, t.ret)
}
}
tblInt := []struct {
lsh uint64
rsh int64
ret uint64
overflow bool
}{
{math.MaxUint64, math.MinInt64, math.MaxUint64 + math.MinInt64, false},
{math.MaxInt64, math.MinInt64, 0, true},
{0, -1, 0, true},
{1, -1, 0, false},
{0, 1, 1, false},
{1, 1, 2, false},
}
for _, t := range tblInt {
ret, err := AddInteger(t.lsh, t.rsh)
if t.overflow {
c.Assert(err, NotNil)
} else {
c.Assert(ret, Equals, t.ret)
}
}
}
func (s *testOverflowSuite) TestSub(c *C) {
tblUint64 := []struct {
lsh uint64
rsh uint64
ret uint64
overflow bool
}{
{math.MaxUint64, 1, math.MaxUint64 - 1, false},
{math.MaxUint64, 0, math.MaxUint64, false},
{0, math.MaxUint64, 0, true},
{0, 1, 0, true},
{1, math.MaxUint64, 0, true},
{1, 1, 0, false},
}
for _, t := range tblUint64 {
ret, err := SubUint64(t.lsh, t.rsh)
if t.overflow {
c.Assert(err, NotNil)
} else {
c.Assert(ret, Equals, t.ret)
}
}
tblInt64 := []struct {
lsh int64
rsh int64
ret int64
overflow bool
}{
{math.MinInt64, 0, math.MinInt64, false},
{math.MinInt64, 1, 0, true},
{math.MaxInt64, -1, 0, true},
{0, math.MinInt64, 0, true},
{-1, math.MinInt64, math.MaxInt64, false},
{math.MinInt64, math.MaxInt64, 0, true},
{math.MinInt64, math.MinInt64, 0, false},
{math.MinInt64, -math.MaxInt64, -1, false},
{1, 1, 0, false},
}
for _, t := range tblInt64 {
ret, err := SubInt64(t.lsh, t.rsh)
if t.overflow {
c.Assert(err, NotNil)
} else {
c.Assert(ret, Equals, t.ret)
}
}
tblInt := []struct {
lsh uint64
rsh int64
ret uint64
overflow bool
}{
{0, math.MinInt64, -math.MinInt64, false},
{0, 1, 0, true},
{math.MaxUint64, math.MinInt64, 0, true},
{math.MaxInt64, math.MinInt64, 2*math.MaxInt64 + 1, false},
{math.MaxUint64, -1, 0, true},
{0, -1, 1, false},
{1, 1, 0, false},
}
for _, t := range tblInt {
ret, err := SubUintWithInt(t.lsh, t.rsh)
if t.overflow {
c.Assert(err, NotNil)
} else {
c.Assert(ret, Equals, t.ret)
}
}
tblInt2 := []struct {
lsh int64
rsh uint64
ret uint64
overflow bool
}{
{math.MinInt64, 0, 0, true},
{math.MaxInt64, 0, math.MaxInt64, false},
{math.MaxInt64, math.MaxUint64, 0, true},
{math.MaxInt64, -math.MinInt64, 0, true},
{-1, 0, 0, true},
{1, 1, 0, false},
}
for _, t := range tblInt2 {
ret, err := SubIntWithUint(t.lsh, t.rsh)
if t.overflow {
c.Assert(err, NotNil)
} else {
c.Assert(ret, Equals, t.ret)
}
}
}
func (s *testOverflowSuite) TestMul(c *C) {
tblUint64 := []struct {
lsh uint64
rsh uint64
ret uint64
overflow bool
}{
{math.MaxUint64, 1, math.MaxUint64, false},
{math.MaxUint64, 0, 0, false},
{math.MaxUint64, 2, 0, true},
{1, 1, 1, false},
}
for _, t := range tblUint64 {
ret, err := MulUint64(t.lsh, t.rsh)
if t.overflow {
c.Assert(err, NotNil)
} else {
c.Assert(ret, Equals, t.ret)
}
}
tblInt64 := []struct {
lsh int64
rsh int64
ret int64
overflow bool
}{
{math.MaxInt64, 1, math.MaxInt64, false},
{math.MinInt64, 1, math.MinInt64, false},
{math.MaxInt64, -1, -math.MaxInt64, false},
{math.MinInt64, -1, 0, true},
{math.MinInt64, 0, 0, false},
{math.MaxInt64, 0, 0, false},
{math.MaxInt64, math.MaxInt64, 0, true},
{math.MaxInt64, math.MinInt64, 0, true},
{math.MinInt64 / 10, 11, 0, true},
{1, 1, 1, false},
}
for _, t := range tblInt64 {
ret, err := MulInt64(t.lsh, t.rsh)
if t.overflow {
c.Assert(err, NotNil)
} else {
c.Assert(ret, Equals, t.ret)
}
}
tblInt := []struct {
lsh uint64
rsh int64
ret uint64
overflow bool
}{
{math.MaxUint64, 0, 0, false},
{0, -1, 0, false},
{1, -1, 0, true},
{math.MaxUint64, -1, 0, true},
{math.MaxUint64, 10, 0, true},
{1, 1, 1, false},
}
for _, t := range tblInt {
ret, err := MulInteger(t.lsh, t.rsh)
if t.overflow {
c.Assert(err, NotNil)
} else {
c.Assert(ret, Equals, t.ret)
}
}
}