From 90f198fee383a5810922e60a1f998117ab742d62 Mon Sep 17 00:00:00 2001 From: shenli Date: Thu, 5 Nov 2015 12:51:45 +0800 Subject: [PATCH 01/35] tidb: Split bootstrap into two steps. Fix https://github.com/pingcap/tidb/issues/515 --- bootstrap.go | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/bootstrap.go b/bootstrap.go index 81169100d8..1f1bc3c6dd 100644 --- a/bootstrap.go +++ b/bootstrap.go @@ -96,9 +96,6 @@ const ( // Bootstrap initiates system DB for a store. func bootstrap(s Session) { - // Create a test database. - mustExecute(s, "CREATE DATABASE IF NOT EXISTS test") - // Check if system db exists. _, err := s.Execute(fmt.Sprintf("USE %s;", mysql.SystemDB)) if err == nil { @@ -107,30 +104,33 @@ func bootstrap(s Session) { } else if !errors2.ErrorEqual(err, errors.ErrDatabaseNotExist) { log.Fatal(err) } - mustExecute(s, fmt.Sprintf("CREATE DATABASE %s;", mysql.SystemDB)) - initUserTable(s) - initPrivTables(s) - initGlobalVariables(s) + doDDLWorks(s) + doDMLWorks(s) } -func initUserTable(s Session) { +// Execute DDL statements in bootstrap stage. +func doDDLWorks(s Session) { + // Create a test database. + mustExecute(s, "CREATE DATABASE IF NOT EXISTS test") + // Create system db. + mustExecute(s, fmt.Sprintf("CREATE DATABASE %s;", mysql.SystemDB)) + // Create user table. mustExecute(s, CreateUserTable) + // Create privilege tables. + mustExecute(s, CreateDBPrivTable) + mustExecute(s, CreateTablePrivTable) + mustExecute(s, CreateColumnPrivTable) + // Create global systemt variable table. + mustExecute(s, CreateGloablVariablesTable) +} + +// Execute DML statements in bootstrap stage. +func doDMLWorks(s Session) { // Insert a default user with empty password. mustExecute(s, `INSERT INTO mysql.user VALUES ("localhost", "root", "", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y"), ("127.0.0.1", "root", "", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y"), ("::1", "root", "", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y");`) -} - -// Initiates privilege tables including mysql.db, mysql.tables_priv and mysql.column_priv. -func initPrivTables(s Session) { - mustExecute(s, CreateDBPrivTable) - mustExecute(s, CreateTablePrivTable) - mustExecute(s, CreateColumnPrivTable) -} - -// Initiates global system variables table. -func initGlobalVariables(s Session) { - mustExecute(s, CreateGloablVariablesTable) + // Init global system variable table. values := make([]string, 0, len(variable.SysVars)) for k, v := range variable.SysVars { value := fmt.Sprintf(`("%s", "%s")`, strings.ToLower(k), v.Value) From 6eff296608f5f223b01ce290aabbd0fdb28c1853 Mon Sep 17 00:00:00 2001 From: shenli Date: Thu, 5 Nov 2015 16:03:42 +0800 Subject: [PATCH 02/35] *: Address comment --- bootstrap.go | 69 ++++++++++++++++++++++++++++++++++++----- mysql/const.go | 2 ++ plan/plans/info_test.go | 2 +- tidb_test.go | 62 +++++++++++++++++++++++++++++++++++- 4 files changed, 125 insertions(+), 10 deletions(-) diff --git a/bootstrap.go b/bootstrap.go index 1f1bc3c6dd..741d651152 100644 --- a/bootstrap.go +++ b/bootstrap.go @@ -22,10 +22,11 @@ import ( "runtime/debug" "strings" + "github.com/juju/errors" "github.com/ngaut/log" "github.com/pingcap/tidb/mysql" "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util/errors" + terrors "github.com/pingcap/tidb/util/errors" "github.com/pingcap/tidb/util/errors2" ) @@ -92,28 +93,73 @@ const ( CreateGloablVariablesTable = `CREATE TABLE if not exists mysql.GLOBAL_VARIABLES( VARIABLE_NAME VARCHAR(64) Not Null PRIMARY KEY, VARIABLE_VALUE VARCHAR(1024) DEFAULT Null);` + // CreateTiDBTable is the SQL statement creates a table in system db. + // This table is a key-value struct contains some information used by TiDB. + // Currently we only put bootstraped in it which indicates if the system is already bootstraped. + CreateTiDBTable = `CREATE TABLE if not exists mysql.tidb( + VARIABLE_NAME VARCHAR(64) Not Null PRIMARY KEY, + VARIABLE_VALUE VARCHAR(1024) DEFAULT Null, + COMMENT VARCHAR(1024));` ) // Bootstrap initiates system DB for a store. func bootstrap(s Session) { - // Check if system db exists. - _, err := s.Execute(fmt.Sprintf("USE %s;", mysql.SystemDB)) - if err == nil { - // We have already finished bootstrap. - return - } else if !errors2.ErrorEqual(err, errors.ErrDatabaseNotExist) { + b, err := alreadyBootstraped(s) + if err != nil { log.Fatal(err) } + if b { + return + } doDDLWorks(s) doDMLWorks(s) } +const ( + bootstrapedVar = "bootstraped" + bootstrapedVarTrue = "True" + bootstrapedVarFalse = "False" +) + +func alreadyBootstraped(s Session) (bool, error) { + // Check if system db exists. + _, err := s.Execute(fmt.Sprintf("USE %s;", mysql.SystemDB)) + if !errors2.ErrorEqual(err, terrors.ErrDatabaseNotExist) { + return false, errors.Trace(err) + } + // Check bootstraped variable value in TiDB table. + v, err := checkBootstrapedVar(s) + if err != nil { + return false, errors.Trace(err) + } + return v, nil +} + +func checkBootstrapedVar(s Session) (bool, error) { + sql := fmt.Sprintf(`SELECT VARIABLE_VALUE FROM %s.%s WHERE VARIABLE_NAME="%s"`, mysql.SystemDB, mysql.TiDBTable, bootstrapedVar) + rs, err := s.Execute(sql) + if err != nil { + // TODO: use terrors to compare error. + str := err.Error() + if strings.Contains(str, "does not exist") { + return false, nil + } + return false, errors.Trace(err) + } + if len(rs) != 1 { + return false, errors.New("Wrong number of Recordset") + } + r := rs[0] + row, err := r.Next() + return row.Data[0].(string) == bootstrapedVarTrue, errors.Trace(err) +} + // Execute DDL statements in bootstrap stage. func doDDLWorks(s Session) { // Create a test database. mustExecute(s, "CREATE DATABASE IF NOT EXISTS test") // Create system db. - mustExecute(s, fmt.Sprintf("CREATE DATABASE %s;", mysql.SystemDB)) + mustExecute(s, fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s;", mysql.SystemDB)) // Create user table. mustExecute(s, CreateUserTable) // Create privilege tables. @@ -122,10 +168,14 @@ func doDDLWorks(s Session) { mustExecute(s, CreateColumnPrivTable) // Create global systemt variable table. mustExecute(s, CreateGloablVariablesTable) + // Create TiDB table. + mustExecute(s, CreateTiDBTable) } // Execute DML statements in bootstrap stage. +// All the statements run in a single transaction. func doDMLWorks(s Session) { + mustExecute(s, "BEGIN") // Insert a default user with empty password. mustExecute(s, `INSERT INTO mysql.user VALUES ("localhost", "root", "", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y"), ("127.0.0.1", "root", "", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y"), @@ -138,6 +188,9 @@ func doDMLWorks(s Session) { } sql := fmt.Sprintf("INSERT INTO %s.%s VALUES %s;", mysql.SystemDB, mysql.GlobalVariablesTable, strings.Join(values, ", ")) mustExecute(s, sql) + sql = fmt.Sprintf(`INSERT INTO %s.%s VALUES("%s", "%s", "Bootstrap flag. Do not delete.") ON DUPLICATE KEY UPDATE VARIABLE_VALUE="%s"`, mysql.SystemDB, mysql.TiDBTable, bootstrapedVar, bootstrapedVarTrue, bootstrapedVarTrue) + mustExecute(s, sql) + mustExecute(s, "COMMIT") } func mustExecute(s Session, sql string) { diff --git a/mysql/const.go b/mysql/const.go index 6cd649380b..3172ec6932 100644 --- a/mysql/const.go +++ b/mysql/const.go @@ -130,6 +130,8 @@ const ( ColumnPrivTable = "Columns_priv" // GlobalVariablesTable is the table contains global system variables. GlobalVariablesTable = "GLOBAL_VARIABLES" + // TiDBTable is the table contains tidb info. + TiDBTable = "tidb" ) // PrivilegeType privilege diff --git a/plan/plans/info_test.go b/plan/plans/info_test.go index 3e9433dc30..06f7acdc11 100644 --- a/plan/plans/info_test.go +++ b/plan/plans/info_test.go @@ -86,7 +86,7 @@ func (p *testInfoSchemaSuit) TestInfoSchema(c *C) { cnt = mustQuery(c, testDB, "select * from information_schema.columns") c.Assert(cnt, Greater, 0) cnt = mustQuery(c, testDB, "select * from information_schema.statistics") - c.Assert(cnt, Equals, 15) + c.Assert(cnt, Equals, 16) cnt = mustQuery(c, testDB, "select * from information_schema.character_sets") c.Assert(cnt, Greater, 0) cnt = mustQuery(c, testDB, "select * from information_schema.collations") diff --git a/tidb_test.go b/tidb_test.go index 0cb22ed701..63c3d6b1a7 100644 --- a/tidb_test.go +++ b/tidb_test.go @@ -20,6 +20,7 @@ import ( "os" "runtime" "sync" + "sync/atomic" "testing" "time" @@ -28,6 +29,8 @@ import ( "github.com/pingcap/tidb/kv" "github.com/pingcap/tidb/mysql" "github.com/pingcap/tidb/rset" + "github.com/pingcap/tidb/sessionctx" + "github.com/pingcap/tidb/sessionctx/autocommit" "github.com/pingcap/tidb/sessionctx/variable" "github.com/pingcap/tidb/util/errors2" ) @@ -106,7 +109,8 @@ func checkResult(c *C, r sql.Result, affectedRows int64, insertID int64) { } type testSessionSuite struct { - dbName string + dbName string + dbNameBootstrap string createDBSQL string dropDBSQL string @@ -332,6 +336,7 @@ func sessionExec(c *C, se Session, sql string) ([]rset.Recordset, error) { func (s *testSessionSuite) SetUpSuite(c *C) { s.dbName = "test_session_db" + s.dbNameBootstrap = "test_main_db_bootstrap" s.createDBSQL = fmt.Sprintf("create database if not exists %s;", s.dbName) s.dropDBSQL = fmt.Sprintf("drop database %s;", s.dbName) s.useDBSQL = fmt.Sprintf("use %s;", s.dbName) @@ -1024,6 +1029,61 @@ func (s *testSessionSuite) TestBootstrap(c *C) { c.Assert(v[0], Equals, int64(len(variable.SysVars))) } +// Create a new session on store but only do ddl works. +func (s *testSessionSuite) bootstrapWithError(store kv.Storage, c *C) { + ss := &session{ + values: make(map[fmt.Stringer]interface{}), + store: store, + sid: atomic.AddInt64(&sessionID, 1), + } + domain, err := domap.Get(store) + c.Assert(err, IsNil) + sessionctx.BindDomain(ss, domain) + variable.BindSessionVars(ss) + variable.GetSessionVars(ss).SetStatusFlag(mysql.ServerStatusAutocommit, true) + // session implements autocommit.Checker. Bind it to ctx + autocommit.BindAutocommitChecker(ss, ss) + sessionMu.Lock() + defer sessionMu.Unlock() + b, err := alreadyBootstraped(ss) + c.Assert(b, IsFalse) + c.Assert(err, IsNil) + doDDLWorks(ss) + // Leave dml unfinished. +} + +func (s *testSessionSuite) TestBootstrapWithError(c *C) { + store := newStore(c, s.dbNameBootstrap) + s.bootstrapWithError(store, c) + se := newSession(c, store, s.dbNameBootstrap) + mustExecSQL(c, se, "USE mysql;") + r := mustExecSQL(c, se, `select * from user;`) + row, err := r.Next() + c.Assert(err, IsNil) + c.Assert(row, NotNil) + match(c, row.Data, "localhost", "root", "", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y") + row, err = r.Next() + c.Assert(err, IsNil) + c.Assert(row, NotNil) + match(c, row.Data, "127.0.0.1", "root", "", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y") + mustExecSQL(c, se, "USE test;") + // Check privilege tables. + mustExecSQL(c, se, "SELECT * from mysql.db;") + mustExecSQL(c, se, "SELECT * from mysql.tables_priv;") + mustExecSQL(c, se, "SELECT * from mysql.columns_priv;") + // Check privilege tables. + r = mustExecSQL(c, se, "SELECT COUNT(*) from mysql.global_variables;") + v, err := r.FirstRow() + c.Assert(err, IsNil) + c.Assert(v[0], Equals, int64(len(variable.SysVars))) + r = mustExecSQL(c, se, `SELECT VARIABLE_VALUE from mysql.TiDB where VARIABLE_NAME="bootstraped";`) + row, err = r.Next() + c.Assert(err, IsNil) + c.Assert(row, NotNil) + c.Assert(row.Data, HasLen, 1) + c.Assert(row.Data[0], Equals, "True") +} + func (s *testSessionSuite) TestEnum(c *C) { store := newStore(c, s.dbName) se := newSession(c, store, s.dbName) From ad94f8ec4eb1e419ba9758a2afa33f18d54e4bb4 Mon Sep 17 00:00:00 2001 From: shenli Date: Thu, 5 Nov 2015 17:13:22 +0800 Subject: [PATCH 03/35] *: Address comment --- bootstrap.go | 17 +++++++++++------ tidb_test.go | 2 +- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/bootstrap.go b/bootstrap.go index 741d651152..bdb14222b6 100644 --- a/bootstrap.go +++ b/bootstrap.go @@ -104,7 +104,7 @@ const ( // Bootstrap initiates system DB for a store. func bootstrap(s Session) { - b, err := alreadyBootstraped(s) + b, err := checkBootstraped(s) if err != nil { log.Fatal(err) } @@ -116,12 +116,11 @@ func bootstrap(s Session) { } const ( - bootstrapedVar = "bootstraped" - bootstrapedVarTrue = "True" - bootstrapedVarFalse = "False" + bootstrapedVar = "bootstraped" + bootstrapedVarTrue = "True" ) -func alreadyBootstraped(s Session) (bool, error) { +func checkBootstraped(s Session) (bool, error) { // Check if system db exists. _, err := s.Execute(fmt.Sprintf("USE %s;", mysql.SystemDB)) if !errors2.ErrorEqual(err, terrors.ErrDatabaseNotExist) { @@ -151,7 +150,13 @@ func checkBootstrapedVar(s Session) (bool, error) { } r := rs[0] row, err := r.Next() - return row.Data[0].(string) == bootstrapedVarTrue, errors.Trace(err) + if err != nil { + return false, errors.Trace(err) + } + if row == nil { + return false, nil + } + return row.Data[0].(string) == bootstrapedVarTrue, nil } // Execute DDL statements in bootstrap stage. diff --git a/tidb_test.go b/tidb_test.go index 63c3d6b1a7..d01069d215 100644 --- a/tidb_test.go +++ b/tidb_test.go @@ -1045,7 +1045,7 @@ func (s *testSessionSuite) bootstrapWithError(store kv.Storage, c *C) { autocommit.BindAutocommitChecker(ss, ss) sessionMu.Lock() defer sessionMu.Unlock() - b, err := alreadyBootstraped(ss) + b, err := checkBootstraped(ss) c.Assert(b, IsFalse) c.Assert(err, IsNil) doDDLWorks(ss) From 6cd4392fcad7fade1d39e540dfff59a829cce0f9 Mon Sep 17 00:00:00 2001 From: shenli Date: Thu, 5 Nov 2015 17:24:43 +0800 Subject: [PATCH 04/35] *: Address comment --- bootstrap.go | 22 +++++++++++----------- tidb_test.go | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/bootstrap.go b/bootstrap.go index bdb14222b6..d934f5a55e 100644 --- a/bootstrap.go +++ b/bootstrap.go @@ -95,7 +95,7 @@ const ( VARIABLE_VALUE VARCHAR(1024) DEFAULT Null);` // CreateTiDBTable is the SQL statement creates a table in system db. // This table is a key-value struct contains some information used by TiDB. - // Currently we only put bootstraped in it which indicates if the system is already bootstraped. + // Currently we only put bootstrapped in it which indicates if the system is already bootstrapped. CreateTiDBTable = `CREATE TABLE if not exists mysql.tidb( VARIABLE_NAME VARCHAR(64) Not Null PRIMARY KEY, VARIABLE_VALUE VARCHAR(1024) DEFAULT Null, @@ -104,7 +104,7 @@ const ( // Bootstrap initiates system DB for a store. func bootstrap(s Session) { - b, err := checkBootstraped(s) + b, err := checkBootstrapped(s) if err != nil { log.Fatal(err) } @@ -116,26 +116,26 @@ func bootstrap(s Session) { } const ( - bootstrapedVar = "bootstraped" - bootstrapedVarTrue = "True" + bootstrappedVar = "bootstrapped" + bootstrappedVarTrue = "True" ) -func checkBootstraped(s Session) (bool, error) { +func checkBootstrapped(s Session) (bool, error) { // Check if system db exists. _, err := s.Execute(fmt.Sprintf("USE %s;", mysql.SystemDB)) if !errors2.ErrorEqual(err, terrors.ErrDatabaseNotExist) { return false, errors.Trace(err) } - // Check bootstraped variable value in TiDB table. - v, err := checkBootstrapedVar(s) + // Check bootstrapped variable value in TiDB table. + v, err := checkBootstrappedVar(s) if err != nil { return false, errors.Trace(err) } return v, nil } -func checkBootstrapedVar(s Session) (bool, error) { - sql := fmt.Sprintf(`SELECT VARIABLE_VALUE FROM %s.%s WHERE VARIABLE_NAME="%s"`, mysql.SystemDB, mysql.TiDBTable, bootstrapedVar) +func checkBootstrappedVar(s Session) (bool, error) { + sql := fmt.Sprintf(`SELECT VARIABLE_VALUE FROM %s.%s WHERE VARIABLE_NAME="%s"`, mysql.SystemDB, mysql.TiDBTable, bootstrappedVar) rs, err := s.Execute(sql) if err != nil { // TODO: use terrors to compare error. @@ -156,7 +156,7 @@ func checkBootstrapedVar(s Session) (bool, error) { if row == nil { return false, nil } - return row.Data[0].(string) == bootstrapedVarTrue, nil + return row.Data[0].(string) == bootstrappedVarTrue, nil } // Execute DDL statements in bootstrap stage. @@ -193,7 +193,7 @@ func doDMLWorks(s Session) { } sql := fmt.Sprintf("INSERT INTO %s.%s VALUES %s;", mysql.SystemDB, mysql.GlobalVariablesTable, strings.Join(values, ", ")) mustExecute(s, sql) - sql = fmt.Sprintf(`INSERT INTO %s.%s VALUES("%s", "%s", "Bootstrap flag. Do not delete.") ON DUPLICATE KEY UPDATE VARIABLE_VALUE="%s"`, mysql.SystemDB, mysql.TiDBTable, bootstrapedVar, bootstrapedVarTrue, bootstrapedVarTrue) + sql = fmt.Sprintf(`INSERT INTO %s.%s VALUES("%s", "%s", "Bootstrap flag. Do not delete.") ON DUPLICATE KEY UPDATE VARIABLE_VALUE="%s"`, mysql.SystemDB, mysql.TiDBTable, bootstrappedVar, bootstrappedVarTrue, bootstrappedVarTrue) mustExecute(s, sql) mustExecute(s, "COMMIT") } diff --git a/tidb_test.go b/tidb_test.go index d01069d215..42c6bf12f0 100644 --- a/tidb_test.go +++ b/tidb_test.go @@ -1045,7 +1045,7 @@ func (s *testSessionSuite) bootstrapWithError(store kv.Storage, c *C) { autocommit.BindAutocommitChecker(ss, ss) sessionMu.Lock() defer sessionMu.Unlock() - b, err := checkBootstraped(ss) + b, err := checkBootstrapped(ss) c.Assert(b, IsFalse) c.Assert(err, IsNil) doDDLWorks(ss) @@ -1076,7 +1076,7 @@ func (s *testSessionSuite) TestBootstrapWithError(c *C) { v, err := r.FirstRow() c.Assert(err, IsNil) c.Assert(v[0], Equals, int64(len(variable.SysVars))) - r = mustExecSQL(c, se, `SELECT VARIABLE_VALUE from mysql.TiDB where VARIABLE_NAME="bootstraped";`) + r = mustExecSQL(c, se, `SELECT VARIABLE_VALUE from mysql.TiDB where VARIABLE_NAME="bootstrapped";`) row, err = r.Next() c.Assert(err, IsNil) c.Assert(row, NotNil) From 6a799bfbd874176c644b95c41639158029b9c405 Mon Sep 17 00:00:00 2001 From: xia Date: Thu, 5 Nov 2015 17:06:19 +0800 Subject: [PATCH 05/35] parser: support ADDDATE and SUBDATE function Conflicts: parser/parser.y --- parser/parser.y | 44 ++++++++++++++++++++++++++++--------------- parser/parser_test.go | 44 +++++++++++++++++++++++++++++++++++++++++++ parser/scanner.l | 10 ++++++---- 3 files changed, 79 insertions(+), 19 deletions(-) diff --git a/parser/parser.y b/parser/parser.y index 1393a7dff1..856c98553d 100644 --- a/parser/parser.y +++ b/parser/parser.y @@ -59,6 +59,7 @@ import ( abs "ABS" add "ADD" + addDate "ADDDATE" after "AFTER" all "ALL" alter "ALTER" @@ -228,6 +229,7 @@ import ( some "SOME" start "START" stringType "string" + subDate "SUBDATE" substring "SUBSTRING" substringIndex "SUBSTRING_INDEX" sum "SUM" @@ -373,6 +375,7 @@ import ( CreateTableStmt "CREATE TABLE statement" CreateUserStmt "CREATE User statement" CrossOpt "Cross join option" + DateArithOpt "Date Arith option" DatabaseSym "DATABASE or SCHEMA" DBName "Database Name" DeallocateSym "Deallocate or drop" @@ -1653,9 +1656,11 @@ UnReservedKeyword: | "NATIONAL" | "ROW" | "QUARTER" | "ESCAPE" | "GRANTS" NotKeywordToken: - "ABS" | "COALESCE" | "CONCAT" | "CONCAT_WS" | "COUNT" | "DAY" | "DATE_ADD" | "DATE_SUB" | "DAYOFMONTH" | "DAYOFWEEK" | "DAYOFYEAR" | "FOUND_ROWS" | "GROUP_CONCAT" -| "HOUR" | "IFNULL" | "LENGTH" | "LOCATE" | "MAX" | "MICROSECOND" | "MIN" | "MINUTE" | "NULLIF" | "MONTH" | "NOW" | "RAND" | "SECOND" | "SQL_CALC_FOUND_ROWS" -| "SUBSTRING" %prec lowerThanLeftParen | "SUBSTRING_INDEX" | "SUM" | "TRIM" | "WEEKDAY" | "WEEKOFYEAR" | "YEARWEEK" + "ABS" | "ADDDATE" | "COALESCE" | "CONCAT" | "CONCAT_WS" | "COUNT" | "DAY" | "DATE_ADD" | "DATE_SUB" | "DAYOFMONTH" +| "DAYOFWEEK" | "DAYOFYEAR" | "FOUND_ROWS" | "GROUP_CONCAT"| "HOUR" | "IFNULL" | "LENGTH" | "LOCATE" | "MAX" +| "MICROSECOND" | "MIN" | "MINUTE" | "NULLIF" | "MONTH" | "NOW" | "RAND" | "SECOND" | "SQL_CALC_FOUND_ROWS" +| "SUBDATE" | "SUBSTRING" %prec lowerThanLeftParen | "SUBSTRING_INDEX" | "SUM" | "TRIM" | "WEEKDAY" | "WEEKOFYEAR" +| "YEARWEEK" /************************************************************************************ * @@ -2128,20 +2133,11 @@ FunctionCallNonKeyword: { $$ = &ast.FuncCallExpr{FnName: $1.(string), Args: []ast.ExprNode{$3.(ast.ExprNode)}} } -| "DATE_ADD" '(' Expression ',' "INTERVAL" Expression TimeUnit ')' +| DateArithOpt '(' Expression ',' "INTERVAL" Expression TimeUnit ')' { $$ = &ast.FuncDateArithExpr{ - Op:ast.DateAdd, - Unit: $7.(string), - Date: $3.(ast.ExprNode), - Interval: $6.(ast.ExprNode), - } - } -| "DATE_SUB" '(' Expression ',' "INTERVAL" Expression TimeUnit ')' - { - $$ = &ast.FuncDateArithExpr{ - Op:ast.DateSub, - Unit: $7.(string), + Op:$1.(ast.DateArithType), + Unit: $7.(string), Date: $3.(ast.ExprNode), Interval: $6.(ast.ExprNode), } @@ -2321,6 +2317,24 @@ FunctionCallNonKeyword: $$ = &ast.FuncCallExpr{FnName: $1.(string), Args: $3.([]ast.ExprNode)} } +DateArithOpt: + "ADDDATE" + { + $$ = ast.DateAdd + } +| "DATE_ADD" + { + $$ = ast.DateAdd + } +| "DATE_SUB" + { + $$ = ast.DateSub + } +| "SUBDATE" + { + $$ = ast.DateSub + } + TrimDirection: "BOTH" { diff --git a/parser/parser_test.go b/parser/parser_test.go index 50d6e5cf2d..56f3acdc80 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -421,6 +421,28 @@ func (s *testParserSuite) TestBuiltin(c *C) { {`select date_add("2011-11-11 10:10:10.123456", interval "11 10" day_hour)`, true}, {`select date_add("2011-11-11 10:10:10.123456", interval "11-11" year_month)`, true}, + // For adddate + {`select adddate("2011-11-11 10:10:10.123456", interval 10 microsecond)`, true}, + {`select adddate("2011-11-11 10:10:10.123456", interval 10 second)`, true}, + {`select adddate("2011-11-11 10:10:10.123456", interval 10 minute)`, true}, + {`select adddate("2011-11-11 10:10:10.123456", interval 10 hour)`, true}, + {`select adddate("2011-11-11 10:10:10.123456", interval 10 day)`, true}, + {`select adddate("2011-11-11 10:10:10.123456", interval 1 week)`, true}, + {`select adddate("2011-11-11 10:10:10.123456", interval 1 month)`, true}, + {`select adddate("2011-11-11 10:10:10.123456", interval 1 quarter)`, true}, + {`select adddate("2011-11-11 10:10:10.123456", interval 1 year)`, true}, + {`select adddate("2011-11-11 10:10:10.123456", interval "10.10" second_microsecond)`, true}, + {`select adddate("2011-11-11 10:10:10.123456", interval "10:10.10" minute_microsecond)`, true}, + {`select adddate("2011-11-11 10:10:10.123456", interval "10:10" minute_second)`, true}, + {`select adddate("2011-11-11 10:10:10.123456", interval "10:10:10.10" hour_microsecond)`, true}, + {`select adddate("2011-11-11 10:10:10.123456", interval "10:10:10" hour_second)`, true}, + {`select adddate("2011-11-11 10:10:10.123456", interval "10:10" hour_minute)`, true}, + {`select adddate("2011-11-11 10:10:10.123456", interval "11 10:10:10.10" day_microsecond)`, true}, + {`select adddate("2011-11-11 10:10:10.123456", interval "11 10:10:10" day_second)`, true}, + {`select adddate("2011-11-11 10:10:10.123456", interval "11 10:10" day_minute)`, true}, + {`select adddate("2011-11-11 10:10:10.123456", interval "11 10" day_hour)`, true}, + {`select adddate("2011-11-11 10:10:10.123456", interval "11-11" year_month)`, true}, + // For date_sub {`select date_sub("2011-11-11 10:10:10.123456", interval 10 microsecond)`, true}, {`select date_sub("2011-11-11 10:10:10.123456", interval 10 second)`, true}, @@ -442,6 +464,28 @@ func (s *testParserSuite) TestBuiltin(c *C) { {`select date_sub("2011-11-11 10:10:10.123456", interval "11 10:10" day_minute)`, true}, {`select date_sub("2011-11-11 10:10:10.123456", interval "11 10" day_hour)`, true}, {`select date_sub("2011-11-11 10:10:10.123456", interval "11-11" year_month)`, true}, + + // For subdate + {`select subdate("2011-11-11 10:10:10.123456", interval 10 microsecond)`, true}, + {`select subdate("2011-11-11 10:10:10.123456", interval 10 second)`, true}, + {`select subdate("2011-11-11 10:10:10.123456", interval 10 minute)`, true}, + {`select subdate("2011-11-11 10:10:10.123456", interval 10 hour)`, true}, + {`select subdate("2011-11-11 10:10:10.123456", interval 10 day)`, true}, + {`select subdate("2011-11-11 10:10:10.123456", interval 1 week)`, true}, + {`select subdate("2011-11-11 10:10:10.123456", interval 1 month)`, true}, + {`select subdate("2011-11-11 10:10:10.123456", interval 1 quarter)`, true}, + {`select subdate("2011-11-11 10:10:10.123456", interval 1 year)`, true}, + {`select subdate("2011-11-11 10:10:10.123456", interval "10.10" second_microsecond)`, true}, + {`select subdate("2011-11-11 10:10:10.123456", interval "10:10.10" minute_microsecond)`, true}, + {`select subdate("2011-11-11 10:10:10.123456", interval "10:10" minute_second)`, true}, + {`select subdate("2011-11-11 10:10:10.123456", interval "10:10:10.10" hour_microsecond)`, true}, + {`select subdate("2011-11-11 10:10:10.123456", interval "10:10:10" hour_second)`, true}, + {`select subdate("2011-11-11 10:10:10.123456", interval "10:10" hour_minute)`, true}, + {`select subdate("2011-11-11 10:10:10.123456", interval "11 10:10:10.10" day_microsecond)`, true}, + {`select subdate("2011-11-11 10:10:10.123456", interval "11 10:10:10" day_second)`, true}, + {`select subdate("2011-11-11 10:10:10.123456", interval "11 10:10" day_minute)`, true}, + {`select subdate("2011-11-11 10:10:10.123456", interval "11 10" day_hour)`, true}, + {`select subdate("2011-11-11 10:10:10.123456", interval "11-11" year_month)`, true}, } s.RunTest(c, table) } diff --git a/parser/scanner.l b/parser/scanner.l index 191eaef35e..a7592c6957 100644 --- a/parser/scanner.l +++ b/parser/scanner.l @@ -266,6 +266,7 @@ z [zZ] abs {a}{b}{s} add {a}{d}{d} +adddate {a}{d}{d}{d}{a}{t}{e} after {a}{f}{t}{e}{r} all {a}{l}{l} alter {a}{l}{t}{e}{r} @@ -413,6 +414,7 @@ share {s}{h}{a}{r}{e} show {s}{h}{o}{w} some {s}{o}{m}{e} start {s}{t}{a}{r}{t} +subdate {s}{u}{b}{d}{a}{t}{e} substring {s}{u}{b}{s}{t}{r}{i}{n}{g} substring_index {s}{u}{b}{s}{t}{r}{i}{n}{g}_{i}{n}{d}{e}{x} sum {s}{u}{m} @@ -595,6 +597,7 @@ year_month {y}{e}{a}{r}_{m}{o}{n}{t}{h} {abs} lval.item = string(l.val) return abs {add} return add +{adddate} return addDate {after} lval.item = string(l.val) return after {all} return all @@ -658,10 +661,8 @@ year_month {y}{e}{a}{r}_{m}{o}{n}{t}{h} {database} lval.item = string(l.val) return database {databases} return databases -{date_add} lval.item = string(l.val) - return dateAdd -{date_sub} lval.item = string(l.val) - return dateSub +{date_add} return dateAdd +{date_sub} return dateSub {day} lval.item = string(l.val) return day {dayofweek} lval.item = string(l.val) @@ -850,6 +851,7 @@ year_month {y}{e}{a}{r}_{m}{o}{n}{t}{h} {set} return set {share} return share {show} return show +{subdate} return subDate {substring} lval.item = string(l.val) return substring {substring_index} lval.item = string(l.val) From 70ce8e8ff3bc952cfff4fb1290700b98babe4062 Mon Sep 17 00:00:00 2001 From: Shen Li Date: Thu, 5 Nov 2015 22:44:27 +0800 Subject: [PATCH 06/35] tidb: Address comment --- bootstrap.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/bootstrap.go b/bootstrap.go index d934f5a55e..f5683617a0 100644 --- a/bootstrap.go +++ b/bootstrap.go @@ -150,12 +150,9 @@ func checkBootstrappedVar(s Session) (bool, error) { } r := rs[0] row, err := r.Next() - if err != nil { + if err != nil || row == nil { return false, errors.Trace(err) } - if row == nil { - return false, nil - } return row.Data[0].(string) == bootstrappedVarTrue, nil } From 91ddd72604c6b9670cb51bbcd190038b9ce38577 Mon Sep 17 00:00:00 2001 From: ZhiFeng Hu Date: Fri, 6 Nov 2015 13:41:29 +0800 Subject: [PATCH 07/35] Dockerfile to build tidb, for #522, close #526 You must install docker first, then docker build --rm -t tidb-server . after build success, you may see images via docker images docker run -d --name tidb-server tidb-server Signed-off-by: ZhiFeng Hu --- Dockerfile | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000..f8c33a15b3 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +FROM golang + +VOLUME /opt + +RUN apt-get update && apt-get install -y wget git make ; \ + cd /opt ; \ + export PATH=$GOROOT/bin:$GOPATH/bin:$PATH ; \ + go get -v github.com/pingcap/tidb ; \ + cd $GOPATH/src/github.com/pingcap/tidb ; \ + make ; make server ; cp tidb-server/tidb-server /usr/bin/ + +EXPOSE 4000 + +CMD ["/usr/bin/tidb-server"] + From 1e01c693840d8a15ee32db018b22acd1be4b4bc5 Mon Sep 17 00:00:00 2001 From: shenli Date: Fri, 6 Nov 2015 16:41:21 +0800 Subject: [PATCH 08/35] tidb: Clean up log in session.ExecRestrictedSQL --- session.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/session.go b/session.go index 3b64312a3f..6c0a37524e 100644 --- a/session.go +++ b/session.go @@ -256,7 +256,7 @@ func (s *session) ExecRestrictedSQL(ctx context.Context, sql string) (rset.Recor // Check statement for some restriction // For example only support DML on system meta table. // TODO: Add more restrictions. - log.Infof("Executing %s [%s]", st.OriginText(), sql) + log.Debugf("Executing %s [%s]", st.OriginText(), sql) ctx.SetValue(&sqlexec.RestrictedSQLExecutorKeyType{}, true) defer ctx.ClearValue(&sqlexec.RestrictedSQLExecutorKeyType{}) rs, err := st.Exec(ctx) From 69b2c312912d9ce73b38586079595fbc52a4f7c0 Mon Sep 17 00:00:00 2001 From: xia Date: Fri, 6 Nov 2015 17:30:52 +0800 Subject: [PATCH 09/35] expression: support adddate or subdate function with days form --- expression/date_arith.go | 53 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 5 deletions(-) diff --git a/expression/date_arith.go b/expression/date_arith.go index 0a9a6c0de3..79f8f5f22d 100644 --- a/expression/date_arith.go +++ b/expression/date_arith.go @@ -15,6 +15,7 @@ package expression import ( "fmt" + "regexp" "strings" "time" @@ -28,24 +29,33 @@ import ( type DateArithType byte const ( + // AddDate is to run adddate function option. + // See: https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_adddate + AddDate DateArithType = iota + 1 // DateAdd is to run date_add function option. // See: https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_date-add - DateAdd DateArithType = iota + 1 + DateAdd // DateSub is to run date_sub function option. // See: https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_date-sub DateSub + // SubDate is to run subdate function option. + // See: https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_subdate + SubDate + // DateArithDaysForm is to run adddate or subdate function with days form Flag. + DateArithDaysForm ) // DateArith is used for dealing with addition and substraction of time. type DateArith struct { Op DateArithType - Unit string + Form DateArithType Date Expression + Unit string Interval Expression } func (da *DateArith) isAdd() bool { - if da.Op == DateAdd { + if da.Op == AddDate || da.Op == DateAdd { return true } @@ -71,10 +81,19 @@ func (da *DateArith) Accept(v Visitor) (Expression, error) { // String implements the Expression String interface. func (da *DateArith) String() string { var str string - if da.isAdd() { + switch da.Op { + case AddDate: + str = "ADDDATE" + case DateAdd: str = "DATE_ADD" - } else { + case DateSub: str = "DATE_SUB" + case SubDate: + str = "SUBDATE" + } + + if da.Form == DateArithDaysForm { + return fmt.Sprintf("%s(%s, %s)", str, da.Date, da.Interval) } return fmt.Sprintf("%s(%s, INTERVAL %s %s)", str, da.Date, da.Interval, strings.ToUpper(da.Unit)) @@ -107,6 +126,7 @@ func (da *DateArith) evalArgs(ctx context.Context, args map[interface{}]interfac if dVal == nil || err != nil { return mysql.ZeroTimestamp, 0, 0, 0, 0, errors.Trace(err) } + dValStr, err := types.ToString(dVal) if err != nil { return mysql.ZeroTimestamp, 0, 0, 0, 0, errors.Trace(err) @@ -126,6 +146,13 @@ func (da *DateArith) evalArgs(ctx context.Context, args map[interface{}]interfac if iVal == nil || err != nil { return mysql.ZeroTimestamp, 0, 0, 0, 0, errors.Trace(err) } + // handle adddate(expr,days) or subdate(expr,days) form + if da.Form == DateArithDaysForm { + if iVal, err = da.evalDaysForm(iVal); err != nil { + return mysql.ZeroTimestamp, 0, 0, 0, 0, errors.Trace(err) + } + } + iValStr, err := types.ToString(iVal) if err != nil { return mysql.ZeroTimestamp, 0, 0, 0, 0, errors.Trace(err) @@ -137,3 +164,19 @@ func (da *DateArith) evalArgs(ctx context.Context, args map[interface{}]interfac return t, years, months, days, durations, nil } + +func (da *DateArith) evalDaysForm(val interface{}) (interface{}, error) { + switch val.(type) { + case string: + if strings.ToLower(val.(string)) == "false" { + return 0, nil + } + if strings.ToLower(val.(string)) == "true" { + return 1, nil + } + reg := regexp.MustCompile(`[\d]+`) + val = reg.FindString(val.(string)) + } + + return types.ToInt64(val) +} From 7eb1f451fd72513c979394d5e508fbfe20f9eeea Mon Sep 17 00:00:00 2001 From: xia Date: Fri, 6 Nov 2015 17:31:53 +0800 Subject: [PATCH 10/35] *: support ast --- ast/functions.go | 26 ++++++++++++++++++++------ optimizer/convert_expr.go | 3 +++ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/ast/functions.go b/ast/functions.go index 7faab4c6e1..658cb16e5c 100644 --- a/ast/functions.go +++ b/ast/functions.go @@ -338,26 +338,40 @@ func (n *FuncTrimExpr) IsStatic() bool { return n.Str.IsStatic() && n.RemStr.IsStatic() } -// DateArithType is type for DateArith option. +// DateArithType is type for DateArith type. type DateArithType byte const ( + // AddDate is to run adddate function option. + // See: https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_adddate + AddDate DateArithType = iota + 1 // DateAdd is to run date_add function option. // See: https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_date-add - DateAdd DateArithType = iota + 1 + DateAdd // DateSub is to run date_sub function option. // See: https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_date-sub DateSub + // SubDate is to run subdate function option. + // See: https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_subdate + SubDate + // DateArithDaysForm is to run adddate or subdate function with days form. + DateArithDaysForm ) +// DateArithInterval is the struct of DateArith interval part. +type DateArithInterval struct { + Form DateArithType + Unit string + Interval ExprNode +} + // FuncDateArithExpr is the struct for date arithmetic functions. type FuncDateArithExpr struct { funcNode - Op DateArithType - Unit string - Date ExprNode - Interval ExprNode + Op DateArithType + Date ExprNode + DateArithInterval } // Accept implements Node Accept interface. diff --git a/optimizer/convert_expr.go b/optimizer/convert_expr.go index a136819ae1..9fe63dd268 100644 --- a/optimizer/convert_expr.go +++ b/optimizer/convert_expr.go @@ -410,6 +410,9 @@ func (c *expressionConverter) funcDateArith(v *ast.FuncDateArithExpr) { case ast.DateSub: oldDateArith.Op = expression.DateSub } + if v.Form == ast.DateArithDaysForm { + oldDateArith.Form = expression.DateArithDaysForm + } c.exprMap[v] = oldDateArith } From c173e20814435227704fbb3956b7e9f0feefd9a9 Mon Sep 17 00:00:00 2001 From: xia Date: Fri, 6 Nov 2015 17:32:18 +0800 Subject: [PATCH 11/35] parser: support adddate or subdate function with days form --- parser/parser.y | 47 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/parser/parser.y b/parser/parser.y index 856c98553d..a210a518fa 100644 --- a/parser/parser.y +++ b/parser/parser.y @@ -375,7 +375,9 @@ import ( CreateTableStmt "CREATE TABLE statement" CreateUserStmt "CREATE User statement" CrossOpt "Cross join option" - DateArithOpt "Date Arith option" + DateArithOpt "Date arith dateadd or datesub option" + DateArithMultiFormsOpt "Date arith adddate or subdate option" + DateArithInterval "Date arith interval part" DatabaseSym "DATABASE or SCHEMA" DBName "Database Name" DeallocateSym "Deallocate or drop" @@ -2136,10 +2138,19 @@ FunctionCallNonKeyword: | DateArithOpt '(' Expression ',' "INTERVAL" Expression TimeUnit ')' { $$ = &ast.FuncDateArithExpr{ - Op:$1.(ast.DateArithType), - Unit: $7.(string), + Op: $1.(ast.DateArithType), Date: $3.(ast.ExprNode), - Interval: $6.(ast.ExprNode), + DateArithInterval: ast.DateArithInterval{ + Unit: $7.(string), + Interval: $6.(ast.ExprNode)}, + } + } +| DateArithMultiFormsOpt '(' Expression ',' DateArithInterval')' + { + $$ = &ast.FuncDateArithExpr{ + Op: $1.(ast.DateArithType), + Date: $3.(ast.ExprNode), + DateArithInterval: $5.(ast.DateArithInterval), } } | "EXTRACT" '(' TimeUnit "FROM" Expression ')' @@ -2318,11 +2329,7 @@ FunctionCallNonKeyword: } DateArithOpt: - "ADDDATE" - { - $$ = ast.DateAdd - } -| "DATE_ADD" + "DATE_ADD" { $$ = ast.DateAdd } @@ -2330,9 +2337,29 @@ DateArithOpt: { $$ = ast.DateSub } + +DateArithMultiFormsOpt: + "ADDDATE" + { + $$ = ast.AddDate + } | "SUBDATE" { - $$ = ast.DateSub + $$ = ast.SubDate + } + +DateArithInterval: + Expression + { + $$ = ast.DateArithInterval{ + Form: ast.DateArithDaysForm, + Unit: "day", + Interval: $1.(ast.ExprNode), + } + } +| "INTERVAL" Expression TimeUnit + { + $$ = ast.DateArithInterval{Unit: $3.(string), Interval: $2.(ast.ExprNode)} } TrimDirection: From 5ce49e128db47e0f6fe6fbde5460bca1932e3d22 Mon Sep 17 00:00:00 2001 From: xia Date: Fri, 6 Nov 2015 17:33:05 +0800 Subject: [PATCH 12/35] *: add test --- expression/date_arith_test.go | 91 ++++++++++++++++++++++++++++++++--- parser/parser_test.go | 6 +++ 2 files changed, 89 insertions(+), 8 deletions(-) diff --git a/expression/date_arith_test.go b/expression/date_arith_test.go index 2775d7d914..d33ad166e3 100644 --- a/expression/date_arith_test.go +++ b/expression/date_arith_test.go @@ -38,6 +38,14 @@ func (t *testDateArithSuite) TestDateArith(c *C) { c.Assert(err, IsNil) e.Op = DateSub c.Assert(e.String(), Equals, `DATE_SUB("2011-11-11 10:10:10", INTERVAL "1" DAY)`) + e.Op = AddDate + c.Assert(e.String(), Equals, `ADDDATE("2011-11-11 10:10:10", INTERVAL "1" DAY)`) + e.Op = SubDate + c.Assert(e.String(), Equals, `SUBDATE("2011-11-11 10:10:10", INTERVAL "1" DAY)`) + e.Form = DateArithDaysForm + c.Assert(e.String(), Equals, `SUBDATE("2011-11-11 10:10:10", "1")`) + e.Op = AddDate + c.Assert(e.String(), Equals, `ADDDATE("2011-11-11 10:10:10", "1")`) // Test null. nullTbl := []struct { @@ -46,8 +54,8 @@ func (t *testDateArithSuite) TestDateArith(c *C) { Date interface{} Interval interface{} }{ - {DateAdd, "DAY", nil, "1"}, - {DateAdd, "DAY", input, nil}, + {AddDate, "DAY", nil, "1"}, + {AddDate, "DAY", input, nil}, } for _, t := range nullTbl { e := &DateArith{ @@ -59,10 +67,18 @@ func (t *testDateArithSuite) TestDateArith(c *C) { v, err := e.Eval(nil, nil) c.Assert(err, IsNil) c.Assert(v, IsNil) + e.Op = DateAdd + v, err = e.Eval(nil, nil) + c.Assert(err, IsNil) + c.Assert(v, IsNil) e.Op = DateSub v, err = e.Eval(nil, nil) c.Assert(err, IsNil) c.Assert(v, IsNil) + e.Op = SubDate + v, err = e.Eval(nil, nil) + c.Assert(err, IsNil) + c.Assert(v, IsNil) } // Test eval. @@ -116,6 +132,56 @@ func (t *testDateArithSuite) TestDateArith(c *C) { value, ok = v.(mysql.Time) c.Assert(ok, IsTrue) c.Assert(value.String(), Equals, t.SubExpect) + + e.Op = AddDate + v, err = e.Eval(nil, nil) + c.Assert(err, IsNil) + value, ok = v.(mysql.Time) + c.Assert(ok, IsTrue) + c.Assert(value.String(), Equals, t.AddExpect) + + e.Op = SubDate + v, err = e.Eval(nil, nil) + c.Assert(err, IsNil) + value, ok = v.(mysql.Time) + c.Assert(ok, IsTrue) + c.Assert(value.String(), Equals, t.SubExpect) + } + + // Test eval for adddate and subdate with days form + tblDays := []struct { + Interval interface{} + AddExpect string + SubExpect string + }{ + {"20", "2011-12-01 10:10:10", "2011-10-22 10:10:10"}, + {19.88, "2011-12-01 10:10:10", "2011-10-22 10:10:10"}, + {"19.88", "2011-11-30 10:10:10", "2011-10-23 10:10:10"}, + {"20-11", "2011-12-01 10:10:10", "2011-10-22 10:10:10"}, + {"20,11", "2011-12-01 10:10:10", "2011-10-22 10:10:10"}, + {"1000", "2014-08-07 10:10:10", "2009-02-14 10:10:10"}, + {"true", "2011-11-12 10:10:10", "2011-11-10 10:10:10"}, + } + for _, t := range tblDays { + e := &DateArith{ + Op: AddDate, + Form: DateArithDaysForm, + Unit: "day", + Date: Value{Val: input}, + Interval: Value{Val: t.Interval}, + } + v, err := e.Eval(nil, nil) + c.Assert(err, IsNil) + value, ok := v.(mysql.Time) + c.Assert(ok, IsTrue) + c.Assert(value.String(), Equals, t.AddExpect) + + e.Op = DateSub + v, err = e.Eval(nil, nil) + c.Assert(err, IsNil) + value, ok = v.(mysql.Time) + c.Assert(ok, IsTrue) + c.Assert(value.String(), Equals, t.SubExpect) } // Test error. @@ -147,12 +213,21 @@ func (t *testDateArithSuite) TestDateArith(c *C) { v, err := e.Eval(nil, nil) c.Assert(err, NotNil, Commentf("%s", v)) - e = &DateArith{ - Op: DateSub, - Unit: t.Unit, - Date: Value{Val: input}, - Interval: Value{Val: t.Interval}, - } + e.Op = DateSub + _, err = e.Eval(nil, nil) + c.Assert(err, NotNil) + e.Date = Value{Val: errInput} + v, err = e.Eval(nil, nil) + c.Assert(err, NotNil, Commentf("%s", v)) + + e.Op = AddDate + _, err = e.Eval(nil, nil) + c.Assert(err, NotNil) + e.Date = Value{Val: errInput} + v, err = e.Eval(nil, nil) + c.Assert(err, NotNil, Commentf("%s", v)) + + e.Op = SubDate _, err = e.Eval(nil, nil) c.Assert(err, NotNil) e.Date = Value{Val: errInput} diff --git a/parser/parser_test.go b/parser/parser_test.go index 56f3acdc80..09a21e4a88 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -442,6 +442,9 @@ func (s *testParserSuite) TestBuiltin(c *C) { {`select adddate("2011-11-11 10:10:10.123456", interval "11 10:10" day_minute)`, true}, {`select adddate("2011-11-11 10:10:10.123456", interval "11 10" day_hour)`, true}, {`select adddate("2011-11-11 10:10:10.123456", interval "11-11" year_month)`, true}, + {`select adddate("2011-11-11 10:10:10.123456", 10)`, true}, + {`select adddate("2011-11-11 10:10:10.123456", 0.10)`, true}, + {`select adddate("2011-11-11 10:10:10.123456", "11,11")`, true}, // For date_sub {`select date_sub("2011-11-11 10:10:10.123456", interval 10 microsecond)`, true}, @@ -486,6 +489,9 @@ func (s *testParserSuite) TestBuiltin(c *C) { {`select subdate("2011-11-11 10:10:10.123456", interval "11 10:10" day_minute)`, true}, {`select subdate("2011-11-11 10:10:10.123456", interval "11 10" day_hour)`, true}, {`select subdate("2011-11-11 10:10:10.123456", interval "11-11" year_month)`, true}, + {`select adddate("2011-11-11 10:10:10.123456", 10)`, true}, + {`select adddate("2011-11-11 10:10:10.123456", 0.10)`, true}, + {`select adddate("2011-11-11 10:10:10.123456", "11,11")`, true}, } s.RunTest(c, table) } From 74292a6aa82cacf0aa96d2eea9080ee210b1a210 Mon Sep 17 00:00:00 2001 From: ZhiFeng Hu Date: Fri, 6 Nov 2015 17:41:00 +0800 Subject: [PATCH 13/35] Replace go get -v to go get -d as @siddontang told Signed-off-by: ZhiFeng Hu --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index f8c33a15b3..110acb1688 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,7 @@ VOLUME /opt RUN apt-get update && apt-get install -y wget git make ; \ cd /opt ; \ export PATH=$GOROOT/bin:$GOPATH/bin:$PATH ; \ - go get -v github.com/pingcap/tidb ; \ + go get -d github.com/pingcap/tidb ; \ cd $GOPATH/src/github.com/pingcap/tidb ; \ make ; make server ; cp tidb-server/tidb-server /usr/bin/ From d184f69349c575258998d992b332edfc4df2c1f7 Mon Sep 17 00:00:00 2001 From: Ewan Chou Date: Fri, 6 Nov 2015 18:04:29 +0800 Subject: [PATCH 14/35] terror: implement juju/errors interfaces to support printing error stack. Also make base error immutable. --- terror/terror.go | 75 ++++++++++++++++++++++++++++++++++++------- terror/terror_test.go | 13 ++++++++ 2 files changed, 77 insertions(+), 11 deletions(-) diff --git a/terror/terror.go b/terror/terror.go index 68e33f9ffc..f1c282b22c 100644 --- a/terror/terror.go +++ b/terror/terror.go @@ -15,8 +15,10 @@ package terror import ( "fmt" + "runtime" "strconv" + "github.com/cockroachdb/cockroach/util/log" "github.com/juju/errors" ) @@ -91,7 +93,7 @@ func (ec ErrClass) EqualClass(err error) bool { return false } if te, ok := e.(*Error); ok { - return te.Class == ec + return te.class == ec } return false } @@ -105,32 +107,83 @@ func (ec ErrClass) NotEqualClass(err error) bool { // Usually used to create base *Error. func (ec ErrClass) New(code ErrCode, message string) *Error { return &Error{ - Class: ec, - Code: code, - Message: message, + class: ec, + code: code, + message: message, + isBase: true, } } // Error implements error interface and adds integer Class and Code, so // errors with different message can be compared. type Error struct { - Class ErrClass - Code ErrCode - Message string + class ErrClass + code ErrCode + message string + cause error + previous error + file string + line int + // base error is created by error class, should not be modified. + isBase bool +} + +// Class returns ErrClass +func (e *Error) Class() ErrClass { + return e.class +} + +// Code returns ErrCode +func (e *Error) Code() ErrCode { + return e.code +} + +// Location returns the location where the error is created, +// implements juju/errors locationer interface. +func (e *Error) Location() (file string, line int) { + return e.file, e.line } // Error implements error interface. func (e *Error) Error() string { - return fmt.Sprintf("[%s:%d]%s", e.Class, e.Code, e.Message) + return fmt.Sprintf("[%s:%d]%s", e.class, e.code, e.message) } // Gen generates a new *Error with the same class and code, and a new formatted message. func (e *Error) Gen(format string, args ...interface{}) *Error { err := *e - err.Message = fmt.Sprintf(format, args...) + err.isBase = false + err.message = fmt.Sprintf(format, args...) + _, err.file, err.line, _ = runtime.Caller(1) return &err } +// Wrap wraps an error and returns itself. +func (e *Error) Wrap(err error) *Error { + if e.isBase { + log.Fatal("base error should not call Wrap method.") + } + e.previous = err + e.cause = errors.Cause(err) + return e +} + +// Cause returns the cause error, implements juju/errors causer interface. +func (e *Error) Cause() error { + return e.cause +} + +// Message returns the message of the error, implements juju/errors wrapper interface. +func (e *Error) Message() string { + return e.message +} + +// Underlying returns the Previous error, or nil +// if there is none, implements juju/errors wrapper interface. +func (e *Error) Underlying() error { + return e.previous +} + // Equal checks if err is equal to e. func (e *Error) Equal(err error) bool { originErr := errors.Cause(err) @@ -138,7 +191,7 @@ func (e *Error) Equal(err error) bool { return false } inErr, ok := originErr.(*Error) - return ok && e.Class == inErr.Class && e.Code == inErr.Code + return ok && e.class == inErr.class && e.code == inErr.code } // NotEqual checks if err is not equal to e. @@ -162,7 +215,7 @@ func ErrorEqual(err1, err2 error) bool { te1, ok1 := e1.(*Error) te2, ok2 := e2.(*Error) if ok1 && ok2 { - return te1.Class == te2.Class && te1.Code == te2.Code + return te1.class == te2.class && te1.code == te2.code } return e1.Error() == e2.Error() diff --git a/terror/terror_test.go b/terror/terror_test.go index e96d6c2754..16bd0d2814 100644 --- a/terror/terror_test.go +++ b/terror/terror_test.go @@ -14,6 +14,7 @@ package terror import ( + "fmt" "testing" "github.com/juju/errors" @@ -49,6 +50,18 @@ func (s *testTErrorSuite) TestTError(c *C) { c.Assert(optimizerErr.Equal(errors.New("abc")), IsFalse) } +var predefinedErr = ClassExecutor.New(ErrCode(123), "predefiend error") + +func example() error { + var err = errors.New("cause error") + return predefinedErr.Gen("error message:%s", "abc").Wrap(err) +} + +func (s *testTErrorSuite) TestExample(c *C) { + err := example() + fmt.Println(errors.ErrorStack(err)) +} + func (s *testTErrorSuite) TestErrorEqual(c *C) { e1 := errors.New("test error") c.Assert(e1, NotNil) From de8f85e3f534a2de3bbbaa0fb79f11e69591c400 Mon Sep 17 00:00:00 2001 From: Ewan Chou Date: Fri, 6 Nov 2015 18:07:21 +0800 Subject: [PATCH 15/35] terror: fix wrong import --- terror/terror.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terror/terror.go b/terror/terror.go index f1c282b22c..3835b9fc04 100644 --- a/terror/terror.go +++ b/terror/terror.go @@ -18,8 +18,8 @@ import ( "runtime" "strconv" - "github.com/cockroachdb/cockroach/util/log" "github.com/juju/errors" + "github.com/ngaut/log" ) // Common base error instances. From 807b8979396bc12c4509a44149e2788239b3077a Mon Sep 17 00:00:00 2001 From: Ewan Chou Date: Fri, 6 Nov 2015 18:33:11 +0800 Subject: [PATCH 16/35] terror: remove cause functionality. Because that make equal check invalid. --- terror/terror.go | 43 +++++-------------------------------------- terror/terror_test.go | 3 +-- 2 files changed, 6 insertions(+), 40 deletions(-) diff --git a/terror/terror.go b/terror/terror.go index 3835b9fc04..3845392d54 100644 --- a/terror/terror.go +++ b/terror/terror.go @@ -19,7 +19,6 @@ import ( "strconv" "github.com/juju/errors" - "github.com/ngaut/log" ) // Common base error instances. @@ -110,22 +109,17 @@ func (ec ErrClass) New(code ErrCode, message string) *Error { class: ec, code: code, message: message, - isBase: true, } } // Error implements error interface and adds integer Class and Code, so // errors with different message can be compared. type Error struct { - class ErrClass - code ErrCode - message string - cause error - previous error - file string - line int - // base error is created by error class, should not be modified. - isBase bool + class ErrClass + code ErrCode + message string + file string + line int } // Class returns ErrClass @@ -152,38 +146,11 @@ func (e *Error) Error() string { // Gen generates a new *Error with the same class and code, and a new formatted message. func (e *Error) Gen(format string, args ...interface{}) *Error { err := *e - err.isBase = false err.message = fmt.Sprintf(format, args...) _, err.file, err.line, _ = runtime.Caller(1) return &err } -// Wrap wraps an error and returns itself. -func (e *Error) Wrap(err error) *Error { - if e.isBase { - log.Fatal("base error should not call Wrap method.") - } - e.previous = err - e.cause = errors.Cause(err) - return e -} - -// Cause returns the cause error, implements juju/errors causer interface. -func (e *Error) Cause() error { - return e.cause -} - -// Message returns the message of the error, implements juju/errors wrapper interface. -func (e *Error) Message() string { - return e.message -} - -// Underlying returns the Previous error, or nil -// if there is none, implements juju/errors wrapper interface. -func (e *Error) Underlying() error { - return e.previous -} - // Equal checks if err is equal to e. func (e *Error) Equal(err error) bool { originErr := errors.Cause(err) diff --git a/terror/terror_test.go b/terror/terror_test.go index 16bd0d2814..9709a92a73 100644 --- a/terror/terror_test.go +++ b/terror/terror_test.go @@ -53,8 +53,7 @@ func (s *testTErrorSuite) TestTError(c *C) { var predefinedErr = ClassExecutor.New(ErrCode(123), "predefiend error") func example() error { - var err = errors.New("cause error") - return predefinedErr.Gen("error message:%s", "abc").Wrap(err) + return predefinedErr.Gen("error message:%s", "abc") } func (s *testTErrorSuite) TestExample(c *C) { From f1a4be756b9dce476eca4b61689ce4d759d81ae1 Mon Sep 17 00:00:00 2001 From: Ewan Chou Date: Fri, 6 Nov 2015 19:26:03 +0800 Subject: [PATCH 17/35] terror: improve test. --- terror/terror_test.go | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/terror/terror_test.go b/terror/terror_test.go index 9709a92a73..9cb32b1273 100644 --- a/terror/terror_test.go +++ b/terror/terror_test.go @@ -14,11 +14,11 @@ package terror import ( - "fmt" "testing" "github.com/juju/errors" . "github.com/pingcap/check" + "strings" ) func TestT(t *testing.T) { @@ -53,12 +53,22 @@ func (s *testTErrorSuite) TestTError(c *C) { var predefinedErr = ClassExecutor.New(ErrCode(123), "predefiend error") func example() error { + err := call() + return errors.Trace(err) +} + +func call() error { return predefinedErr.Gen("error message:%s", "abc") } -func (s *testTErrorSuite) TestExample(c *C) { +func (s *testTErrorSuite) TestTraceAndLocation(c *C) { err := example() - fmt.Println(errors.ErrorStack(err)) + stack := errors.ErrorStack(err) + lines := strings.Split(stack, "\n") + c.Assert(len(lines), Equals, 2) + for _, v := range lines { + c.Assert(strings.Contains(v, "terror_test.go"), IsTrue) + } } func (s *testTErrorSuite) TestErrorEqual(c *C) { From 3fa25044d90a8593886cdbaa1c852fa908ae2f28 Mon Sep 17 00:00:00 2001 From: ZhiFeng Hu Date: Fri, 6 Nov 2015 20:57:45 +0800 Subject: [PATCH 18/35] column/column.go fix missing : symbol Missing : symbol? Signed-off-by: ZhiFeng Hu --- column/column.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/column/column.go b/column/column.go index d6831c8f97..2c6518bb2a 100644 --- a/column/column.go +++ b/column/column.go @@ -56,7 +56,7 @@ func (c *Col) String() string { // FindCol finds column in cols by name. func FindCol(cols []*Col, name string) (c *Col) { - for _, c = range cols { + for _, c := range cols { if strings.EqualFold(c.Name.O, name) { return } From 88ef8dd9eb7291efcf5aaf85de2b3030a07c8709 Mon Sep 17 00:00:00 2001 From: ZhiFeng Hu Date: Fri, 6 Nov 2015 21:21:57 +0800 Subject: [PATCH 19/35] If we found c, then return c Signed-off-by: ZhiFeng Hu --- column/column.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/column/column.go b/column/column.go index 2c6518bb2a..b0b833c6f9 100644 --- a/column/column.go +++ b/column/column.go @@ -58,7 +58,7 @@ func (c *Col) String() string { func FindCol(cols []*Col, name string) (c *Col) { for _, c := range cols { if strings.EqualFold(c.Name.O, name) { - return + return c } } return nil From b98b6920b9cbbcb8131ff0b1ed6ec700de0c97f5 Mon Sep 17 00:00:00 2001 From: xia Date: Fri, 6 Nov 2015 21:23:11 +0800 Subject: [PATCH 20/35] *: add comments --- ast/functions.go | 17 ++++--- expression/date_arith.go | 87 ++++++++++++++++++----------------- expression/date_arith_test.go | 50 ++------------------ parser/parser.y | 4 +- 4 files changed, 58 insertions(+), 100 deletions(-) diff --git a/ast/functions.go b/ast/functions.go index 658cb16e5c..021ed15e09 100644 --- a/ast/functions.go +++ b/ast/functions.go @@ -342,24 +342,22 @@ func (n *FuncTrimExpr) IsStatic() bool { type DateArithType byte const ( - // AddDate is to run adddate function option. + // DateAdd is to run adddate or date_add function option. // See: https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_adddate - AddDate DateArithType = iota + 1 - // DateAdd is to run date_add function option. // See: https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_date-add - DateAdd - // DateSub is to run date_sub function option. + DateAdd DateArithType = iota + 1 + // DateSub is to run subdate or date_sub function option. + // See: https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_subdate // See: https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_date-sub DateSub - // SubDate is to run subdate function option. - // See: https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_subdate - SubDate - // DateArithDaysForm is to run adddate or subdate function with days form. + // DateArithDaysForm is to run adddate or subdate function with days form Flag. DateArithDaysForm ) // DateArithInterval is the struct of DateArith interval part. type DateArithInterval struct { + // Form is the flag of DateArith running form. + // The function runs with interval or days. Form DateArithType Unit string Interval ExprNode @@ -369,6 +367,7 @@ type DateArithInterval struct { type FuncDateArithExpr struct { funcNode + // Op is used for distinguishing date_add and date_sub. Op DateArithType Date ExprNode DateArithInterval diff --git a/expression/date_arith.go b/expression/date_arith.go index 79f8f5f22d..a14575d9b5 100644 --- a/expression/date_arith.go +++ b/expression/date_arith.go @@ -29,33 +29,40 @@ import ( type DateArithType byte const ( - // AddDate is to run adddate function option. + // DateAdd is to run adddate or date_add function option. // See: https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_adddate - AddDate DateArithType = iota + 1 - // DateAdd is to run date_add function option. // See: https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_date-add - DateAdd - // DateSub is to run date_sub function option. + DateAdd DateArithType = iota + 1 + // DateSub is to run subdate or date_sub function option. + // See: https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_subdate // See: https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_date-sub DateSub - // SubDate is to run subdate function option. - // See: https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_subdate - SubDate // DateArithDaysForm is to run adddate or subdate function with days form Flag. DateArithDaysForm ) // DateArith is used for dealing with addition and substraction of time. type DateArith struct { - Op DateArithType + // Op is used for distinguishing date_add and date_sub. + Op DateArithType + // Form is the flag of DateArith running form. + // The function runs with interval or days. Form DateArithType Date Expression Unit string Interval Expression } +type evalArgsResult struct { + time mysql.Time + year int64 + month int64 + day int64 + duration time.Duration +} + func (da *DateArith) isAdd() bool { - if da.Op == AddDate || da.Op == DateAdd { + if da.Op == DateAdd { return true } @@ -81,19 +88,10 @@ func (da *DateArith) Accept(v Visitor) (Expression, error) { // String implements the Expression String interface. func (da *DateArith) String() string { var str string - switch da.Op { - case AddDate: - str = "ADDDATE" - case DateAdd: + if da.isAdd() { str = "DATE_ADD" - case DateSub: + } else { str = "DATE_SUB" - case SubDate: - str = "SUBDATE" - } - - if da.Form == DateArithDaysForm { - return fmt.Sprintf("%s(%s, %s)", str, da.Date, da.Interval) } return fmt.Sprintf("%s(%s, INTERVAL %s %s)", str, da.Date, da.Interval, strings.ToUpper(da.Unit)) @@ -101,68 +99,73 @@ func (da *DateArith) String() string { // Eval implements the Expression Eval interface. func (da *DateArith) Eval(ctx context.Context, args map[interface{}]interface{}) (interface{}, error) { - t, years, months, days, durations, err := da.evalArgs(ctx, args) - if t.IsZero() || err != nil { + val, err := da.evalArgs(ctx, args) + if val.time.IsZero() || err != nil { return nil, errors.Trace(err) } if !da.isAdd() { - years, months, days, durations = -years, -months, -days, -durations + val.year, val.month, val.day, val.duration = + -val.year, -val.month, -val.day, -val.duration } - t.Time = t.Time.Add(durations) - t.Time = t.Time.AddDate(int(years), int(months), int(days)) + val.time.Time = val.time.Time.Add(val.duration) + val.time.Time = val.time.Time.AddDate(int(val.year), int(val.month), int(val.day)) // "2011-11-11 10:10:20.000000" outputs "2011-11-11 10:10:20". - if t.Time.Nanosecond() == 0 { - t.Fsp = 0 + if val.time.Time.Nanosecond() == 0 { + val.time.Fsp = 0 } - return t, nil + return val.time, nil } func (da *DateArith) evalArgs(ctx context.Context, args map[interface{}]interface{}) ( - mysql.Time, int64, int64, int64, time.Duration, error) { + *evalArgsResult, error) { + ret := &evalArgsResult{time: mysql.ZeroTimestamp} + dVal, err := da.Date.Eval(ctx, args) if dVal == nil || err != nil { - return mysql.ZeroTimestamp, 0, 0, 0, 0, errors.Trace(err) + return ret, errors.Trace(err) } - dValStr, err := types.ToString(dVal) if err != nil { - return mysql.ZeroTimestamp, 0, 0, 0, 0, errors.Trace(err) + return ret, errors.Trace(err) } f := types.NewFieldType(mysql.TypeDatetime) f.Decimal = mysql.MaxFsp dVal, err = types.Convert(dValStr, f) if dVal == nil || err != nil { - return mysql.ZeroTimestamp, 0, 0, 0, 0, errors.Trace(err) + return ret, errors.Trace(err) } - t, ok := dVal.(mysql.Time) + + var ok bool + ret.time, ok = dVal.(mysql.Time) if !ok { - return mysql.ZeroTimestamp, 0, 0, 0, 0, errors.Errorf("need time type, but got %T", dVal) + return ret, errors.Errorf("need time type, but got %T", dVal) } iVal, err := da.Interval.Eval(ctx, args) if iVal == nil || err != nil { - return mysql.ZeroTimestamp, 0, 0, 0, 0, errors.Trace(err) + ret.time = mysql.ZeroTimestamp + return ret, errors.Trace(err) } // handle adddate(expr,days) or subdate(expr,days) form if da.Form == DateArithDaysForm { if iVal, err = da.evalDaysForm(iVal); err != nil { - return mysql.ZeroTimestamp, 0, 0, 0, 0, errors.Trace(err) + return ret, errors.Trace(err) } } iValStr, err := types.ToString(iVal) if err != nil { - return mysql.ZeroTimestamp, 0, 0, 0, 0, errors.Trace(err) + return ret, errors.Trace(err) } - years, months, days, durations, err := mysql.ExtractTimeValue(da.Unit, strings.TrimSpace(iValStr)) + ret.year, ret.month, ret.day, ret.duration, err = mysql.ExtractTimeValue(da.Unit, strings.TrimSpace(iValStr)) if err != nil { - return mysql.ZeroTimestamp, 0, 0, 0, 0, errors.Trace(err) + return ret, errors.Trace(err) } - return t, years, months, days, durations, nil + return ret, nil } func (da *DateArith) evalDaysForm(val interface{}) (interface{}, error) { diff --git a/expression/date_arith_test.go b/expression/date_arith_test.go index d33ad166e3..4f492fc640 100644 --- a/expression/date_arith_test.go +++ b/expression/date_arith_test.go @@ -38,14 +38,6 @@ func (t *testDateArithSuite) TestDateArith(c *C) { c.Assert(err, IsNil) e.Op = DateSub c.Assert(e.String(), Equals, `DATE_SUB("2011-11-11 10:10:10", INTERVAL "1" DAY)`) - e.Op = AddDate - c.Assert(e.String(), Equals, `ADDDATE("2011-11-11 10:10:10", INTERVAL "1" DAY)`) - e.Op = SubDate - c.Assert(e.String(), Equals, `SUBDATE("2011-11-11 10:10:10", INTERVAL "1" DAY)`) - e.Form = DateArithDaysForm - c.Assert(e.String(), Equals, `SUBDATE("2011-11-11 10:10:10", "1")`) - e.Op = AddDate - c.Assert(e.String(), Equals, `ADDDATE("2011-11-11 10:10:10", "1")`) // Test null. nullTbl := []struct { @@ -54,8 +46,8 @@ func (t *testDateArithSuite) TestDateArith(c *C) { Date interface{} Interval interface{} }{ - {AddDate, "DAY", nil, "1"}, - {AddDate, "DAY", input, nil}, + {DateAdd, "DAY", nil, "1"}, + {DateAdd, "DAY", input, nil}, } for _, t := range nullTbl { e := &DateArith{ @@ -67,18 +59,10 @@ func (t *testDateArithSuite) TestDateArith(c *C) { v, err := e.Eval(nil, nil) c.Assert(err, IsNil) c.Assert(v, IsNil) - e.Op = DateAdd - v, err = e.Eval(nil, nil) - c.Assert(err, IsNil) - c.Assert(v, IsNil) e.Op = DateSub v, err = e.Eval(nil, nil) c.Assert(err, IsNil) c.Assert(v, IsNil) - e.Op = SubDate - v, err = e.Eval(nil, nil) - c.Assert(err, IsNil) - c.Assert(v, IsNil) } // Test eval. @@ -132,20 +116,6 @@ func (t *testDateArithSuite) TestDateArith(c *C) { value, ok = v.(mysql.Time) c.Assert(ok, IsTrue) c.Assert(value.String(), Equals, t.SubExpect) - - e.Op = AddDate - v, err = e.Eval(nil, nil) - c.Assert(err, IsNil) - value, ok = v.(mysql.Time) - c.Assert(ok, IsTrue) - c.Assert(value.String(), Equals, t.AddExpect) - - e.Op = SubDate - v, err = e.Eval(nil, nil) - c.Assert(err, IsNil) - value, ok = v.(mysql.Time) - c.Assert(ok, IsTrue) - c.Assert(value.String(), Equals, t.SubExpect) } // Test eval for adddate and subdate with days form @@ -164,7 +134,7 @@ func (t *testDateArithSuite) TestDateArith(c *C) { } for _, t := range tblDays { e := &DateArith{ - Op: AddDate, + Op: DateAdd, Form: DateArithDaysForm, Unit: "day", Date: Value{Val: input}, @@ -219,19 +189,5 @@ func (t *testDateArithSuite) TestDateArith(c *C) { e.Date = Value{Val: errInput} v, err = e.Eval(nil, nil) c.Assert(err, NotNil, Commentf("%s", v)) - - e.Op = AddDate - _, err = e.Eval(nil, nil) - c.Assert(err, NotNil) - e.Date = Value{Val: errInput} - v, err = e.Eval(nil, nil) - c.Assert(err, NotNil, Commentf("%s", v)) - - e.Op = SubDate - _, err = e.Eval(nil, nil) - c.Assert(err, NotNil) - e.Date = Value{Val: errInput} - v, err = e.Eval(nil, nil) - c.Assert(err, NotNil, Commentf("%s", v)) } } diff --git a/parser/parser.y b/parser/parser.y index a210a518fa..7966a3bcbe 100644 --- a/parser/parser.y +++ b/parser/parser.y @@ -2341,11 +2341,11 @@ DateArithOpt: DateArithMultiFormsOpt: "ADDDATE" { - $$ = ast.AddDate + $$ = ast.DateAdd } | "SUBDATE" { - $$ = ast.SubDate + $$ = ast.DateSub } DateArithInterval: From 98856ad68e820c20655c67fb68cdffd9fe20fc0b Mon Sep 17 00:00:00 2001 From: ZhiFeng Hu Date: Fri, 6 Nov 2015 21:28:52 +0800 Subject: [PATCH 21/35] Return *Col instead c *Col Make code more readable Signed-off-by: ZhiFeng Hu --- column/column.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/column/column.go b/column/column.go index b0b833c6f9..bd88c96b13 100644 --- a/column/column.go +++ b/column/column.go @@ -55,7 +55,7 @@ func (c *Col) String() string { } // FindCol finds column in cols by name. -func FindCol(cols []*Col, name string) (c *Col) { +func FindCol(cols []*Col, name string) *Col { for _, c := range cols { if strings.EqualFold(c.Name.O, name) { return c From 3b1f107d4b6f1bee5db7316d0d907ef564a647b3 Mon Sep 17 00:00:00 2001 From: xia Date: Fri, 6 Nov 2015 21:29:08 +0800 Subject: [PATCH 22/35] *: add comments --- expression/date_arith.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/expression/date_arith.go b/expression/date_arith.go index a14575d9b5..ea4c469a30 100644 --- a/expression/date_arith.go +++ b/expression/date_arith.go @@ -168,6 +168,8 @@ func (da *DateArith) evalArgs(ctx context.Context, args map[interface{}]interfac return ret, nil } +var reg = regexp.MustCompile(`[\d]+`) + func (da *DateArith) evalDaysForm(val interface{}) (interface{}, error) { switch val.(type) { case string: @@ -177,7 +179,6 @@ func (da *DateArith) evalDaysForm(val interface{}) (interface{}, error) { if strings.ToLower(val.(string)) == "true" { return 1, nil } - reg := regexp.MustCompile(`[\d]+`) val = reg.FindString(val.(string)) } From 8c99975ac40a4f0da5518e058d293fec850ca225 Mon Sep 17 00:00:00 2001 From: qiuyesuifeng Date: Sat, 7 Nov 2015 16:57:10 +0800 Subject: [PATCH 23/35] *: fix mybatis test error. --- ast/functions.go | 4 ++-- expression/visitor.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ast/functions.go b/ast/functions.go index 7faab4c6e1..1fb30a4a64 100644 --- a/ast/functions.go +++ b/ast/functions.go @@ -375,11 +375,11 @@ func (n *FuncDateArithExpr) Accept(v Visitor) (Node, bool) { n.Date = node.(ExprNode) } if n.Interval != nil { - node, ok := n.Date.Accept(v) + node, ok := n.Interval.Accept(v) if !ok { return n, false } - n.Date = node.(ExprNode) + n.Interval = node.(ExprNode) } return v.Leave(n) } diff --git a/expression/visitor.go b/expression/visitor.go index 6646d59db7..80c5ab46d9 100644 --- a/expression/visitor.go +++ b/expression/visitor.go @@ -108,7 +108,7 @@ type Visitor interface { VisitFunctionTrim(v *FunctionTrim) (Expression, error) // VisitDateArith visits DateArith expression. - VisitDateArith(dc *DateArith) (Expression, error) + VisitDateArith(da *DateArith) (Expression, error) } // BaseVisitor is the base implementation of Visitor. From 3bafc500232ec53723329a4a14a91fc4ee9b53e7 Mon Sep 17 00:00:00 2001 From: Shen Li Date: Sun, 8 Nov 2015 23:32:35 +0800 Subject: [PATCH 24/35] plans: Fix bug in show variables plan See: https://github.com/pingcap/tidb/issues/540 --- plan/plans/show.go | 8 +++++++- plan/plans/show_test.go | 22 ++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/plan/plans/show.go b/plan/plans/show.go index 9b6537f9a3..9d8976e561 100644 --- a/plan/plans/show.go +++ b/plan/plans/show.go @@ -378,10 +378,16 @@ func (s *ShowPlan) fetchShowVariables(ctx context.Context) error { var value string if !s.GlobalScope { - // Try to get Session Scope variable value + // Try to get Session Scope variable value first. sv, ok := sessionVars.Systems[v.Name] if ok { value = sv + } else { + // If session scope variable is not set, get the global scope value. + value, err = ctx.(variable.GlobalSysVarAccessor).GetGlobalSysVar(ctx, v.Name) + if err != nil { + return errors.Trace(err) + } } } else { value, err = ctx.(variable.GlobalSysVarAccessor).GetGlobalSysVar(ctx, v.Name) diff --git a/plan/plans/show_test.go b/plan/plans/show_test.go index df6c9a425a..1068add2de 100644 --- a/plan/plans/show_test.go +++ b/plan/plans/show_test.go @@ -180,6 +180,28 @@ func (p *testShowSuit) TestShowVariables(c *C) { c.Assert(v, Equals, "on") } +func (p *testShowSuit) TestIssue540(c *C) { + // Show variables where variable_name="time_zone" + pln := &plans.ShowPlan{ + Target: stmt.ShowVariables, + GlobalScope: false, + Pattern: &expression.PatternLike{ + Pattern: &expression.Value{ + Val: "time_zone", + }, + }, + } + // Make sure the session scope var is not set. + sessionVars := variable.GetSessionVars(p.ctx) + _, ok := sessionVars.Systems["time_zone"] + c.Assert(ok, IsFalse) + + r, err := pln.Next(p.ctx) + c.Assert(err, IsNil) + c.Assert(r.Data[0], Equals, "time_zone") + c.Assert(r.Data[1], Equals, "SYSTEM") +} + func (p *testShowSuit) TestShowCollation(c *C) { pln := &plans.ShowPlan{} From 7bab524baeedf3f3d281a4f1839082d4c44fef75 Mon Sep 17 00:00:00 2001 From: qiuyesuifeng Date: Sat, 7 Nov 2015 15:05:11 +0800 Subject: [PATCH 25/35] *: add hash set check for not changed value. --- store/localstore/txn.go | 2 +- structure/hash.go | 19 +++++++++++++++---- structure/structure_test.go | 24 ++++++++++++++++++++++-- 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/store/localstore/txn.go b/store/localstore/txn.go index e31ff236be..4a30423d5d 100644 --- a/store/localstore/txn.go +++ b/store/localstore/txn.go @@ -231,7 +231,7 @@ func (txn *dbTxn) Commit() error { txn.close() }() - return txn.doCommit() + return errors.Trace(txn.doCommit()) } func (txn *dbTxn) CommittedVersion() (kv.Version, error) { diff --git a/structure/hash.go b/structure/hash.go index 3162e819c4..b0f2299da6 100644 --- a/structure/hash.go +++ b/structure/hash.go @@ -105,8 +105,9 @@ func (t *TxStructure) updateHash(key []byte, field []byte, fn func(oldValue []by return errors.Trace(err) } + newMeta := meta if oldValue == nil { - meta.Length++ + newMeta.Length++ } var newValue []byte @@ -115,11 +116,21 @@ func (t *TxStructure) updateHash(key []byte, field []byte, fn func(oldValue []by return errors.Trace(err) } - if err = t.txn.Set(dataKey, newValue); err != nil { - return errors.Trace(err) + // Check if hash field has been changed. + if bytes.Compare(oldValue, newValue) != 0 { + if err = t.txn.Set(dataKey, newValue); err != nil { + return errors.Trace(err) + } } - return errors.Trace(t.txn.Set(metaKey, meta.Value())) + // Check if hash meta has been changed. + if bytes.Compare(meta.Value(), newMeta.Value()) != 0 { + if err = t.txn.Set(metaKey, newMeta.Value()); err != nil { + return errors.Trace(err) + } + } + + return nil } // HLen gets the number of fields in a hash. diff --git a/structure/structure_test.go b/structure/structure_test.go index 3b1afd348d..699bd38223 100644 --- a/structure/structure_test.go +++ b/structure/structure_test.go @@ -193,7 +193,7 @@ func (s *tesTxStructureSuite) TestHash(c *C) { value, err = tx.HGet(key, []byte("fake")) c.Assert(err, IsNil) - c.Assert(err, IsNil) + c.Assert(value, IsNil) keys, err := tx.HKeys(key) c.Assert(err, IsNil) @@ -224,13 +224,33 @@ func (s *tesTxStructureSuite) TestHash(c *C) { c.Assert(err, IsNil) c.Assert(l, Equals, int64(2)) + // Test set new value which equals to old value. + err = tx.HSet(key, []byte("1"), []byte("1")) + c.Assert(err, IsNil) + + value, err = tx.HGet(key, []byte("1")) + c.Assert(err, IsNil) + c.Assert(value, DeepEquals, []byte("1")) + + l, err = tx.HLen(key) + c.Assert(err, IsNil) + c.Assert(l, Equals, int64(2)) + n, err = tx.HInc(key, []byte("1"), 1) c.Assert(err, IsNil) c.Assert(n, Equals, int64(2)) + l, err = tx.HLen(key) + c.Assert(err, IsNil) + c.Assert(l, Equals, int64(2)) + + n, err = tx.HInc(key, []byte("1"), 1) + c.Assert(err, IsNil) + c.Assert(n, Equals, int64(3)) + n, err = tx.HGetInt64(key, []byte("1")) c.Assert(err, IsNil) - c.Assert(n, Equals, int64(2)) + c.Assert(n, Equals, int64(3)) l, err = tx.HLen(key) c.Assert(err, IsNil) From 184e4f258950df91b8e6fe31eff00b6474c50c5b Mon Sep 17 00:00:00 2001 From: qiuyesuifeng Date: Mon, 9 Nov 2015 10:43:37 +0800 Subject: [PATCH 26/35] structure: add more test. --- structure/structure_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/structure/structure_test.go b/structure/structure_test.go index 699bd38223..5e011aac42 100644 --- a/structure/structure_test.go +++ b/structure/structure_test.go @@ -225,6 +225,10 @@ func (s *tesTxStructureSuite) TestHash(c *C) { c.Assert(l, Equals, int64(2)) // Test set new value which equals to old value. + value, err = tx.HGet(key, []byte("1")) + c.Assert(err, IsNil) + c.Assert(value, DeepEquals, []byte("1")) + err = tx.HSet(key, []byte("1"), []byte("1")) c.Assert(err, IsNil) @@ -248,6 +252,10 @@ func (s *tesTxStructureSuite) TestHash(c *C) { c.Assert(err, IsNil) c.Assert(n, Equals, int64(3)) + l, err = tx.HLen(key) + c.Assert(err, IsNil) + c.Assert(l, Equals, int64(2)) + n, err = tx.HGetInt64(key, []byte("1")) c.Assert(err, IsNil) c.Assert(n, Equals, int64(3)) From c56ef6e737bd7e072ce142575d60ec784bea8509 Mon Sep 17 00:00:00 2001 From: qiuyesuifeng Date: Mon, 9 Nov 2015 12:07:21 +0800 Subject: [PATCH 27/35] structure: address comment. --- structure/hash.go | 49 +++++++++++++++++-------------------- structure/structure_test.go | 49 +++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 27 deletions(-) diff --git a/structure/hash.go b/structure/hash.go index b0f2299da6..5195c945a8 100644 --- a/structure/hash.go +++ b/structure/hash.go @@ -92,40 +92,35 @@ func (t *TxStructure) HGetInt64(key []byte, field []byte) (int64, error) { } func (t *TxStructure) updateHash(key []byte, field []byte, fn func(oldValue []byte) ([]byte, error)) error { + dataKey := t.encodeHashDataKey(key, field) + oldValue, err := t.loadHashValue(dataKey) + if err != nil { + return errors.Trace(err) + } + + newValue, err := fn(oldValue) + if err != nil { + return errors.Trace(err) + } + + // Check if new value is equal to old value. + if bytes.Equal(oldValue, newValue) { + return nil + } + + if err = t.txn.Set(dataKey, newValue); err != nil { + return errors.Trace(err) + } + metaKey := t.encodeHashMetaKey(key) meta, err := t.loadHashMeta(metaKey) if err != nil { return errors.Trace(err) } - dataKey := t.encodeHashDataKey(key, field) - var oldValue []byte - oldValue, err = t.loadHashValue(dataKey) - if err != nil { - return errors.Trace(err) - } - - newMeta := meta if oldValue == nil { - newMeta.Length++ - } - - var newValue []byte - newValue, err = fn(oldValue) - if err != nil { - return errors.Trace(err) - } - - // Check if hash field has been changed. - if bytes.Compare(oldValue, newValue) != 0 { - if err = t.txn.Set(dataKey, newValue); err != nil { - return errors.Trace(err) - } - } - - // Check if hash meta has been changed. - if bytes.Compare(meta.Value(), newMeta.Value()) != 0 { - if err = t.txn.Set(metaKey, newMeta.Value()); err != nil { + meta.Length++ + if err = t.txn.Set(metaKey, meta.Value()); err != nil { return errors.Trace(err) } } diff --git a/structure/structure_test.go b/structure/structure_test.go index 5e011aac42..76c2cdd13c 100644 --- a/structure/structure_test.go +++ b/structure/structure_test.go @@ -274,6 +274,55 @@ func (s *tesTxStructureSuite) TestHash(c *C) { err = tx.HDel(key, []byte("fake_key")) c.Assert(err, IsNil) + // Test set nil value. + value, err = tx.HGet(key, []byte("nil_key")) + c.Assert(err, IsNil) + c.Assert(value, IsNil) + + l, err = tx.HLen(key) + c.Assert(err, IsNil) + c.Assert(l, Equals, int64(0)) + + err = tx.HSet(key, []byte("nil_key"), nil) + c.Assert(err, IsNil) + + l, err = tx.HLen(key) + c.Assert(err, IsNil) + c.Assert(l, Equals, int64(0)) + + err = tx.HSet(key, []byte("nil_key"), []byte("1")) + c.Assert(err, IsNil) + + l, err = tx.HLen(key) + c.Assert(err, IsNil) + c.Assert(l, Equals, int64(1)) + + value, err = tx.HGet(key, []byte("nil_key")) + c.Assert(err, IsNil) + c.Assert(value, DeepEquals, []byte("1")) + + err = tx.HSet(key, []byte("nil_key"), nil) + c.Assert(err, NotNil) + + l, err = tx.HLen(key) + c.Assert(err, IsNil) + c.Assert(l, Equals, int64(1)) + + value, err = tx.HGet(key, []byte("nil_key")) + c.Assert(err, IsNil) + c.Assert(value, DeepEquals, []byte("1")) + + err = tx.HSet(key, []byte("nil_key"), []byte("2")) + c.Assert(err, IsNil) + + l, err = tx.HLen(key) + c.Assert(err, IsNil) + c.Assert(l, Equals, int64(1)) + + value, err = tx.HGet(key, []byte("nil_key")) + c.Assert(err, IsNil) + c.Assert(value, DeepEquals, []byte("2")) + err = txn.Commit() c.Assert(err, IsNil) From 7d49f39f2e615786408abf698c436c3151164bfe Mon Sep 17 00:00:00 2001 From: shenli Date: Mon, 9 Nov 2015 13:02:30 +0800 Subject: [PATCH 28/35] *: Use Bind/Get global sysvar accessor instead of type assertion --- expression/variable.go | 3 ++- expression/variable_test.go | 4 +++- plan/plans/show.go | 5 +++-- plan/plans/show_test.go | 4 +++- session.go | 3 +++ sessionctx/variable/sysvar.go | 24 ++++++++++++++++++++++++ stmt/stmts/set.go | 4 ++-- util/mock/context.go | 2 +- 8 files changed, 41 insertions(+), 8 deletions(-) diff --git a/expression/variable.go b/expression/variable.go index 07acfebb0d..e58169fbde 100644 --- a/expression/variable.go +++ b/expression/variable.go @@ -62,6 +62,7 @@ func (v *Variable) String() string { func (v *Variable) Eval(ctx context.Context, args map[interface{}]interface{}) (interface{}, error) { name := strings.ToLower(v.Name) sessionVars := variable.GetSessionVars(ctx) + globalVars := variable.GetGlobalSysVarAccessor(ctx) if !v.IsSystem { // user vars if value, ok := sessionVars.Users[name]; ok { @@ -82,7 +83,7 @@ func (v *Variable) Eval(ctx context.Context, args map[interface{}]interface{}) ( return value, nil } } - value, err := ctx.(variable.GlobalSysVarAccessor).GetGlobalSysVar(ctx, name) + value, err := globalVars.GetGlobalSysVar(ctx, name) if err != nil { return nil, errors.Trace(err) } diff --git a/expression/variable_test.go b/expression/variable_test.go index 2e976fd47f..eb6954fd8e 100644 --- a/expression/variable_test.go +++ b/expression/variable_test.go @@ -27,8 +27,10 @@ type testVariableSuite struct { } func (s *testVariableSuite) SetUpSuite(c *C) { - s.ctx = mock.NewContext() + nc := mock.NewContext() + s.ctx = nc variable.BindSessionVars(s.ctx) + variable.BindGlobalSysVarAccessor(s.ctx, nc) } func (s *testVariableSuite) TestVariable(c *C) { diff --git a/plan/plans/show.go b/plan/plans/show.go index 9d8976e561..e3590cc6d0 100644 --- a/plan/plans/show.go +++ b/plan/plans/show.go @@ -353,6 +353,7 @@ func (s *ShowPlan) fetchShowTables(ctx context.Context) error { func (s *ShowPlan) fetchShowVariables(ctx context.Context) error { sessionVars := variable.GetSessionVars(ctx) + globalVars := variable.GetGlobalSysVarAccessor(ctx) m := map[interface{}]interface{}{} for _, v := range variable.SysVars { @@ -384,13 +385,13 @@ func (s *ShowPlan) fetchShowVariables(ctx context.Context) error { value = sv } else { // If session scope variable is not set, get the global scope value. - value, err = ctx.(variable.GlobalSysVarAccessor).GetGlobalSysVar(ctx, v.Name) + value, err = globalVars.GetGlobalSysVar(ctx, v.Name) if err != nil { return errors.Trace(err) } } } else { - value, err = ctx.(variable.GlobalSysVarAccessor).GetGlobalSysVar(ctx, v.Name) + value, err = globalVars.GetGlobalSysVar(ctx, v.Name) if err != nil { return errors.Trace(err) } diff --git a/plan/plans/show_test.go b/plan/plans/show_test.go index 1068add2de..54e1462eb0 100644 --- a/plan/plans/show_test.go +++ b/plan/plans/show_test.go @@ -53,8 +53,10 @@ type testShowSuit struct { var _ = Suite(&testShowSuit{}) func (p *testShowSuit) SetUpSuite(c *C) { - p.ctx = mock.NewContext() + nc := mock.NewContext() + p.ctx = nc variable.BindSessionVars(p.ctx) + variable.BindGlobalSysVarAccessor(p.ctx, nc) p.dbName = "testshowplan" p.store = newStore(c, p.dbName) diff --git a/session.go b/session.go index 6c0a37524e..151e16ec6f 100644 --- a/session.go +++ b/session.go @@ -543,6 +543,9 @@ func CreateSession(store kv.Storage) (Session, error) { variable.BindSessionVars(s) variable.GetSessionVars(s).SetStatusFlag(mysql.ServerStatusAutocommit, true) + // session implements variable.GlobalSysVarAccessor. Bind it to ctx. + variable.BindGlobalSysVarAccessor(s, s) + // session implements autocommit.Checker. Bind it to ctx autocommit.BindAutocommitChecker(s, s) sessionMu.Lock() diff --git a/sessionctx/variable/sysvar.go b/sessionctx/variable/sysvar.go index 9d5997cad3..c969c5222a 100644 --- a/sessionctx/variable/sysvar.go +++ b/sessionctx/variable/sysvar.go @@ -581,3 +581,27 @@ type GlobalSysVarAccessor interface { // SetGlobalSysVar sets the global system variable name to value. SetGlobalSysVar(ctx context.Context, name string, value string) error } + +// keyType is a dummy type to avoid naming collision in context. +type globalSysVarAccessorKeyType int + +// String defines a Stringer function for debugging and pretty printing. +func (k globalSysVarAccessorKeyType) String() string { + return "global_sysvar_accessor" +} + +const accessorKey globalSysVarAccessorKeyType = 0 + +// BindGlobalSysVarAccessor binds global sysvar accessor to context. +func BindGlobalSysVarAccessor(ctx context.Context, accessor GlobalSysVarAccessor) { + ctx.SetValue(accessorKey, accessor) +} + +// GetGlobalSysVarAccessor gets accessor from ctx. +func GetGlobalSysVarAccessor(ctx context.Context) GlobalSysVarAccessor { + v, ok := ctx.Value(accessorKey).(GlobalSysVarAccessor) + if !ok { + panic("Miss global sysvar accessor") + } + return v +} diff --git a/stmt/stmts/set.go b/stmt/stmts/set.go index af9d4c0cae..b266b8c792 100644 --- a/stmt/stmts/set.go +++ b/stmt/stmts/set.go @@ -100,7 +100,7 @@ func (s *SetStmt) Exec(ctx context.Context) (_ rset.Recordset, err error) { log.Debug("Set sys/user variables") sessionVars := variable.GetSessionVars(ctx) - + globalVars := variable.GetGlobalSysVarAccessor(ctx) for _, v := range s.Variables { // Variable is case insensitive, we use lower case. name := strings.ToLower(v.Name) @@ -138,7 +138,7 @@ func (s *SetStmt) Exec(ctx context.Context) (_ rset.Recordset, err error) { if err != nil { return nil, errors.Trace(err) } - err = ctx.(variable.GlobalSysVarAccessor).SetGlobalSysVar(ctx, name, svalue) + err = globalVars.SetGlobalSysVar(ctx, name, svalue) return nil, errors.Trace(err) } return nil, errors.Errorf("Variable '%s' is a SESSION variable and can't be used with SET GLOBAL", name) diff --git a/util/mock/context.go b/util/mock/context.go index 5cf65c4888..c5f4115a25 100644 --- a/util/mock/context.go +++ b/util/mock/context.go @@ -79,7 +79,7 @@ func (c *Context) SetGlobalSysVar(ctx context.Context, name string, value string } // NewContext creates a new mocked context.Context. -func NewContext() context.Context { +func NewContext() *Context { return &Context{ values: make(map[fmt.Stringer]interface{}), } From 88e129c3a167bda7252c00a43744084ac66bb26d Mon Sep 17 00:00:00 2001 From: ngaut Date: Mon, 9 Nov 2015 13:37:30 +0800 Subject: [PATCH 29/35] structure: Rename Length to FieldCount --- structure/hash.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/structure/hash.go b/structure/hash.go index 5195c945a8..80dbf3236f 100644 --- a/structure/hash.go +++ b/structure/hash.go @@ -30,17 +30,17 @@ type HashPair struct { } type hashMeta struct { - Length int64 + FieldCount int64 } func (meta hashMeta) Value() []byte { buf := make([]byte, 8) - binary.BigEndian.PutUint64(buf[0:8], uint64(meta.Length)) + binary.BigEndian.PutUint64(buf[0:8], uint64(meta.FieldCount)) return buf } func (meta hashMeta) IsEmpty() bool { - return meta.Length <= 0 + return meta.FieldCount <= 0 } // HSet sets the string value of a hash field. @@ -119,7 +119,7 @@ func (t *TxStructure) updateHash(key []byte, field []byte, fn func(oldValue []by } if oldValue == nil { - meta.Length++ + meta.FieldCount++ if err = t.txn.Set(metaKey, meta.Value()); err != nil { return errors.Trace(err) } @@ -135,7 +135,7 @@ func (t *TxStructure) HLen(key []byte) (int64, error) { if err != nil { return 0, errors.Trace(err) } - return meta.Length, nil + return meta.FieldCount, nil } // HDel deletes one or more hash fields. @@ -160,7 +160,7 @@ func (t *TxStructure) HDel(key []byte, fields ...[]byte) error { return errors.Trace(err) } - meta.Length-- + meta.FieldCount-- } } @@ -260,7 +260,7 @@ func (t *TxStructure) loadHashMeta(metaKey []byte) (hashMeta, error) { return hashMeta{}, errors.Trace(err) } - meta := hashMeta{Length: 0} + meta := hashMeta{FieldCount: 0} if v == nil { return meta, nil } @@ -269,7 +269,7 @@ func (t *TxStructure) loadHashMeta(metaKey []byte) (hashMeta, error) { return meta, errors.New("invalid list meta data") } - meta.Length = int64(binary.BigEndian.Uint64(v[0:8])) + meta.FieldCount = int64(binary.BigEndian.Uint64(v[0:8])) return meta, nil } From a1417b0bf489a37c7f86fded3d8b798c6ef96c83 Mon Sep 17 00:00:00 2001 From: Ewan Chou Date: Mon, 9 Nov 2015 14:04:35 +0800 Subject: [PATCH 30/35] parser: set exactly the same field text as MySQL. --- optimizer/convert_stmt.go | 25 +++++++++++++++++--- parser/parser.y | 49 +++++++++++++++++++++++++-------------- parser/scanner.l | 24 +++++++++++++++++++ 3 files changed, 77 insertions(+), 21 deletions(-) diff --git a/optimizer/convert_stmt.go b/optimizer/convert_stmt.go index df769237f1..3f903b797d 100644 --- a/optimizer/convert_stmt.go +++ b/optimizer/convert_stmt.go @@ -174,6 +174,13 @@ func convertUpdate(converter *expressionConverter, v *ast.UpdateStmt) (*stmts.Up return oldUpdate, nil } +func getInnerFromParentheses(expr ast.ExprNode) ast.ExprNode { + if pexpr, ok := expr.(*ast.ParenthesesExpr); ok { + return getInnerFromParentheses(pexpr.Expr) + } + return expr +} + func convertSelect(converter *expressionConverter, s *ast.SelectStmt) (*stmts.SelectStmt, error) { oldSelect := &stmts.SelectStmt{ Distinct: s.Distinct, @@ -189,9 +196,21 @@ func convertSelect(converter *expressionConverter, s *ast.SelectStmt) (*stmts.Se if err != nil { return nil, errors.Trace(err) } - // TODO: handle parenthesesed column name expression, which should not set AsName. - if _, ok := oldField.Expr.(*expression.Ident); !ok && oldField.AsName == "" { - oldField.AsName = val.Text() + if oldField.AsName == "" { + innerExpr := getInnerFromParentheses(val.Expr) + switch innerExpr.(type) { + case *ast.ColumnNameExpr: + // Do not set column name as name and remove parentheses. + oldField.Expr = converter.exprMap[innerExpr] + case *ast.ValueExpr: + if innerExpr.Text() != "" { + oldField.AsName = innerExpr.Text() + } else { + oldField.AsName = val.Text() + } + default: + oldField.AsName = val.Text() + } } } else if val.WildCard != nil { str := "*" diff --git a/parser/parser.y b/parser/parser.y index 7966a3bcbe..29dfe796e9 100644 --- a/parser/parser.y +++ b/parser/parser.y @@ -1526,12 +1526,6 @@ Field: { expr := $1.(ast.ExprNode) asName := $2.(string) - if asName != "" { - // Set expr original text. - offset := yyS[yypt-1].offset - end := yyS[yypt].offset-1 - expr.SetText(yylex.(*lexer).src[offset:end]) - } $$ = &ast.SelectField{Expr: expr, AsName: model.NewCIStr(asName)} } @@ -1567,7 +1561,7 @@ FieldList: Field { field := $1.(*ast.SelectField) - field.Offset = yyS[yypt].offset + field.Offset = yylex.(*lexer).startOffset(yyS[yypt].offset) $$ = []*ast.SelectField{field} } | FieldList ',' Field @@ -1575,12 +1569,13 @@ FieldList: fl := $1.([]*ast.SelectField) last := fl[len(fl)-1] + l := yylex.(*lexer) if last.Expr != nil && last.AsName.O == "" { - lastEnd := yyS[yypt-1].offset-1 // Comma offset. - last.SetText(yylex.(*lexer).src[last.Offset:lastEnd]) + lastEnd := l.endOffset(yyS[yypt-1].offset) + last.SetText(l.src[last.Offset:lastEnd]) } newField := $3.(*ast.SelectField) - newField.Offset = yyS[yypt].offset + newField.Offset = l.startOffset(yyS[yypt].offset) $$ = append(fl, newField) } @@ -1871,7 +1866,12 @@ Operand: } | '(' Expression ')' { - $$ = &ast.ParenthesesExpr{Expr: $2.(ast.ExprNode)} + l := yylex.(*lexer) + startOffset := l.startOffset(yyS[yypt-1].offset) + endOffset := l.endOffset(yyS[yypt].offset) + expr := $2.(ast.ExprNode) + expr.SetText(l.src[startOffset:endOffset]) + $$ = &ast.ParenthesesExpr{Expr: expr} } | "DEFAULT" %prec lowerThanLeftParen { @@ -2862,7 +2862,9 @@ TableFactor: | '(' SelectStmt ')' TableAsName { st := $2.(*ast.SelectStmt) - yylex.(*lexer).SetLastSelectFieldText(st, yyS[yypt-1].offset-1) + l := yylex.(*lexer) + endOffset := l.endOffset(yyS[yypt-1].offset) + l.SetLastSelectFieldText(st, endOffset) $$ = &ast.TableSource{Source: $2.(*ast.SelectStmt), AsName: $4.(model.CIStr)} } | '(' UnionStmt ')' TableAsName @@ -3009,7 +3011,9 @@ SubSelect: '(' SelectStmt ')' { s := $2.(*ast.SelectStmt) - yylex.(*lexer).SetLastSelectFieldText(s, yyS[yypt].offset-1) + l := yylex.(*lexer) + endOffset := l.endOffset(yyS[yypt].offset) + l.SetLastSelectFieldText(s, endOffset) src := yylex.(*lexer).src // See the implemention of yyParse function s.SetText(src[yyS[yypt-1].offset-1:yyS[yypt].offset-1]) @@ -3046,7 +3050,9 @@ UnionStmt: union := $1.(*ast.UnionStmt) union.Distinct = union.Distinct || $3.(bool) lastSelect := union.Selects[len(union.Selects)-1] - yylex.(*lexer).SetLastSelectFieldText(lastSelect, yyS[yypt-2].offset-1) + l := yylex.(*lexer) + endOffset := l.endOffset(yyS[yypt-2].offset) + l.SetLastSelectFieldText(lastSelect, endOffset) union.Selects = append(union.Selects, $4.(*ast.SelectStmt)) $$ = union } @@ -3055,9 +3061,12 @@ UnionStmt: union := $1.(*ast.UnionStmt) union.Distinct = union.Distinct || $3.(bool) lastSelect := union.Selects[len(union.Selects)-1] - yylex.(*lexer).SetLastSelectFieldText(lastSelect, yyS[yypt-6].offset-1) + l := yylex.(*lexer) + endOffset := l.endOffset(yyS[yypt-6].offset) + l.SetLastSelectFieldText(lastSelect, endOffset) st := $5.(*ast.SelectStmt) - yylex.(*lexer).SetLastSelectFieldText(st, yyS[yypt-2].offset-1) + endOffset = l.endOffset(yyS[yypt-2].offset) + l.SetLastSelectFieldText(st, endOffset) union.Selects = append(union.Selects, st) if $7 != nil { union.OrderBy = $7.(*ast.OrderByClause) @@ -3081,7 +3090,9 @@ UnionClauseList: union := $1.(*ast.UnionStmt) union.Distinct = union.Distinct || $3.(bool) lastSelect := union.Selects[len(union.Selects)-1] - yylex.(*lexer).SetLastSelectFieldText(lastSelect, yyS[yypt-2].offset-1) + l := yylex.(*lexer) + endOffset := l.endOffset(yyS[yypt-2].offset) + l.SetLastSelectFieldText(lastSelect, endOffset) union.Selects = append(union.Selects, $4.(*ast.SelectStmt)) $$ = union } @@ -3091,7 +3102,9 @@ UnionSelect: | '(' SelectStmt ')' { st := $2.(*ast.SelectStmt) - yylex.(*lexer).SetLastSelectFieldText(st, yyS[yypt].offset-1) + l := yylex.(*lexer) + endOffset := l.endOffset(yyS[yypt].offset) + l.SetLastSelectFieldText(st, endOffset) $$ = st } diff --git a/parser/scanner.l b/parser/scanner.l index a7592c6957..ced6d486b3 100644 --- a/parser/scanner.l +++ b/parser/scanner.l @@ -128,6 +128,30 @@ func (l *lexer) SetLastSelectFieldText(st *ast.SelectStmt, lastEnd int) { } } +func isWhiteSpace(c byte) bool { + switch c { + case ' ', '\t', '\r', '\n': + return true + } + return false; +} + +func (l *lexer) startOffset(offset int) int { + offset-- + for isWhiteSpace(l.src[offset]) { + offset++ + } + return offset +} + +func (l *lexer) endOffset(offset int) int { + offset-- + for offset > 0 && isWhiteSpace(l.src[offset-1]) { + offset-- + } + return offset +} + func (l *lexer) unget(b byte) { l.ungetBuf = append(l.ungetBuf, b) l.i-- From df1caf0f54dc0bbed7df16f9897481e53c647abe Mon Sep 17 00:00:00 2001 From: Ewan Chou Date: Mon, 9 Nov 2015 15:01:36 +0800 Subject: [PATCH 31/35] parser: add test cases and use unicode function. --- parser/scanner.l | 12 ++---------- tidb_test.go | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/parser/scanner.l b/parser/scanner.l index ced6d486b3..d13b209ac4 100644 --- a/parser/scanner.l +++ b/parser/scanner.l @@ -128,17 +128,9 @@ func (l *lexer) SetLastSelectFieldText(st *ast.SelectStmt, lastEnd int) { } } -func isWhiteSpace(c byte) bool { - switch c { - case ' ', '\t', '\r', '\n': - return true - } - return false; -} - func (l *lexer) startOffset(offset int) int { offset-- - for isWhiteSpace(l.src[offset]) { + for unicode.IsSpace(rune(l.src[offset])) { offset++ } return offset @@ -146,7 +138,7 @@ func (l *lexer) startOffset(offset int) int { func (l *lexer) endOffset(offset int) int { offset-- - for offset > 0 && isWhiteSpace(l.src[offset-1]) { + for offset > 0 && unicode.IsSpace(rune(l.src[offset-1])) { offset-- } return offset diff --git a/tidb_test.go b/tidb_test.go index b3b59aba95..2832e1b0e7 100644 --- a/tidb_test.go +++ b/tidb_test.go @@ -1347,6 +1347,31 @@ func (s *testSessionSuite) TestBuiltin(c *C) { mustExecFailed(c, se, `select cast("xxx 10:10:10" as datetime)`) } +func (s *testSessionSuite) TestFieldText(c *C) { + store := newStore(c, s.dbName) + se := newSession(c, store, s.dbName) + mustExecSQL(c, se, "drop table if exists t") + mustExecSQL(c, se, "create table t (a int)") + cases := []struct { + sql string + field string + }{ + {"select distinct(a) from t", "a"}, + {"select (1)", "1"}, + {"select (1+1)", "(1+1)"}, + {"select a from t", "a"}, + {"select ((a+1)) from t", "((a+1))"}, + } + for _, v := range cases { + results, err := se.Execute(v.sql) + c.Assert(err, IsNil) + result := results[0] + fields, err := result.Fields() + c.Assert(err, IsNil) + c.Assert(fields[0].Name, Equals, v.field) + } +} + func newSession(c *C, store kv.Storage, dbName string) Session { se, err := CreateSession(store) c.Assert(err, IsNil) From a54c7a7ab9601d13f873d1812a6d9a492da02fc0 Mon Sep 17 00:00:00 2001 From: shenli Date: Mon, 9 Nov 2015 16:50:21 +0800 Subject: [PATCH 32/35] variable: Address comment --- sessionctx/variable/sysvar.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sessionctx/variable/sysvar.go b/sessionctx/variable/sysvar.go index c969c5222a..50e554d53b 100644 --- a/sessionctx/variable/sysvar.go +++ b/sessionctx/variable/sysvar.go @@ -582,7 +582,7 @@ type GlobalSysVarAccessor interface { SetGlobalSysVar(ctx context.Context, name string, value string) error } -// keyType is a dummy type to avoid naming collision in context. +// globalSysVarAccessorKeyType is a dummy type to avoid naming collision in context. type globalSysVarAccessorKeyType int // String defines a Stringer function for debugging and pretty printing. From 99212f39fe450528a13536108764a93f8a65e0ab Mon Sep 17 00:00:00 2001 From: qiuyesuifeng Date: Tue, 10 Nov 2015 17:16:29 +0800 Subject: [PATCH 33/35] *: tiny refactor remove record. --- stmt/stmts/delete.go | 11 ++++----- table/table.go | 9 +++----- table/tables/tables.go | 46 ++++++++++++++++++++++++------------- table/tables/tables_test.go | 3 +-- 4 files changed, 38 insertions(+), 31 deletions(-) diff --git a/stmt/stmts/delete.go b/stmt/stmts/delete.go index 68c0d14938..c16fff4053 100644 --- a/stmt/stmts/delete.go +++ b/stmt/stmts/delete.go @@ -100,14 +100,11 @@ func (s *DeleteStmt) plan(ctx context.Context) (plan.Plan, error) { } func removeRow(ctx context.Context, t table.Table, h int64, data []interface{}) error { - // remove row's all indexies - if err := t.RemoveRowAllIndex(ctx, h, data); err != nil { - return err - } - // remove row - if err := t.RemoveRow(ctx, h); err != nil { - return err + err := t.RemoveRecord(ctx, h, data) + if err != nil { + return errors.Trace(err) } + variable.GetSessionVars(ctx).AddAffectedRows(1) return nil } diff --git a/table/table.go b/table/table.go index e08a3c1640..67c34db220 100644 --- a/table/table.go +++ b/table/table.go @@ -41,15 +41,9 @@ type Table interface { // Row returns a row for all columns. Row(ctx context.Context, h int64) ([]interface{}, error) - // RemoveRow removes the row of handle h. - RemoveRow(ctx context.Context, h int64) error - // RemoveRowIndex removes an index of a row. RemoveRowIndex(ctx context.Context, h int64, vals []interface{}, idx *column.IndexedCol) error - // RemoveRowAllIndex removes all the indices of a row. - RemoveRowAllIndex(ctx context.Context, h int64, rec []interface{}) error - // BuildIndexForRow builds an index for a row. BuildIndexForRow(ctx context.Context, h int64, vals []interface{}, idx *column.IndexedCol) error @@ -89,6 +83,9 @@ type Table interface { // UpdateRecord updates a row in the table. UpdateRecord(ctx context.Context, h int64, currData []interface{}, newData []interface{}, touched []bool) error + // RemoveRecord removes a row in the table. + RemoveRecord(ctx context.Context, h int64, r []interface{}) error + // TableID returns the ID of the table. TableID() int64 diff --git a/table/tables/tables.go b/table/tables/tables.go index 68ccef38bd..8e917545ed 100644 --- a/table/tables/tables.go +++ b/table/tables/tables.go @@ -444,8 +444,22 @@ func (t *Table) LockRow(ctx context.Context, h int64) error { return errors.Trace(err) } -// RemoveRow implements table.Table RemoveRow interface. -func (t *Table) RemoveRow(ctx context.Context, h int64) error { +// RemoveRecord implements table.Table RemoveRecord interface. +func (t *Table) RemoveRecord(ctx context.Context, h int64, r []interface{}) error { + err := t.removeRowData(ctx, h) + if err != nil { + return errors.Trace(err) + } + + err = t.removeRowIndices(ctx, h, r) + if err != nil { + return errors.Trace(err) + } + + return nil +} + +func (t *Table) removeRowData(ctx context.Context, h int64) error { if err := t.LockRow(ctx, h); err != nil { return errors.Trace(err) } @@ -469,20 +483,8 @@ func (t *Table) RemoveRow(ctx context.Context, h int64) error { return nil } -// RemoveRowIndex implements table.Table RemoveRowIndex interface. -func (t *Table) RemoveRowIndex(ctx context.Context, h int64, vals []interface{}, idx *column.IndexedCol) error { - txn, err := ctx.GetTxn(false) - if err != nil { - return errors.Trace(err) - } - if err = idx.X.Delete(txn, vals, h); err != nil { - return errors.Trace(err) - } - return nil -} - -// RemoveRowAllIndex implements table.Table RemoveRowAllIndex interface. -func (t *Table) RemoveRowAllIndex(ctx context.Context, h int64, rec []interface{}) error { +// removeRowAllIndex removes all the indices of a row. +func (t *Table) removeRowIndices(ctx context.Context, h int64, rec []interface{}) error { for _, v := range t.indices { vals, err := v.FetchValues(rec) if vals == nil { @@ -500,6 +502,18 @@ func (t *Table) RemoveRowAllIndex(ctx context.Context, h int64, rec []interface{ return nil } +// RemoveRowIndex implements table.Table RemoveRowIndex interface. +func (t *Table) RemoveRowIndex(ctx context.Context, h int64, vals []interface{}, idx *column.IndexedCol) error { + txn, err := ctx.GetTxn(false) + if err != nil { + return errors.Trace(err) + } + if err = idx.X.Delete(txn, vals, h); err != nil { + return errors.Trace(err) + } + return nil +} + // BuildIndexForRow implements table.Table BuildIndexForRow interface. func (t *Table) BuildIndexForRow(ctx context.Context, h int64, vals []interface{}, idx *column.IndexedCol) error { txn, err := ctx.GetTxn(false) diff --git a/table/tables/tables_test.go b/table/tables/tables_test.go index edaeeb8c2d..727fe2a9cd 100644 --- a/table/tables/tables_test.go +++ b/table/tables/tables_test.go @@ -88,9 +88,8 @@ func (ts *testSuite) TestBasic(c *C) { return true, nil }) - c.Assert(tb.RemoveRowAllIndex(ctx, rid, []interface{}{1, "cba"}), IsNil) + c.Assert(tb.RemoveRecord(ctx, rid, []interface{}{1, "cba"}), IsNil) - c.Assert(tb.RemoveRow(ctx, rid), IsNil) // Make sure there is index data in the storage. prefix := tb.IndexPrefix() cnt, err := countEntriesWithPrefix(ctx, prefix) From d169c08b6fb44b08593cb73ad18cff116f0fa710 Mon Sep 17 00:00:00 2001 From: shenli Date: Tue, 10 Nov 2015 17:20:28 +0800 Subject: [PATCH 34/35] *: Move privilege checking from ddl to stmts Make ddl.go clean --- ddl/ddl.go | 11 ----------- stmt/stmts/drop.go | 30 +++++++++++++++++++++++++++--- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/ddl/ddl.go b/ddl/ddl.go index f6f8a84469..2762d9722f 100644 --- a/ddl/ddl.go +++ b/ddl/ddl.go @@ -31,7 +31,6 @@ import ( "github.com/pingcap/tidb/model" "github.com/pingcap/tidb/mysql" "github.com/pingcap/tidb/parser/coldef" - "github.com/pingcap/tidb/privilege" "github.com/pingcap/tidb/table" "github.com/pingcap/tidb/table/tables" "github.com/pingcap/tidb/terror" @@ -577,16 +576,6 @@ func (d *ddl) DropTable(ctx context.Context, ti table.Ident) (err error) { if err != nil { return errors.Trace(err) } - // Check Privilege - privChecker := privilege.GetPrivilegeChecker(ctx) - hasPriv, err := privChecker.Check(ctx, schema, tb.Meta(), mysql.DropPriv) - if err != nil { - return errors.Trace(err) - } - if !hasPriv { - return errors.Errorf("You do not have the privilege to drop table %s.%s.", ti.Schema, ti.Name) - } - err = kv.RunInNewTxn(d.store, false, func(txn kv.Transaction) error { t := meta.NewMeta(txn) err := d.verifySchemaMetaVersion(t, is.SchemaMetaVersion()) diff --git a/stmt/stmts/drop.go b/stmt/stmts/drop.go index 1111eb2089..3831654d15 100644 --- a/stmt/stmts/drop.go +++ b/stmt/stmts/drop.go @@ -25,6 +25,8 @@ import ( "github.com/pingcap/tidb/context" "github.com/pingcap/tidb/ddl" "github.com/pingcap/tidb/model" + "github.com/pingcap/tidb/mysql" + "github.com/pingcap/tidb/privilege" "github.com/pingcap/tidb/rset" "github.com/pingcap/tidb/sessionctx" "github.com/pingcap/tidb/stmt" @@ -109,15 +111,37 @@ func (s *DropTableStmt) SetText(text string) { // Exec implements the stmt.Statement Exec interface. func (s *DropTableStmt) Exec(ctx context.Context) (rset.Recordset, error) { var notExistTables []string + is := sessionctx.GetDomain(ctx).InfoSchema() for _, ti := range s.TableIdents { - err := sessionctx.GetDomain(ctx).DDL().DropTable(ctx, ti.Full(ctx)) - // TODO: we should return special error for table not exist, checking "not exist" is not enough, - // because some other errors may contain this error string too. + fullti := ti.Full(ctx) + schema, ok := is.SchemaByName(fullti.Schema) + if !ok { + // TODO: we should return special error for table not exist, checking "not exist" is not enough, + // because some other errors may contain this error string too. + notExistTables = append(notExistTables, ti.String()) + continue + } + tb, err := is.TableByName(fullti.Schema, fullti.Name) if err != nil && strings.HasSuffix(err.Error(), "not exist") { notExistTables = append(notExistTables, ti.String()) + continue } else if err != nil { return nil, errors.Trace(err) } + // Check Privilege + privChecker := privilege.GetPrivilegeChecker(ctx) + hasPriv, err := privChecker.Check(ctx, schema, tb.Meta(), mysql.DropPriv) + if err != nil { + return nil, errors.Trace(err) + } + if !hasPriv { + return nil, errors.Errorf("You do not have the privilege to drop table %s.%s.", ti.Schema, ti.Name) + } + + err = sessionctx.GetDomain(ctx).DDL().DropTable(ctx, fullti) + if err != nil { + return nil, errors.Trace(err) + } } if len(notExistTables) > 0 && !s.IfExists { return nil, errors.Errorf("DROP TABLE: table %s does not exist", strings.Join(notExistTables, ",")) From 421b958f416a721f48d1b8a5d0e05e6bbd4f3e76 Mon Sep 17 00:00:00 2001 From: shenli Date: Tue, 10 Nov 2015 17:38:50 +0800 Subject: [PATCH 35/35] localstore: Fix a bug in kv_test.go Fix ci failure. --- store/localstore/kv_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/store/localstore/kv_test.go b/store/localstore/kv_test.go index 67264d2071..fea59447d5 100644 --- a/store/localstore/kv_test.go +++ b/store/localstore/kv_test.go @@ -451,7 +451,8 @@ func (s *testKVSuite) TestConditionIfNotExist(c *C) { }() } wg.Wait() - c.Assert(success, Greater, int64(1)) + // At least one txn can success. + c.Assert(success, Greater, int64(0)) // Clean up txn, err := s.s.Begin()