Merge remote-tracking branch 'origin/siddontang/dev-schema-change' into qiuyesuifeng/schema-change-add-column
This commit is contained in:
@ -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 {
|
||||
|
||||
10
ddl/ddl.go
10
ddl/ddl.go
@ -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,
|
||||
|
||||
@ -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)
|
||||
})
|
||||
|
||||
@ -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)
|
||||
}
|
||||
@ -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
139
expression/date_arith.go
Normal 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
|
||||
}
|
||||
162
expression/date_arith_test.go
Normal file
162
expression/date_arith_test.go
Normal 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))
|
||||
}
|
||||
}
|
||||
@ -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 {
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
2
kv/kv.go
2
kv/kv.go
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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"))
|
||||
|
||||
@ -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++
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user