Merge remote-tracking branch 'origin/siddontang/dev-schema-change' into qiuyesuifeng/schema-change-add-column

This commit is contained in:
qiuyesuifeng
2015-11-03 12:02:22 +08:00
30 changed files with 501 additions and 303 deletions

View File

@ -276,7 +276,7 @@ func (d *ddl) backfillColumn(t table.Table, columnInfo *model.ColumnInfo, versio
})
rk := kv.EncodeKey(lock)
it, err = kv.NextUntil(it, util.RowKeyPrefixFilter(rk))
err = kv.NextUntil(it, util.RowKeyPrefixFilter(rk))
if errors2.ErrorEqual(err, kv.ErrNotExist) {
break
} else if err != nil {

View File

@ -32,6 +32,7 @@ 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/util/charset"
qerror "github.com/pingcap/tidb/util/errors"
@ -483,6 +484,15 @@ func (d *ddl) DropTable(ctx context.Context, ti table.Ident) (err error) {
if err != nil {
return errors.Trace(ErrNotExists)
}
// 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)
}
job := &model.Job{
SchemaID: schema.ID,

View File

@ -401,7 +401,7 @@ func (d *ddl) getSnapshotRows(t table.Table, version uint64, seekHandle int64) (
break
}
it, err = kv.NextUntil(it, util.RowKeyPrefixFilter(rk))
err = kv.NextUntil(it, util.RowKeyPrefixFilter(rk))
if errors2.ErrorEqual(err, kv.ErrNotExist) {
break
} else if err != nil {
@ -452,7 +452,6 @@ func (d *ddl) backfillTableIndex(t table.Table, indexInfo *model.IndexInfo, hand
}
// create the index.
log.Errorf("create index %d", handle)
err = kvX.Create(txn, vals, handle)
return errors.Trace(err)
})

View File

@ -1,104 +0,0 @@
// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
package expression
import (
"fmt"
"strings"
"github.com/juju/errors"
"github.com/pingcap/tidb/context"
"github.com/pingcap/tidb/mysql"
"github.com/pingcap/tidb/util/types"
)
// DateAdd is for time date_add function.
// See: https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_date-add
type DateAdd struct {
Unit string
Date Expression
Interval Expression
}
// Clone implements the Expression Clone interface.
func (da *DateAdd) Clone() Expression {
n := *da
return &n
}
// Eval implements the Expression Eval interface.
func (da *DateAdd) Eval(ctx context.Context, args map[interface{}]interface{}) (interface{}, error) {
dv, err := da.Date.Eval(ctx, args)
if dv == nil || err != nil {
return nil, errors.Trace(err)
}
sv, err := types.ToString(dv)
if err != nil {
return nil, errors.Trace(err)
}
f := types.NewFieldType(mysql.TypeDatetime)
f.Decimal = mysql.MaxFsp
dv, err = types.Convert(sv, f)
if dv == nil || err != nil {
return nil, errors.Trace(err)
}
t, ok := dv.(mysql.Time)
if !ok {
return nil, errors.Errorf("need time type, but got %T", dv)
}
iv, err := da.Interval.Eval(ctx, args)
if iv == nil || err != nil {
return nil, errors.Trace(err)
}
format, err := types.ToString(iv)
if err != nil {
return nil, errors.Trace(err)
}
years, months, days, durations, err := mysql.ExtractTimeValue(da.Unit, strings.TrimSpace(format))
if err != nil {
return nil, errors.Trace(err)
}
t.Time = t.Time.Add(durations)
t.Time = t.Time.AddDate(int(years), int(months), int(days))
// "2011-11-11 10:10:20.000000" outputs "2011-11-11 10:10:20".
if t.Time.Nanosecond() == 0 {
t.Fsp = 0
}
return t, nil
}
// IsStatic implements the Expression IsStatic interface.
func (da *DateAdd) IsStatic() bool {
return da.Date.IsStatic() && da.Interval.IsStatic()
}
// String implements the Expression String interface.
func (da *DateAdd) String() string {
return fmt.Sprintf("DATE_ADD(%s, INTERVAL %s %s)", da.Date, da.Interval, strings.ToUpper(da.Unit))
}
// Accept implements the Visitor Accept interface.
func (da *DateAdd) Accept(v Visitor) (Expression, error) {
return v.VisitDateAdd(da)
}

View File

@ -1,144 +0,0 @@
// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
package expression
import (
. "github.com/pingcap/check"
"github.com/pingcap/tidb/mysql"
)
var _ = Suite(&testDateAddSuite{})
type testDateAddSuite struct {
}
func (t *testDateAddSuite) TestDateAdd(c *C) {
input := "2011-11-11 10:10:10"
e := &DateAdd{
Unit: "DAY",
Date: Value{Val: input},
Interval: Value{Val: "1"},
}
c.Assert(e.String(), Equals, `DATE_ADD("2011-11-11 10:10:10", INTERVAL "1" DAY)`)
c.Assert(e.Clone(), NotNil)
c.Assert(e.IsStatic(), IsTrue)
_, err := e.Eval(nil, nil)
c.Assert(err, IsNil)
// Test null.
e = &DateAdd{
Unit: "DAY",
Date: Value{Val: nil},
Interval: Value{Val: "1"},
}
v, err := e.Eval(nil, nil)
c.Assert(err, IsNil)
c.Assert(v, IsNil)
e = &DateAdd{
Unit: "DAY",
Date: Value{Val: input},
Interval: Value{Val: nil},
}
v, err = e.Eval(nil, nil)
c.Assert(err, IsNil)
c.Assert(v, IsNil)
// Test eval.
tbl := []struct {
Unit string
Interval interface{}
Expect string
}{
{"MICROSECOND", "1000", "2011-11-11 10:10:10.001000"},
{"MICROSECOND", 1000, "2011-11-11 10:10:10.001000"},
{"SECOND", "10", "2011-11-11 10:10:20"},
{"MINUTE", "10", "2011-11-11 10:20:10"},
{"HOUR", "10", "2011-11-11 20:10:10"},
{"DAY", "11", "2011-11-22 10:10:10"},
{"WEEK", "2", "2011-11-25 10:10:10"},
{"MONTH", "2", "2012-01-11 10:10:10"},
{"QUARTER", "4", "2012-11-11 10:10:10"},
{"YEAR", "2", "2013-11-11 10:10:10"},
{"SECOND_MICROSECOND", "10.00100000", "2011-11-11 10:10:20.100000"},
{"SECOND_MICROSECOND", "10.0010000000", "2011-11-11 10:10:30"},
{"SECOND_MICROSECOND", "10.0010000010", "2011-11-11 10:10:30.000010"},
{"MINUTE_MICROSECOND", "10:10.100", "2011-11-11 10:20:20.100000"},
{"MINUTE_SECOND", "10:10", "2011-11-11 10:20:20"},
{"HOUR_MICROSECOND", "10:10:10.100", "2011-11-11 20:20:20.100000"},
{"HOUR_SECOND", "10:10:10", "2011-11-11 20:20:20"},
{"HOUR_MINUTE", "10:10", "2011-11-11 20:20:10"},
{"DAY_MICROSECOND", "11 10:10:10.100", "2011-11-22 20:20:20.100000"},
{"DAY_SECOND", "11 10:10:10", "2011-11-22 20:20:20"},
{"DAY_MINUTE", "11 10:10", "2011-11-22 20:20:10"},
{"DAY_HOUR", "11 10", "2011-11-22 20:10:10"},
{"YEAR_MONTH", "11-1", "2022-12-11 10:10:10"},
{"YEAR_MONTH", "11-11", "2023-10-11 10:10:10"},
}
for _, t := range tbl {
e := &DateAdd{
Unit: t.Unit,
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.Expect)
}
// Test error.
errInput := "20111111 10:10:10"
errTbl := []struct {
Unit string
Interval interface{}
}{
{"MICROSECOND", "abc1000"},
{"MICROSECOND", ""},
{"SECOND_MICROSECOND", "10"},
{"MINUTE_MICROSECOND", "10.0000"},
{"MINUTE_MICROSECOND", "10:10:10.0000"},
// MySQL support, but tidb not.
{"HOUR_MICROSECOND", "10:10.0000"},
{"YEAR_MONTH", "10 1"},
}
for _, t := range errTbl {
e := &DateAdd{
Unit: t.Unit,
Date: Value{Val: input},
Interval: Value{Val: t.Interval},
}
_, err := e.Eval(nil, nil)
c.Assert(err, NotNil)
e = &DateAdd{
Unit: t.Unit,
Date: Value{Val: errInput},
Interval: Value{Val: t.Interval},
}
v, err := e.Eval(nil, nil)
c.Assert(err, NotNil, Commentf("%s", v))
}
}

139
expression/date_arith.go Normal file
View File

@ -0,0 +1,139 @@
// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
package expression
import (
"fmt"
"strings"
"time"
"github.com/juju/errors"
"github.com/pingcap/tidb/context"
"github.com/pingcap/tidb/mysql"
"github.com/pingcap/tidb/util/types"
)
// DateArithType is type for DateArith option.
type DateArithType byte
const (
// 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
// 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
)
// DateArith is used for dealing with addition and substraction of time.
type DateArith struct {
Op DateArithType
Unit string
Date Expression
Interval Expression
}
func (da *DateArith) isAdd() bool {
if da.Op == DateAdd {
return true
}
return false
}
// Clone implements the Expression Clone interface.
func (da *DateArith) Clone() Expression {
n := *da
return &n
}
// IsStatic implements the Expression IsStatic interface.
func (da *DateArith) IsStatic() bool {
return da.Date.IsStatic() && da.Interval.IsStatic()
}
// Accept implements the Visitor Accept interface.
func (da *DateArith) Accept(v Visitor) (Expression, error) {
return v.VisitDateArith(da)
}
// String implements the Expression String interface.
func (da *DateArith) String() string {
var str string
if da.isAdd() {
str = "DATE_ADD"
} else {
str = "DATE_SUB"
}
return fmt.Sprintf("%s(%s, INTERVAL %s %s)", str, da.Date, da.Interval, strings.ToUpper(da.Unit))
}
// 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 {
return nil, errors.Trace(err)
}
if !da.isAdd() {
years, months, days, durations = -years, -months, -days, -durations
}
t.Time = t.Time.Add(durations)
t.Time = t.Time.AddDate(int(years), int(months), int(days))
// "2011-11-11 10:10:20.000000" outputs "2011-11-11 10:10:20".
if t.Time.Nanosecond() == 0 {
t.Fsp = 0
}
return t, nil
}
func (da *DateArith) evalArgs(ctx context.Context, args map[interface{}]interface{}) (
mysql.Time, int64, int64, int64, time.Duration, error) {
dVal, err := da.Date.Eval(ctx, args)
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)
}
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)
}
t, ok := dVal.(mysql.Time)
if !ok {
return mysql.ZeroTimestamp, 0, 0, 0, 0, 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)
}
iValStr, err := types.ToString(iVal)
if err != nil {
return mysql.ZeroTimestamp, 0, 0, 0, 0, errors.Trace(err)
}
years, months, days, durations, err := mysql.ExtractTimeValue(da.Unit, strings.TrimSpace(iValStr))
if err != nil {
return mysql.ZeroTimestamp, 0, 0, 0, 0, errors.Trace(err)
}
return t, years, months, days, durations, nil
}

