Merge remote-tracking branch 'origin/master' into mvcc

This commit is contained in:
dongxu
2015-09-24 13:49:42 +08:00
16 changed files with 158 additions and 49 deletions

View File

@ -75,6 +75,7 @@ var Funcs = map[string]Func{
"month": {builtinMonth, 1, 1, true, false},
"now": {builtinNow, 0, 1, false, false},
"second": {builtinSecond, 1, 1, true, false},
"sysdate": {builtinSysDate, 0, 1, false, false},
"week": {builtinWeek, 1, 2, true, false},
"weekday": {builtinWeekDay, 1, 1, true, false},
"weekofyear": {builtinWeekOfYear, 1, 1, true, false},

View File

@ -142,18 +142,14 @@ func builtinMonth(args []interface{}, ctx map[interface{}]interface{}) (interfac
}
func builtinNow(args []interface{}, ctx map[interface{}]interface{}) (interface{}, error) {
fsp := int64(0)
// TODO: if NOW is used in stored function or trigger, NOW will return the beginning time
// of the execution.
fsp := 0
if len(args) == 1 {
var err error
fsp, err = types.ToInt64(args[0])
if err != nil {
if fsp, err = checkFsp(args[0]); err != nil {
return nil, errors.Trace(err)
}
if int(fsp) > mysql.MaxFsp {
return nil, errors.Errorf("Too big precision %d specified. Maximum is 6.", fsp)
} else if fsp < 0 {
return nil, errors.Errorf("Invalid negative %d specified, must in [0, 6].", fsp)
}
}
t := mysql.Time{
@ -297,3 +293,23 @@ func builtinYearWeek(args []interface{}, ctx map[interface{}]interface{}) (inter
year, week := t.ISOWeek()
return int64(year*100 + week), nil
}
func builtinSysDate(args []interface{}, ctx map[interface{}]interface{}) (interface{}, error) {
// SYSDATE is not the same as NOW if NOW is used in a stored function or trigger.
// But here we can just think they are the same because we don't support stored function
// and trigger now.
return builtinNow(args, ctx)
}
func checkFsp(arg interface{}) (int, error) {
fsp, err := types.ToInt64(arg)
if err != nil {
return 0, errors.Trace(err)
}
if int(fsp) > mysql.MaxFsp {
return 0, errors.Errorf("Too big precision %d specified. Maximum is 6.", fsp)
} else if fsp < 0 {
return 0, errors.Errorf("Invalid negative %d specified, must in [0, 6].", fsp)
}
return int(fsp), nil
}

View File

@ -15,6 +15,7 @@ package builtin
import (
"strings"
"time"
. "github.com/pingcap/check"
mysql "github.com/pingcap/tidb/mysqldef"
@ -247,3 +248,21 @@ func (s *testBuiltinSuite) TestNow(c *C) {
_, err = builtinNow([]interface{}{-2}, nil)
c.Assert(err, NotNil)
}
func (s *testBuiltinSuite) TestSysDate(c *C) {
last := time.Now()
v, err := builtinSysDate(nil, nil)
c.Assert(err, IsNil)
n, ok := v.(mysql.Time)
c.Assert(ok, IsTrue)
c.Assert(n.String(), GreaterEqual, last.Format(mysql.TimeFormat))
v, err = builtinSysDate([]interface{}{6}, nil)
c.Assert(err, IsNil)
n, ok = v.(mysql.Time)
c.Assert(ok, IsTrue)
c.Assert(n.String(), GreaterEqual, last.Format(mysql.TimeFormat))
_, err = builtinSysDate([]interface{}{-2}, nil)
c.Assert(err, NotNil)
}

View File

@ -116,6 +116,7 @@ import (
div "DIV"
do "DO"
drop "DROP"
dual "DUAL"
duplicate "DUPLICATE"
elseKwd "ELSE"
end "END"
@ -209,6 +210,7 @@ import (
substring "SUBSTRING"
sum "SUM"
sysVar "SYS_VAR"
sysDate "SYSDATE"
tableKwd "TABLE"
tables "TABLES"
then "THEN"
@ -325,7 +327,6 @@ import (
ConstraintKeywordOpt "Constraint Keyword or empty"
ConstraintOpt "optional column value constraint"
ConstraintOpts "optional column value constraints"
CreateDatabase "Create {DATABASE | SCHEMA}"
CreateDatabaseStmt "Create Database Statement"
CreateIndexStmt "CREATE INDEX statement"
CreateIndexStmtUnique "CREATE INDEX optional UNIQUE clause"
@ -335,6 +336,7 @@ import (
CreateTableStmt "CREATE TABLE statement"
CreateUserStmt "CREATE User statement"
CrossOpt "Cross join option"
DatabaseSym "DATABASE or SCHEMA"
DBName "Database Name"
DeallocateSym "Deallocate or drop"
DeallocateStmt "Deallocate prepared statement"
@ -975,9 +977,9 @@ IndexColNameList:
* | [DEFAULT] COLLATE [=] collation_name
*******************************************************************/
CreateDatabaseStmt:
CreateDatabase IfNotExists DBName CreateSpecListOpt
"CREATE" DatabaseSym IfNotExists DBName CreateSpecListOpt
{
opts := $4.([]*coldef.DatabaseOpt)
opts := $5.([]*coldef.DatabaseOpt)
//compose charset from x
var cs, co string
for _, x := range opts {
@ -994,8 +996,8 @@ CreateDatabaseStmt:
dbopt := &coldef.CharsetOpt{Chs: cs, Col: co}
$$ = &stmts.CreateDatabaseStmt{
IfNotExists: $2.(bool),
Name: $3.(string),
IfNotExists: $3.(bool),
Name: $4.(string),
Opt: dbopt}
if yylex.(*lexer).root {
@ -1003,14 +1005,6 @@ CreateDatabaseStmt:
}
}
CreateDatabase:
"CREATE" "DATABASE"
{
}
| "CREATE" "SCHEMA"
{
}
DBName:
Identifier
@ -1137,6 +1131,7 @@ DefaultOpt:
DefaultKwdOpt:
{}
| "DEFAULT"
/******************************************************************
* Do statement
* See: https://dev.mysql.com/doc/refman/5.7/en/do.html
@ -1220,10 +1215,12 @@ DeleteFromStmt:
break
}
}
DatabaseSym:
"DATABASE" | "SCHEMA"
DropDatabaseStmt:
"DROP" "DATABASE" IfExists Identifier
"DROP" DatabaseSym IfExists DBName
{
$$ = &stmts.DropDatabaseStmt{IfExists: $3.(bool), Name: $4.(string)}
if yylex.(*lexer).root {
@ -2257,6 +2254,20 @@ FunctionCallNonKeyword:
Len: $7.(expression.Expression),
}
}
| "SYSDATE" '(' ExpressionOpt ')'
{
args := []expression.Expression{}
if $3 != nil {
args = append(args, $3.(expression.Expression))
}
var err error
$$, err = expression.NewCall($1.(string), args, false)
if err != nil {
l := yylex.(*lexer)
l.err(err)
return 1
}
}
| "WEEKDAY" '(' Expression ')'
{
args := []expression.Expression{$3.(expression.Expression)}
@ -2694,13 +2705,13 @@ RollbackStmt:
}
SelectStmt:
"SELECT" SelectStmtOpts SelectStmtFieldList SelectStmtLimit SelectLockOpt
"SELECT" SelectStmtOpts SelectStmtFieldList FromDual SelectStmtLimit SelectLockOpt
{
$$ = &stmts.SelectStmt {
Distinct: $2.(bool),
Fields: $3.([]*field.Field),
From: nil,
Lock: $5.(coldef.LockType),
Lock: $6.(coldef.LockType),
}
}
| "SELECT" SelectStmtOpts SelectStmtFieldList "FROM"
@ -2739,6 +2750,11 @@ SelectStmt:
$$ = st
}
FromDual:
/* Empty */
| "FROM" "DUAL"
FromClause:
TableRefs
{

View File

@ -354,6 +354,20 @@ func (s *testParserSuite) TestParser0(c *C) {
{"show collation like 'utf8%'", true},
{"show collation where Charset = 'utf8' and Collation = 'utf8_bin'", true},
// For drop datbase/schema
{"create database xxx", true},
{"create database if exists xxx", false},
{"create database if not exists xxx", true},
{"create schema xxx", true},
{"create schema if exists xxx", false},
{"create schema if not exists xxx", true},
{"drop database xxx", true},
{"drop database if exists xxx", true},
{"drop database if not exists xxx", false},
{"drop schema xxx", true},
{"drop schema if exists xxx", true},
{"drop schema if not exists xxx", false},
// For issue 224
{`SELECT CAST('test collated returns' AS CHAR CHARACTER SET utf8) COLLATE utf8_bin;`, true},
@ -363,6 +377,11 @@ func (s *testParserSuite) TestParser0(c *C) {
{"select current_timestamp(6)", true},
{"select now()", true},
{"select now(6)", true},
{"select sysdate(), sysdate(6)", true},
// For dual
{"select 1 from dual", true},
{"select 1 from dual limit 1", true},
}
for _, t := range table {

View File

@ -279,6 +279,7 @@ describe {d}{e}{s}{c}{r}{i}{b}{e}
distinct {d}{i}{s}{t}{i}{n}{c}{t}
div {d}{i}{v}
do {d}{o}
dual {d}{u}{a}{l}
duplicate {d}{u}{p}{l}{i}{c}{a}{t}{e}
else {e}{l}{s}{e}
end {e}{n}{d}
@ -354,6 +355,7 @@ 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}
sysdate {s}{y}{s}{d}{a}{t}{e}
table {t}{a}{b}{l}{e}
tables {t}{a}{b}{l}{e}{s}
then {t}{h}{e}{n}
@ -567,6 +569,7 @@ sys_var "@@"(({global}".")|({session}".")|{local}".")?{ident}
{div} return div
{do} lval.item = string(l.val)
return do
{dual} return dual
{duplicate} lval.item = string(l.val)
return duplicate
{else} return elseKwd
@ -689,6 +692,8 @@ sys_var "@@"(({global}".")|({session}".")|{local}".")?{ident}
return substring
{sum} lval.item = string(l.val)
return sum
{sysdate} lval.item = string(l.val)
return sysDate
{table} return tableKwd
{tables} lval.item = string(l.val)
return tables

View File

@ -30,7 +30,7 @@ import (
var (
_ plan.Plan = (*SelectFieldsDefaultPlan)(nil)
_ plan.Plan = (*SelectEmptyFieldListPlan)(nil)
_ plan.Plan = (*SelectFromDualPlan)(nil)
)
// SelectFieldsDefaultPlan extracts specific fields from Src Plan.
@ -94,19 +94,19 @@ func (r *SelectFieldsDefaultPlan) Close() error {
return r.Src.Close()
}
// SelectEmptyFieldListPlan is the plan for "select expr, expr, ..."" with no FROM.
type SelectEmptyFieldListPlan struct {
// SelectFromDualPlan is the plan for "select expr, expr, ..."" or "select expr, expr, ... from dual".
type SelectFromDualPlan struct {
Fields []*field.Field
done bool
}
// Explain implements the plan.Plan Explain interface.
func (s *SelectEmptyFieldListPlan) Explain(w format.Formatter) {
func (s *SelectFromDualPlan) Explain(w format.Formatter) {
// TODO: finish this
}
// GetFields implements the plan.Plan GetFields interface.
func (s *SelectEmptyFieldListPlan) GetFields() []*field.ResultField {
func (s *SelectFromDualPlan) GetFields() []*field.ResultField {
ans := make([]*field.ResultField, 0, len(s.Fields))
if len(s.Fields) > 0 {
for _, f := range s.Fields {
@ -119,12 +119,12 @@ func (s *SelectEmptyFieldListPlan) GetFields() []*field.ResultField {
}
// Filter implements the plan.Plan Filter interface.
func (s *SelectEmptyFieldListPlan) Filter(ctx context.Context, expr expression.Expression) (plan.Plan, bool, error) {
func (s *SelectFromDualPlan) Filter(ctx context.Context, expr expression.Expression) (plan.Plan, bool, error) {
return s, false, nil
}
// Next implements plan.Plan Next interface.
func (s *SelectEmptyFieldListPlan) Next(ctx context.Context) (row *plan.Row, err error) {
func (s *SelectFromDualPlan) Next(ctx context.Context) (row *plan.Row, err error) {
if s.done {
return
}
@ -138,7 +138,7 @@ func (s *SelectEmptyFieldListPlan) Next(ctx context.Context) (row *plan.Row, err
}
// Close implements plan.Plan Close interface.
func (s *SelectEmptyFieldListPlan) Close() error {
func (s *SelectFromDualPlan) Close() error {
s.done = false
return nil
}

View File

@ -113,7 +113,7 @@ func (s *testFieldsSuit) TestDefaultFieldsPlan(c *C) {
}
func (s *testFieldsSuit) TestSelectExprPlan(c *C) {
pln := &plans.SelectEmptyFieldListPlan{
pln := &plans.SelectFromDualPlan{
Fields: []*field.Field{
{
Expr: &expression.Value{

View File

@ -101,8 +101,8 @@ func (s *ShowPlan) GetFields() []*field.ResultField {
names = []string{"Variable_name", "Value"}
case stmt.ShowCollation:
names = []string{"Collation", "Charset", "Id", "Default", "Compiled", "Sortlen"}
types = []byte{mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeLong,
mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeLong}
types = []byte{mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeLonglong,
mysql.TypeVarchar, mysql.TypeVarchar, mysql.TypeLonglong}
}
fields := make([]*field.ResultField, 0, len(names))
for i, name := range names {

View File

@ -156,7 +156,7 @@ func (p *testShowSuit) TestShowCollation(c *C) {
pln.Target = stmt.ShowCollation
fls := pln.GetFields()
c.Assert(fls, HasLen, 6)
c.Assert(fls[2].Col.Tp, Equals, mysql.TypeLong)
c.Assert(fls[2].Col.Tp, Equals, mysql.TypeLonglong)
pln.Pattern = &expression.PatternLike{
Pattern: &expression.Value{

View File

@ -29,7 +29,7 @@ import (
var (
_ plan.Planner = (*SelectFieldsRset)(nil)
_ plan.Planner = (*FieldRset)(nil)
_ plan.Planner = (*SelectFromDualRset)(nil)
)
// SelectFieldsRset is record set to select fields.
@ -80,12 +80,12 @@ func (r *SelectFieldsRset) Plan(ctx context.Context) (plan.Plan, error) {
return p, nil
}
// FieldRset is Recordset for select expression, like `select 1, 1+1`.
type FieldRset struct {
// SelectFromDualRset is Recordset for select from dual, like `select 1, 1+1` or `select 1 from dual`.
type SelectFromDualRset struct {
Fields []*field.Field
}
// Plan gets SelectExprPlan.
func (r *FieldRset) Plan(ctx context.Context) (plan.Plan, error) {
return &plans.SelectEmptyFieldListPlan{Fields: r.Fields}, nil
func (r *SelectFromDualRset) Plan(ctx context.Context) (plan.Plan, error) {
return &plans.SelectFromDualPlan{Fields: r.Fields}, nil
}

View File

@ -26,7 +26,7 @@ var _ = Suite(&testSelectFieldsPlannerSuite{})
type testSelectFieldsPlannerSuite struct {
sr *SelectFieldsRset
fr *FieldRset
fr *SelectFromDualRset
}
func (s *testSelectFieldsPlannerSuite) SetUpSuite(c *C) {
@ -47,7 +47,7 @@ func (s *testSelectFieldsPlannerSuite) SetUpSuite(c *C) {
}
s.sr = &SelectFieldsRset{Src: tblPlan, SelectList: selectList}
s.fr = &FieldRset{Fields: fields}
s.fr = &SelectFromDualRset{Fields: fields}
}
func (s *testSelectFieldsPlannerSuite) TestDistinctPlanner(c *C) {
@ -106,6 +106,6 @@ func (s *testSelectFieldsPlannerSuite) TestFieldPlanner(c *C) {
p, err := s.fr.Plan(nil)
c.Assert(err, IsNil)
_, ok := p.(*plans.SelectEmptyFieldListPlan)
_, ok := p.(*plans.SelectFromDualPlan)
c.Assert(ok, IsTrue)
}

View File

@ -131,7 +131,7 @@ func (s *SelectStmt) Plan(ctx context.Context) (plan.Plan, error) {
}
} else if s.Fields != nil {
// Only evaluate fields values.
fr := &rsets.FieldRset{Fields: s.Fields}
fr := &rsets.SelectFromDualRset{Fields: s.Fields}
r, err = fr.Plan(ctx)
if err != nil {
return nil, err

View File

@ -17,8 +17,8 @@ import (
"os"
"path"
"github.com/boltdb/bolt"
"github.com/juju/errors"
"github.com/ngaut/bolt"
"github.com/ngaut/log"
"github.com/pingcap/tidb/store/localstore/engine"
)

View File

@ -17,7 +17,7 @@ import (
"os"
"testing"
"github.com/ngaut/bolt"
"github.com/boltdb/bolt"
. "github.com/pingcap/check"
"github.com/pingcap/tidb/store/localstore/engine"
)

View File

@ -785,6 +785,16 @@ func (s *testSessionSuite) TestSelect(c *C) {
_, err = se.Execute("select * from t as a join (select 1) as a")
c.Assert(err, IsNil)
r := mustExecSQL(c, se, "select 1, 2 from dual")
row, err := r.FirstRow()
c.Assert(err, IsNil)
match(c, row, 1, 2)
r = mustExecSQL(c, se, "select 1, 2")
row, err = r.FirstRow()
c.Assert(err, IsNil)
match(c, row, 1, 2)
}
func (s *testSessionSuite) TestSubQuery(c *C) {
@ -847,7 +857,7 @@ func (s *testSessionSuite) TestTimeFunc(c *C) {
se := newSession(c, store, s.dbName)
last := time.Now().Format(mysql.TimeFormat)
r := mustExecSQL(c, se, "select now(), now(6), current_timestamp, current_timestamp(), current_timestamp(6)")
r := mustExecSQL(c, se, "select now(), now(6), current_timestamp, current_timestamp(), current_timestamp(6), sysdate(), sysdate(6)")
row, err := r.FirstRow()
c.Assert(err, IsNil)
for _, t := range row {
@ -884,6 +894,29 @@ func (s *testSessionSuite) TestBootstrap(c *C) {
mustExecSQL(c, se, "USE test;")
}
func (s *testSessionSuite) TestDatabase(c *C) {
store := newStore(c, s.dbName)
se := newSession(c, store, s.dbName)
// Test database.
mustExecSQL(c, se, "create database xxx")
mustExecSQL(c, se, "drop database xxx")
mustExecSQL(c, se, "drop database if exists xxx")
mustExecSQL(c, se, "create database xxx")
mustExecSQL(c, se, "create database if not exists xxx")
mustExecSQL(c, se, "drop database if exists xxx")
// Test schema.
mustExecSQL(c, se, "create schema xxx")
mustExecSQL(c, se, "drop schema xxx")
mustExecSQL(c, se, "drop schema if exists xxx")
mustExecSQL(c, se, "create schema xxx")
mustExecSQL(c, se, "create schema if not exists xxx")
mustExecSQL(c, se, "drop schema if exists xxx")
}
func newSession(c *C, store kv.Storage, dbName string) Session {
se, err := CreateSession(store)
c.Assert(err, IsNil)