Skip to content

Commit

Permalink
expression: add builtin function tidb_decode_key() (#12193)
Browse files Browse the repository at this point in the history
  • Loading branch information
crazycs520 authored Sep 24, 2019
1 parent e086686 commit 1051949
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 12 deletions.
11 changes: 7 additions & 4 deletions expression/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -533,7 +533,6 @@ var funcs = map[string]functionClass{
ast.Year: &yearFunctionClass{baseFunctionClass{ast.Year, 1, 1}},
ast.YearWeek: &yearWeekFunctionClass{baseFunctionClass{ast.YearWeek, 1, 2}},
ast.LastDay: &lastDayFunctionClass{baseFunctionClass{ast.LastDay, 1, 1}},
ast.TiDBParseTso: &tidbParseTsoFunctionClass{baseFunctionClass{ast.TiDBParseTso, 1, 1}},

// string functions
ast.ASCII: &asciiFunctionClass{baseFunctionClass{ast.ASCII, 1, 1}},
Expand Down Expand Up @@ -605,9 +604,6 @@ var funcs = map[string]functionClass{
ast.RowCount: &rowCountFunctionClass{baseFunctionClass{ast.RowCount, 0, 0}},
ast.SessionUser: &userFunctionClass{baseFunctionClass{ast.SessionUser, 0, 0}},
ast.SystemUser: &userFunctionClass{baseFunctionClass{ast.SystemUser, 0, 0}},
// This function is used to show tidb-server version info.
ast.TiDBVersion: &tidbVersionFunctionClass{baseFunctionClass{ast.TiDBVersion, 0, 0}},
ast.TiDBIsDDLOwner: &tidbIsDDLOwnerFunctionClass{baseFunctionClass{ast.TiDBIsDDLOwner, 0, 0}},

// control functions
ast.If: &ifFunctionClass{baseFunctionClass{ast.If, 3, 3}},
Expand Down Expand Up @@ -719,4 +715,11 @@ var funcs = map[string]functionClass{
ast.JSONDepth: &jsonDepthFunctionClass{baseFunctionClass{ast.JSONDepth, 1, 1}},
ast.JSONKeys: &jsonKeysFunctionClass{baseFunctionClass{ast.JSONKeys, 1, 2}},
ast.JSONLength: &jsonLengthFunctionClass{baseFunctionClass{ast.JSONLength, 1, 2}},

// TiDB internal function.
ast.TiDBDecodeKey: &tidbDecodeKeyFunctionClass{baseFunctionClass{ast.TiDBDecodeKey, 1, 1}},
// This function is used to show tidb-server version info.
ast.TiDBVersion: &tidbVersionFunctionClass{baseFunctionClass{ast.TiDBVersion, 0, 0}},
ast.TiDBIsDDLOwner: &tidbIsDDLOwnerFunctionClass{baseFunctionClass{ast.TiDBIsDDLOwner, 0, 0}},
ast.TiDBParseTso: &tidbParseTsoFunctionClass{baseFunctionClass{ast.TiDBParseTso, 1, 1}},
}
67 changes: 67 additions & 0 deletions expression/builtin_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,18 @@
package expression

import (
"encoding/hex"
"fmt"
"sort"
"strconv"

"github.com/pingcap/errors"
"github.com/pingcap/parser/mysql"
"github.com/pingcap/tidb/sessionctx"
"github.com/pingcap/tidb/tablecodec"
"github.com/pingcap/tidb/types"
"github.com/pingcap/tidb/util/chunk"
"github.com/pingcap/tidb/util/codec"
"github.com/pingcap/tidb/util/printer"
)

Expand All @@ -44,6 +49,7 @@ var (
_ functionClass = &rowCountFunctionClass{}
_ functionClass = &tidbVersionFunctionClass{}
_ functionClass = &tidbIsDDLOwnerFunctionClass{}
_ functionClass = &tidbDecodeKeyFunctionClass{}
)

var (
Expand All @@ -57,6 +63,7 @@ var (
_ builtinFunc = &builtinVersionSig{}
_ builtinFunc = &builtinTiDBVersionSig{}
_ builtinFunc = &builtinRowCountSig{}
_ builtinFunc = &builtinTiDBDecodeKeySig{}
)

type databaseFunctionClass struct {
Expand Down Expand Up @@ -589,3 +596,63 @@ func (b *builtinRowCountSig) evalInt(_ chunk.Row) (res int64, isNull bool, err e
res = int64(b.ctx.GetSessionVars().StmtCtx.PrevAffectedRows)
return res, false, nil
}

type tidbDecodeKeyFunctionClass struct {
baseFunctionClass
}

func (c *tidbDecodeKeyFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
if err := c.verifyArgs(args); err != nil {
return nil, err
}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, types.ETString)
sig := &builtinTiDBDecodeKeySig{bf}
return sig, nil
}

type builtinTiDBDecodeKeySig struct {
baseBuiltinFunc
}

func (b *builtinTiDBDecodeKeySig) Clone() builtinFunc {
newSig := &builtinTiDBDecodeKeySig{}
newSig.cloneFrom(&b.baseBuiltinFunc)
return newSig
}

// evalInt evals a builtinTiDBIsDDLOwnerSig.
func (b *builtinTiDBDecodeKeySig) evalString(row chunk.Row) (string, bool, error) {
s, isNull, err := b.args[0].EvalString(b.ctx, row)
if isNull || err != nil {
return "", isNull, err
}
return decodeKey(b.ctx, s), false, nil
}

func decodeKey(ctx sessionctx.Context, s string) string {
key, err := hex.DecodeString(s)
if err != nil {
ctx.GetSessionVars().StmtCtx.AppendWarning(errors.Errorf("invalid record/index key: %X", key))
return s
}
// Auto decode byte if needed.
_, bs, err := codec.DecodeBytes([]byte(key), nil)
if err == nil {
key = bs
}
// Try to decode it as a record key.
tableID, handle, err := tablecodec.DecodeRecordKey(key)
if err == nil {
return "tableID=" + strconv.FormatInt(tableID, 10) + ", _tidb_rowid=" + strconv.FormatInt(handle, 10)
}
// Try decode as table index key.
tableID, indexID, indexValues, err := tablecodec.DecodeIndexKeyPrefix(key)
if err == nil {
idxValueStr := fmt.Sprintf("%X", indexValues)
return "tableID=" + strconv.FormatInt(tableID, 10) + ", indexID=" + strconv.FormatInt(indexID, 10) + ", indexValues=" + idxValueStr
}

// TODO: try to decode other type key.
ctx.GetSessionVars().StmtCtx.AppendWarning(errors.Errorf("invalid record/index key: %X", key))
return s
}
17 changes: 17 additions & 0 deletions expression/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4123,6 +4123,23 @@ func (s *testIntegrationSuite) testTiDBIsOwnerFunc(c *C) {
result.Check(testkit.Rows(fmt.Sprintf("%v", ret)))
}

func (s *testIntegrationSuite) TestTiDBInternalFunc(c *C) {
tk := testkit.NewTestKit(c, s.store)
defer s.cleanEnv(c)
result := tk.MustQuery("select tidb_decode_key( '74800000000000002B5F72800000000000A5D3' )")
result.Check(testkit.Rows("tableID=43, _tidb_rowid=42451"))

result = tk.MustQuery("select tidb_decode_key( '74800000000000019B5F698000000000000001015257303100000000FB013736383232313130FF3900000000000000F8010000000000000000F7' )")
result.Check(testkit.Rows("tableID=411, indexID=1, indexValues=015257303100000000FB013736383232313130FF3900000000000000F8010000000000000000F7"))

// Test invalid record/index key.
result = tk.MustQuery("select tidb_decode_key( '7480000000000000FF2E5F728000000011FFE1A3000000000000' )")
result.Check(testkit.Rows("7480000000000000FF2E5F728000000011FFE1A3000000000000"))
warns := tk.Se.GetSessionVars().StmtCtx.GetWarnings()
c.Assert(warns, HasLen, 1)
c.Assert(warns[0].Err.Error(), Equals, "invalid record/index key: 7480000000000000FF2E5F728000000011FFE1A3000000000000")
}

func newStoreWithBootstrap() (kv.Storage, *domain.Domain, error) {
store, err := mockstore.NewMockTikvStore()
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ require (
github.com/pingcap/goleveldb v0.0.0-20171020122428-b9ff6c35079e
github.com/pingcap/kvproto v0.0.0-20190910074005-0e61b6f435c1
github.com/pingcap/log v0.0.0-20190715063458-479153f07ebd
github.com/pingcap/parser v0.0.0-20190912032624-978b8272c04e
github.com/pingcap/parser v0.0.0-20190923031704-33636bc5e5d6
github.com/pingcap/pd v0.0.0-20190712044914-75a1f9f3062b
github.com/pingcap/tidb-tools v2.1.3-0.20190321065848-1e8b48f5c168+incompatible
github.com/pingcap/tipb v0.0.0-20190806070524-16909e03435e
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,8 @@ github.com/pingcap/kvproto v0.0.0-20190910074005-0e61b6f435c1/go.mod h1:QMdbTAXC
github.com/pingcap/log v0.0.0-20190214045112-b37da76f67a7/go.mod h1:xsfkWVaFVV5B8e1K9seWfyJWFrIhbtUTAD8NV1Pq3+w=
github.com/pingcap/log v0.0.0-20190715063458-479153f07ebd h1:hWDol43WY5PGhsh3+8794bFHY1bPrmu6bTalpssCrGg=
github.com/pingcap/log v0.0.0-20190715063458-479153f07ebd/go.mod h1:WpHUKhNZ18v116SvGrmjkA9CBhYmuUTKL+p8JC9ANEw=
github.com/pingcap/parser v0.0.0-20190912032624-978b8272c04e h1:QeD1wC7bGElAhufSHH4JcIbs1cVdxnGWD3n3gcE5qeY=
github.com/pingcap/parser v0.0.0-20190912032624-978b8272c04e/go.mod h1:1FNvfp9+J0wvc4kl8eGNh7Rqrxveg15jJoWo/a0uHwA=
github.com/pingcap/parser v0.0.0-20190923031704-33636bc5e5d6 h1:PyjsTUD8gJ6QGilbwiy/TTn89J84/69Pj9LixOd/fFE=
github.com/pingcap/parser v0.0.0-20190923031704-33636bc5e5d6/go.mod h1:1FNvfp9+J0wvc4kl8eGNh7Rqrxveg15jJoWo/a0uHwA=
github.com/pingcap/pd v0.0.0-20190712044914-75a1f9f3062b h1:oS9PftxQqgcRouKhhdaB52tXhVLEP7Ng3Qqsd6Z18iY=
github.com/pingcap/pd v0.0.0-20190712044914-75a1f9f3062b/go.mod h1:3DlDlFT7EF64A1bmb/tulZb6wbPSagm5G4p1AlhaEDs=
github.com/pingcap/tidb-tools v2.1.3-0.20190321065848-1e8b48f5c168+incompatible h1:MkWCxgZpJBgY2f4HtwWMMFzSBb3+JPzeJgF3VrXE/bU=
Expand Down
22 changes: 17 additions & 5 deletions tablecodec/tablecodec.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,14 +127,10 @@ func DecodeRecordKey(key kv.Key) (tableID int64, handle int64, err error) {
func DecodeIndexKey(key kv.Key) (tableID int64, indexID int64, indexValues []string, err error) {
k := key

tableID, indexID, isRecord, err := DecodeKeyHead(key)
tableID, indexID, key, err = DecodeIndexKeyPrefix(key)
if err != nil {
return 0, 0, nil, errors.Trace(err)
}
if isRecord {
return 0, 0, nil, errInvalidIndexKey.GenWithStack("invalid index key - %q", k)
}
key = key[prefixLen+idLen:]

for len(key) > 0 {
// FIXME: Without the schema information, we can only decode the raw kind of
Expand All @@ -153,6 +149,22 @@ func DecodeIndexKey(key kv.Key) (tableID int64, indexID int64, indexValues []str
return
}

// DecodeIndexKeyPrefix decodes the key and gets the tableID, indexID, indexValues.
func DecodeIndexKeyPrefix(key kv.Key) (tableID int64, indexID int64, indexValues []byte, err error) {
k := key

tableID, indexID, isRecord, err := DecodeKeyHead(key)
if err != nil {
return 0, 0, nil, errors.Trace(err)
}
if isRecord {
return 0, 0, nil, errInvalidIndexKey.GenWithStack("invalid index key - %q", k)
}
indexValues = key[prefixLen+idLen:]

return tableID, indexID, indexValues, nil
}

// DecodeKeyHead decodes the key's head and gets the tableID, indexID. isRecordKey is true when is a record key.
func DecodeKeyHead(key kv.Key) (tableID int64, indexID int64, isRecordKey bool, err error) {
isRecordKey = false
Expand Down

0 comments on commit 1051949

Please sign in to comment.