View File

@ -0,0 +1,162 @@
// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
package expression
import (
. "github.com/pingcap/check"
"github.com/pingcap/tidb/mysql"
)
var _ = Suite(&testDateArithSuite{})
type testDateArithSuite struct {
}
func (t *testDateArithSuite) TestDateArith(c *C) {
input := "2011-11-11 10:10:10"
e := &DateArith{
Op: DateAdd,
Unit: "DAY",
Date: Value{Val: input},
Interval: Value{Val: "1"},
}
c.Assert(e.String(), Equals, `DATE_ADD("2011-11-11 10:10:10", INTERVAL "1" DAY)`)
c.Assert(e.Clone(), NotNil)
c.Assert(e.IsStatic(), IsTrue)
_, err := e.Eval(nil, nil)
c.Assert(err, IsNil)
e.Op = DateSub
c.Assert(e.String(), Equals, `DATE_SUB("2011-11-11 10:10:10", INTERVAL "1" DAY)`)
// Test null.
nullTbl := []struct {
Op DateArithType
Unit string
Date interface{}
Interval interface{}
}{
{DateAdd, "DAY", nil, "1"},
{DateAdd, "DAY", input, nil},
}
for _, t := range nullTbl {
e := &DateArith{
Op: t.Op,
Unit: t.Unit,
Date: Value{Val: t.Date},
Interval: Value{Val: t.Interval},
}
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)
}
// Test eval.
tbl := []struct {
Unit string
Interval interface{}
AddExpect string
SubExpect string
}{
{"MICROSECOND", "1000", "2011-11-11 10:10:10.001000", "2011-11-11 10:10:09.999000"},
{"MICROSECOND", 1000, "2011-11-11 10:10:10.001000", "2011-11-11 10:10:09.999000"},
{"SECOND", "10", "2011-11-11 10:10:20", "2011-11-11 10:10:00"},
{"MINUTE", "10", "2011-11-11 10:20:10", "2011-11-11 10:00:10"},
{"HOUR", "10", "2011-11-11 20:10:10", "2011-11-11 00:10:10"},
{"DAY", "11", "2011-11-22 10:10:10", "2011-10-31 10:10:10"},
{"WEEK", "2", "2011-11-25 10:10:10", "2011-10-28 10:10:10"},
{"MONTH", "2", "2012-01-11 10:10:10", "2011-09-11 10:10:10"},
{"QUARTER", "4", "2012-11-11 10:10:10", "2010-11-11 10:10:10"},
{"YEAR", "2", "2013-11-11 10:10:10", "2009-11-11 10:10:10"},
{"SECOND_MICROSECOND", "10.00100000", "2011-11-11 10:10:20.100000", "2011-11-11 10:09:59.900000"},
{"SECOND_MICROSECOND", "10.0010000000", "2011-11-11 10:10:30", "2011-11-11 10:09:50"},
{"SECOND_MICROSECOND", "10.0010000010", "2011-11-11 10:10:30.000010", "2011-11-11 10:09:49.999990"},
{"MINUTE_MICROSECOND", "10:10.100", "2011-11-11 10:20:20.100000", "2011-11-11 09:59:59.900000"},
{"MINUTE_SECOND", "10:10", "2011-11-11 10:20:20", "2011-11-11 10:00:00"},
{"HOUR_MICROSECOND", "10:10:10.100", "2011-11-11 20:20:20.100000", "2011-11-10 23:59:59.900000"},
{"HOUR_SECOND", "10:10:10", "2011-11-11 20:20:20", "2011-11-11 00:00:00"},
{"HOUR_MINUTE", "10:10", "2011-11-11 20:20:10", "2011-11-11 00:00:10"},
{"DAY_MICROSECOND", "11 10:10:10.100", "2011-11-22 20:20:20.100000", "2011-10-30 23:59:59.900000"},
{"DAY_SECOND", "11 10:10:10", "2011-11-22 20:20:20", "2011-10-31 00:00:00"},
{"DAY_MINUTE", "11 10:10", "2011-11-22 20:20:10", "2011-10-31 00:00:10"},
{"DAY_HOUR", "11 10", "2011-11-22 20:10:10", "2011-10-31 00:10:10"},
{"YEAR_MONTH", "11-1", "2022-12-11 10:10:10", "2000-10-11 10:10:10"},
{"YEAR_MONTH", "11-11", "2023-10-11 10:10:10", "1999-12-11 10:10:10"},
}
for _, t := range tbl {
e := &DateArith{
Op: DateAdd,
Unit: t.Unit,
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.
errInput := "20111111 10:10:10"
errTbl := []struct {
Unit string
Interval interface{}
}{
{"MICROSECOND", "abc1000"},
{"MICROSECOND", ""},
{"SECOND_MICROSECOND", "10"},
{"MINUTE_MICROSECOND", "10.0000"},
{"MINUTE_MICROSECOND", "10:10:10.0000"},
// MySQL support, but tidb not.
{"HOUR_MICROSECOND", "10:10.0000"},
{"YEAR_MONTH", "10 1"},
}
for _, t := range errTbl {
e := &DateArith{
Op: DateAdd,
Unit: t.Unit,
Date: Value{Val: input},
Interval: Value{Val: t.Interval},
}
_, 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 = &DateArith{
Op: DateSub,
Unit: t.Unit,
Date: Value{Val: input},
Interval: Value{Val: t.Interval},
}
_, 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))
}
}

View File

@ -107,8 +107,8 @@ type Visitor interface {
// VisitFunctionTrim visits FunctionTrim expression.
VisitFunctionTrim(v *FunctionTrim) (Expression, error)
// VisitDateAdd visits DateAdd expression.
VisitDateAdd(da *DateAdd) (Expression, error)
// VisitDateArith visits DateArith expression.
VisitDateArith(dc *DateArith) (Expression, error)
}
// BaseVisitor is the base implementation of Visitor.
@ -482,8 +482,8 @@ func (bv *BaseVisitor) VisitFunctionTrim(ss *FunctionTrim) (Expression, error) {
return ss, nil
}
// VisitDateAdd implements Visitor interface.
func (bv *BaseVisitor) VisitDateAdd(da *DateAdd) (Expression, error) {
// VisitDateArith implements Visitor interface.
func (bv *BaseVisitor) VisitDateArith(da *DateArith) (Expression, error) {
var err error
da.Date, err = da.Date.Accept(bv.V)
if err != nil {

View File

@ -16,6 +16,8 @@
package kv
import (
"github.com/juju/errors"
"github.com/ngaut/log"
"github.com/pingcap/tidb/kv/memkv"
"github.com/pingcap/tidb/util/types"
)
@ -73,7 +75,11 @@ func (b *btreeBuffer) NewIterator(param interface{}) Iterator {
}
iter := &btreeIter{e: e}
// the initial push...
iter.Next()
err = iter.Next()
if err != nil {
log.Error(err)
return &btreeIter{ok: false}
}
return iter
}
@ -93,10 +99,14 @@ func (i *btreeIter) Value() []byte {
}
// Next implements Iterator Next.
func (i *btreeIter) Next() (Iterator, error) {
func (i *btreeIter) Next() error {
k, v, err := i.e.Next()
i.k, i.v, i.ok = string(fromIfaces(k)), fromIfaces(v), err == nil
return i, err
if err != nil {
i.ok = false
return errors.Trace(err)
}
i.k, i.v, i.ok = string(fromIfaces(k)), fromIfaces(v), true
return nil
}
// Valid implements Iterator Valid.

View File

@ -87,11 +87,10 @@ func (c *indexIter) Next() (k []interface{}, h int64, err error) {
k = vv
}
// update new iter to next
newIt, err := c.it.Next()
err = c.it.Next()
if err != nil {
return nil, 0, errors.Trace(err)
}
c.it = newIt
return
}
@ -209,7 +208,7 @@ func (c *kvIndex) Drop(txn Transaction) error {
if err != nil {
return errors.Trace(err)
}
it, err = it.Next()
err = it.Next()
if err != nil {
return errors.Trace(err)
}

View File

@ -69,13 +69,13 @@ func DecodeValue(data []byte) ([]interface{}, error) {
// NextUntil applies FnKeyCmp to each entry of the iterator until meets some condition.
// It will stop when fn returns true, or iterator is invalid or occur error
func NextUntil(it Iterator, fn FnKeyCmp) (Iterator, error) {
func NextUntil(it Iterator, fn FnKeyCmp) error {
var err error
for it.Valid() && !fn([]byte(it.Key())) {
it, err = it.Next()
err = it.Next()
if err != nil {
return nil, errors.Trace(err)
return errors.Trace(err)
}
}
return it, nil
return nil
}

View File

@ -175,7 +175,7 @@ type FnKeyCmp func(key Key) bool
// Iterator is the interface for a interator on KV store.
type Iterator interface {
Next() (Iterator, error)
Next() error
Value() []byte
Key() string
Valid() bool

View File

@ -90,13 +90,13 @@ func checkNewIterator(c *C, buffer MemBuffer) {
c.Assert(iter.Key(), Equals, string(val))
c.Assert(valToStr(c, iter), Equals, string(val))
next, err := iter.Next()
err := iter.Next()
c.Assert(err, IsNil)
c.Assert(next.Valid(), IsTrue)
c.Assert(iter.Valid(), IsTrue)
val = encodeInt((i + 1) * indexStep)
c.Assert(next.Key(), Equals, string(val))
c.Assert(valToStr(c, next), Equals, string(val))
c.Assert(iter.Key(), Equals, string(val))
c.Assert(valToStr(c, iter), Equals, string(val))
iter.Close()
}
@ -181,7 +181,7 @@ func (s *testKVSuite) TestNewIteratorMin(c *C) {
it := buffer.NewIterator(nil)
for it.Valid() {
cnt++
it, _ = it.Next()
it.Next()
}
c.Assert(cnt, Equals, 6)

View File

@ -69,9 +69,9 @@ func (m *memDbBuffer) Release() {
}
// Next implements the Iterator Next.
func (i *memDbIter) Next() (Iterator, error) {
func (i *memDbIter) Next() error {
i.iter.Next()
return i, nil
return nil
}
// Valid implements the Iterator Valid.

View File

@ -44,13 +44,13 @@ func newUnionIter(dirtyIt Iterator, snapshotIt Iterator) *UnionIter {
// Go next and update valid status.
func (iter *UnionIter) dirtyNext() {
iter.dirtyIt, _ = iter.dirtyIt.Next()
iter.dirtyIt.Next()
iter.dirtyValid = iter.dirtyIt.Valid()
}
// Go next and update valid status.
func (iter *UnionIter) snapshotNext() {
iter.snapshotIt, _ = iter.snapshotIt.Next()
iter.snapshotIt.Next()
iter.snapshotValid = iter.snapshotIt.Valid()
}
@ -115,14 +115,14 @@ func (iter *UnionIter) updateCur() {
}
// Next implements the Iterator Next interface.
func (iter *UnionIter) Next() (Iterator, error) {
func (iter *UnionIter) Next() error {
if !iter.curIsDirty {
iter.snapshotNext()
} else {
iter.dirtyNext()
}
iter.updateCur()
return iter, nil
return nil
}
// Value implements the Iterator Value interface.

View File

@ -114,6 +114,7 @@ import (
database "DATABASE"
databases "DATABASES"
dateAdd "DATE_ADD"
dateSub "DATE_SUB"
day "DAY"
dayofmonth "DAYOFMONTH"
dayofweek "DAYOFWEEK"
@ -250,6 +251,7 @@ import (
trim "TRIM"
trueKwd "true"
truncate "TRUNCATE"
underscoreCS "UNDERSCORE_CHARSET"
unknown "UNKNOWN"
union "UNION"
unique "UNIQUE"
@ -746,6 +748,7 @@ Assignment:
x, err := expression.NewAssignment($1.(string), $3.(expression.Expression))
if err != nil {
yylex.(*lexer).errf("Parse Assignment error: %s", $1.(string))
return 1
}
$$ = x
}
@ -1066,6 +1069,7 @@ CreateDatabaseStmt:
ok := charset.ValidCharsetAndCollation(cs, co)
if !ok {
yylex.(*lexer).errf("Unknown character set %s or collate %s ", cs, co)
return 1
}
dbopt := &coldef.CharsetOpt{Chs: cs, Col: co}
@ -1711,7 +1715,7 @@ UnReservedKeyword:
| "NATIONAL" | "ROW" | "QUARTER" | "ESCAPE" | "GRANTS"
NotKeywordToken:
"ABS" | "COALESCE" | "CONCAT" | "CONCAT_WS" | "COUNT" | "DAY" | "DATE_ADD" | "DAYOFMONTH" | "DAYOFWEEK" | "DAYOFYEAR" | "FOUND_ROWS" | "GROUP_CONCAT"
"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"
@ -1876,11 +1880,27 @@ Literal:
tp := types.NewFieldType(mysql.TypeString)
l := yylex.(*lexer)
tp.Charset, tp.Collate = l.GetCharsetInfo()
d := &types.DataItem{
$$ = &types.DataItem{
Type: tp,
Data: $1.(string),
}
$$ = d
}
| "UNDERSCORE_CHARSET" stringLit
{
// See: https://dev.mysql.com/doc/refman/5.7/en/charset-literal.html
tp := types.NewFieldType(mysql.TypeString)
tp.Charset = $1.(string)
co, err := charset.GetDefaultCollation(tp.Charset)
if err != nil {
l := yylex.(*lexer)
l.errf("Get collation error for charset: %s", tp.Charset)
return 1
}
tp.Collate = co
$$ = &types.DataItem{
Type: tp,
Data: $2.(string),
}
}
| hexLit
| bitLit
@ -1915,6 +1935,7 @@ Operand:
l := yylex.(*lexer)
if !l.prepare {
l.err("Can not accept placeholder when not parsing prepare sql")
return 1
}
pm := &expression.ParamMarker{}
l.ParamList = append(l.ParamList, pm)
@ -2286,12 +2307,22 @@ FunctionCallNonKeyword:
}
| "DATE_ADD" '(' Expression ',' "INTERVAL" Expression TimeUnit ')'
{
$$ = &expression.DateAdd{
$$ = &expression.DateArith{
Op:expression.DateAdd,
Unit: $7.(string),
Date: $3.(expression.Expression),
Interval: $6.(expression.Expression),
}
}
| "DATE_SUB" '(' Expression ',' "INTERVAL" Expression TimeUnit ')'
{
$$ = &expression.DateArith{
Op:expression.DateSub,
Unit: $7.(string),
Date: $3.(expression.Expression),
Interval: $6.(expression.Expression),
}
}
| "EXTRACT" '(' TimeUnit "FROM" Expression ')'
{
$$ = &expression.Extract{
@ -3896,6 +3927,7 @@ NumericType:
x.Flen = 1
} else if x.Flen > 64 {
yylex.(*lexer).errf("invalid field length %d for bit type, must in [1, 64]", x.Flen)
return 1
}
$$ = x
}

View File

@ -321,6 +321,7 @@ func (s *testParserSuite) TestExpression(c *C) {
{`select '\'a\'';`, true},
{`select "\"a\"";`, true},
{`select """a""";`, true},
{`select _utf8"string";`, true},
// For comparison
{"select 1 <=> 0, 1 <=> null, 1 = null", true},
}
@ -440,6 +441,28 @@ func (s *testParserSuite) TestBuiltin(c *C) {
{`select date_add("2011-11-11 10:10:10.123456", interval "11 10:10" day_minute)`, true},
{`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 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},
{`select date_sub("2011-11-11 10:10:10.123456", interval 10 minute)`, true},
{`select date_sub("2011-11-11 10:10:10.123456", interval 10 hour)`, true},
{`select date_sub("2011-11-11 10:10:10.123456", interval 10 day)`, true},
{`select date_sub("2011-11-11 10:10:10.123456", interval 1 week)`, true},
{`select date_sub("2011-11-11 10:10:10.123456", interval 1 month)`, true},
{`select date_sub("2011-11-11 10:10:10.123456", interval 1 quarter)`, true},
{`select date_sub("2011-11-11 10:10:10.123456", interval 1 year)`, true},
{`select date_sub("2011-11-11 10:10:10.123456", interval "10.10" second_microsecond)`, true},
{`select date_sub("2011-11-11 10:10:10.123456", interval "10:10.10" minute_microsecond)`, true},
{`select date_sub("2011-11-11 10:10:10.123456", interval "10:10" minute_second)`, true},
{`select date_sub("2011-11-11 10:10:10.123456", interval "10:10:10.10" hour_microsecond)`, true},
{`select date_sub("2011-11-11 10:10:10.123456", interval "10:10:10" hour_second)`, true},
{`select date_sub("2011-11-11 10:10:10.123456", interval "10:10" hour_minute)`, true},
{`select date_sub("2011-11-11 10:10:10.123456", interval "11 10:10:10.10" day_microsecond)`, true},
{`select date_sub("2011-11-11 10:10:10.123456", interval "11 10:10:10" day_second)`, true},
{`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},
}
s.RunTest(c, table)
}

View File

@ -27,6 +27,7 @@ import (
"github.com/pingcap/tidb/expression"
"github.com/pingcap/tidb/util/stringutil"
"github.com/pingcap/tidb/util/charset"
"github.com/pingcap/tidb/mysql"
)
@ -298,6 +299,7 @@ current_user {c}{u}{r}{r}{e}{n}{t}_{u}{s}{e}{r}
database {d}{a}{t}{a}{b}{a}{s}{e}
databases {d}{a}{t}{a}{b}{a}{s}{e}{s}
date_add {d}{a}{t}{e}_{a}{d}{d}
date_sub {d}{a}{t}{e}_{s}{u}{b}
day {d}{a}{y}
dayofweek {d}{a}{y}{o}{f}{w}{e}{e}{k}
dayofmonth {d}{a}{y}{o}{f}{m}{o}{n}{t}{h}
@ -649,6 +651,8 @@ year_month {y}{e}{a}{r}_{m}{o}{n}{t}{h}
{databases} return databases
{date_add} lval.item = string(l.val)
return dateAdd
{date_sub} lval.item = string(l.val)
return dateSub
{day} lval.item = string(l.val)
return day
{dayofweek} lval.item = string(l.val)
@ -1016,7 +1020,7 @@ year_month {y}{e}{a}{r}_{m}{o}{n}{t}{h}
return integerType
{ident} lval.item = string(l.val)
return identifier
return l.handleIdent(lval)
. return c0
@ -1107,3 +1111,19 @@ func (l *lexer) bit(lval *yySymType) int {
lval.item = b
return bitLit
}
func (l *lexer) handleIdent(lval *yySymType) int {
s := lval.item.(string)
// A character string literal may have an optional character set introducer and COLLATE clause:
// [_charset_name]'string' [COLLATE collation_name]
// See: https://dev.mysql.com/doc/refman/5.7/en/charset-literal.html
if !strings.HasPrefix(s, "_") {
return identifier
}
cs, _, err := charset.GetCharsetInfo(s[1:])
if err != nil {
return identifier
}
lval.item = cs
return underscoreCS
}

View File

@ -84,7 +84,7 @@ func (r *TableNilPlan) Next(ctx context.Context) (row *plan.Row, err error) {
// Even though the data is nil, we should return not nil row,
// or the iteration will stop.
row = &plan.Row{}
r.iter, err = kv.NextUntil(r.iter, util.RowKeyPrefixFilter(rk))
err = kv.NextUntil(r.iter, util.RowKeyPrefixFilter(rk))
return
}
@ -311,7 +311,7 @@ func (r *TableDefaultPlan) Next(ctx context.Context) (row *plan.Row, err error)
row.RowKeys = append(row.RowKeys, rke)
rk := r.T.RecordKey(handle, nil)
r.iter, err = kv.NextUntil(r.iter, util.RowKeyPrefixFilter(rk))
err = kv.NextUntil(r.iter, util.RowKeyPrefixFilter(rk))
if err != nil {
return nil, errors.Trace(err)
}

View File

@ -166,6 +166,11 @@ func (p *UserPrivileges) Check(ctx context.Context, db *model.DBInfo, tbl *model
if len(p.User) == 0 {
// User current user
p.User = variable.GetSessionVars(ctx).User
if len(p.User) == 0 {
// In embedded db mode, user does not need to login. So we do not have username.
// TODO: remove this check latter.
return true, nil
}
}
err := p.loadPrivileges(ctx)
if err != nil {

View File

@ -233,6 +233,29 @@ func (t *testPrivilegeSuite) TestShowGrants(c *C) {
c.Assert(testutil.CompareUnorderedStringSlice(gs, expected), IsTrue)
}
func (t *testPrivilegeSuite) TestDropTablePriv(c *C) {
se := newSession(c, t.store, t.dbName)
ctx, _ := se.(context.Context)
mustExec(c, se, `CREATE TABLE todrop(c int);`)
variable.GetSessionVars(ctx).User = "root@localhost"
mustExec(c, se, `CREATE USER 'drop'@'localhost' identified by '123';`)
mustExec(c, se, `GRANT Select ON test.todrop TO 'drop'@'localhost';`)
variable.GetSessionVars(ctx).User = "drop@localhost"
mustExec(c, se, `SELECT * FROM todrop;`)
_, err := se.Execute("DROP TABLE todrop;")
c.Assert(err, NotNil)
variable.GetSessionVars(ctx).User = "root@localhost"
mustExec(c, se, `GRANT Drop ON test.todrop TO 'drop'@'localhost';`)
se1 := newSession(c, t.store, t.dbName)
ctx1, _ := se1.(context.Context)
variable.GetSessionVars(ctx1).User = "drop@localhost"
mustExec(c, se1, `DROP TABLE todrop;`)
}
func mustExec(c *C, se tidb.Session, sql string) {
_, err := se.Execute(sql)
c.Assert(err, IsNil)

View File

@ -33,6 +33,13 @@ func (s *testStmtSuite) TestSelectWithoutFrom(c *C) {
rows.Close()
mustCommit(c, tx)
tx = mustBegin(c, s.testDB)
rows, err = tx.Query(`select _utf8"string";`)
c.Assert(err, IsNil)
matchRows(c, rows, [][]interface{}{{"string"}})
rows.Close()
mustCommit(c, tx)
}
func (s *testStmtSuite) TestSelectExplain(c *C) {

View File

@ -109,13 +109,13 @@ func checkSeek(c *C, txn kv.Transaction) {
c.Assert(iter.Key(), Equals, string(val))
c.Assert(valToStr(c, iter), Equals, string(val))
next, err := iter.Next()
err = iter.Next()
c.Assert(err, IsNil)
c.Assert(next.Valid(), IsTrue)
c.Assert(iter.Valid(), IsTrue)
val = encodeInt((i + 1) * indexStep)
c.Assert(next.Key(), Equals, string(val))
c.Assert(valToStr(c, next), Equals, string(val))
c.Assert(iter.Key(), Equals, string(val))
c.Assert(valToStr(c, iter), Equals, string(val))
iter.Close()
}
@ -280,7 +280,7 @@ func (s *testKVSuite) TestDelete2(c *C) {
for it.Valid() {
err = txn.Delete([]byte(it.Key()))
c.Assert(err, IsNil)
it, err = it.Next()
err = it.Next()
c.Assert(err, IsNil)
}
txn.Commit()
@ -415,7 +415,7 @@ func (s *testKVSuite) TestSeekMin(c *C) {
it, err := txn.Seek(nil)
for it.Valid() {
fmt.Printf("%s, %s\n", it.Key(), it.Value())
it, _ = it.Next()
it.Next()
}
it, err = txn.Seek([]byte("DATA_test_main_db_tbl_tbl_test_record__00000000000000000000"))

View File

@ -142,7 +142,7 @@ func (t *testMvccSuite) TestMvccNext(c *C) {
c.Assert(err, IsNil)
c.Assert(it.Valid(), IsTrue)
for it.Valid() {
it, err = it.Next()
err = it.Next()
c.Assert(err, IsNil)
}
txn.Commit()
@ -244,7 +244,7 @@ func (t *testMvccSuite) TestMvccSnapshotScan(c *C) {
if string(it.Value()) == "new" {
found = true
}
it, err = it.Next()
err = it.Next()
c.Assert(err, IsNil)
}
return found
@ -275,7 +275,7 @@ func (t *testMvccSuite) TestBufferedIterator(c *C) {
c.Assert(err, IsNil)
cnt := 0
for iter.Valid() {
iter, err = iter.Next()
err = iter.Next()
c.Assert(err, IsNil)
cnt++
}

View File

@ -151,7 +151,7 @@ func newDBIter(s *dbSnapshot, startKey kv.Key, exceptedVer kv.Version) *dbIter {
return it
}
func (it *dbIter) Next() (kv.Iterator, error) {
func (it *dbIter) Next() error {
encKey := codec.EncodeBytes(nil, it.startKey)
var retErr error
var engineIter engine.Iterator
@ -189,7 +189,7 @@ func (it *dbIter) Next() (kv.Iterator, error) {
// Current key's all versions are deleted, just go next key.
encKey = codec.EncodeBytes(nil, key.Next())
}
return it, errors.Trace(retErr)
return errors.Trace(retErr)
}
func (it *dbIter) Valid() bool {

View File

@ -176,7 +176,7 @@ func (txn *dbTxn) Delete(k kv.Key) error {
func (txn *dbTxn) each(f func(kv.Iterator) error) error {
iter := txn.UnionStore.Dirty.NewIterator(nil)
defer iter.Close()
for ; iter.Valid(); iter, _ = iter.Next() {
for ; iter.Valid(); iter.Next() {
if err := f(iter); err != nil {
return errors.Trace(err)
}

View File

@ -249,7 +249,7 @@ func (t *TxStructure) iterateHash(key []byte, fn func(k []byte, v []byte) error)
return errors.Trace(err)
}
it, err = it.Next()
err = it.Next()
if err != nil {
return errors.Trace(err)
}

View File

@ -626,7 +626,7 @@ func (t *Table) IterRecords(ctx context.Context, startKey string, cols []*column
}
rk := t.RecordKey(handle, nil)
it, err = kv.NextUntil(it, util.RowKeyPrefixFilter(rk))
err = kv.NextUntil(it, util.RowKeyPrefixFilter(rk))
if err != nil {
return errors.Trace(err)
}

View File

@ -14,7 +14,9 @@
package charset
import (
"fmt"
"strings"
"github.com/juju/errors"
)
// Charset is a charset.
@ -118,11 +120,20 @@ func ValidCharsetAndCollation(cs string, co string) bool {
func GetDefaultCollation(charset string) (string, error) {
c, ok := charsets[charset]
if !ok {
return "", fmt.Errorf("Unkown charset %s", charset)
return "", errors.Errorf("Unkown charset %s", charset)
}
return c.DefaultCollation.Name, nil
}
// GetCharsetInfo returns charset and collation for cs as name.
func GetCharsetInfo(cs string) (string, string, error) {
c, ok := charsets[strings.ToLower(cs)]
if !ok {
return "", "", errors.Errorf("Unknown charset %s", cs)
}
return c.Name, c.DefaultCollation.Name, nil
}
// GetCollations returns a list for all collations.
func GetCollations() []*Collation {
return collations

View File

@ -45,7 +45,10 @@ func ScanMetaWithPrefix(txn kv.Transaction, prefix string, filter func([]byte, [
if !filter([]byte(iter.Key()), iter.Value()) {
break
}
iter, err = iter.Next()
err = iter.Next()
if err != nil {
return errors.Trace(err)
}
} else {
break
}
@ -75,7 +78,10 @@ func DelKeyWithPrefix(ctx context.Context, prefix string) error {
if iter.Valid() && strings.HasPrefix(iter.Key(), prefix) {
keys = append(keys, iter.Key())
iter, err = iter.Next()
err = iter.Next()
if err != nil {
return errors.Trace(err)
}
} else {
break
}