Skip to content

Commit

Permalink
expression: add builtin function json_valid (pingcap#12596)
Browse files Browse the repository at this point in the history
  • Loading branch information
Reminiscent authored and imtbkcat committed Nov 14, 2019
1 parent 89032d7 commit 2cdf1b7
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 2 deletions.
88 changes: 87 additions & 1 deletion expression/builtin_json.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,15 @@
package expression

import (
json2 "encoding/json"

"github.com/pingcap/errors"
"github.com/pingcap/parser/ast"
"github.com/pingcap/tidb/sessionctx"
"github.com/pingcap/tidb/types"
"github.com/pingcap/tidb/types/json"
"github.com/pingcap/tidb/util/chunk"
"github.com/pingcap/tidb/util/hack"
"github.com/pingcap/tipb/go-tipb"
)

Expand Down Expand Up @@ -61,6 +64,9 @@ var (
_ builtinFunc = &builtinJSONMergeSig{}
_ builtinFunc = &builtinJSONContainsSig{}
_ builtinFunc = &builtinJSONLengthSig{}
_ builtinFunc = &builtinJSONValidJSONSig{}
_ builtinFunc = &builtinJSONValidStringSig{}
_ builtinFunc = &builtinJSONValidOthersSig{}
)

type jsonTypeFunctionClass struct {
Expand Down Expand Up @@ -685,7 +691,87 @@ type jsonValidFunctionClass struct {
}

func (c *jsonValidFunctionClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) {
return nil, errFunctionNotExists.GenWithStackByArgs("FUNCTION", "JSON_VALID")
if err := c.verifyArgs(args); err != nil {
return nil, err
}

var sig builtinFunc
argType := args[0].GetType().EvalType()
switch argType {
case types.ETJson:
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETInt, types.ETJson)
sig = &builtinJSONValidJSONSig{bf}
sig.setPbCode(tipb.ScalarFuncSig_JsonValidJsonSig)
case types.ETString:
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETInt, types.ETString)
sig = &builtinJSONValidStringSig{bf}
sig.setPbCode(tipb.ScalarFuncSig_JsonValidStringSig)
default:
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETInt, argType)
sig = &builtinJSONValidOthersSig{bf}
sig.setPbCode(tipb.ScalarFuncSig_JsonValidOthersSig)
}
return sig, nil
}

type builtinJSONValidJSONSig struct {
baseBuiltinFunc
}

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

// evalInt evals a builtinJSONValidJSONSig.
// See https://dev.mysql.com/doc/refman/5.7/en/json-attribute-functions.html#function_json-valid
func (b *builtinJSONValidJSONSig) evalInt(row chunk.Row) (res int64, isNull bool, err error) {
_, isNull, err = b.args[0].EvalJSON(b.ctx, row)
return 1, isNull, err
}

type builtinJSONValidStringSig struct {
baseBuiltinFunc
}

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

// evalInt evals a builtinJSONValidStringSig.
// See https://dev.mysql.com/doc/refman/5.7/en/json-attribute-functions.html#function_json-valid
func (b *builtinJSONValidStringSig) evalInt(row chunk.Row) (res int64, isNull bool, err error) {
val, isNull, err := b.args[0].EvalString(b.ctx, row)
if err != nil || isNull {
return 0, isNull, err
}

data := hack.Slice(val)
if json2.Valid(data) {
res = 1
} else {
res = 0
}
return res, false, nil
}

type builtinJSONValidOthersSig struct {
baseBuiltinFunc
}

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

// evalInt evals a builtinJSONValidOthersSig.
// See https://dev.mysql.com/doc/refman/5.7/en/json-attribute-functions.html#function_json-valid
func (b *builtinJSONValidOthersSig) evalInt(row chunk.Row) (res int64, isNull bool, err error) {
return 0, false, nil
}

type jsonArrayAppendFunctionClass struct {
Expand Down
31 changes: 31 additions & 0 deletions expression/builtin_json_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -495,3 +495,34 @@ func (s *testEvaluatorSuite) TestJSONLength(c *C) {
}
}
}

func (s *testEvaluatorSuite) TestJSONValid(c *C) {
defer testleak.AfterTest(c)()
fc := funcs[ast.JSONValid]
tbl := []struct {
Input interface{}
Expected interface{}
}{
{`{"a":1}`, 1},
{`hello`, 0},
{`"hello"`, 1},
{`null`, 1},
{`{}`, 1},
{`[]`, 1},
{`2`, 1},
{`2.5`, 1},
{`2019-8-19`, 0},
{`"2019-8-19"`, 1},
{2, 0},
{2.5, 0},
{nil, nil},
}
dtbl := tblToDtbl(tbl)
for _, t := range dtbl {
f, err := fc.getFunction(s.ctx, s.datumsToConstants(t["Input"]))
c.Assert(err, IsNil)
d, err := evalBuiltinFunc(f, chunk.Row{})
c.Assert(err, IsNil)
c.Assert(d, testutil.DatumEquals, t["Expected"][0])
}
}
7 changes: 6 additions & 1 deletion expression/distsql_builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,12 @@ func getSignatureByPB(ctx sessionctx.Context, sigCode tipb.ScalarFuncSig, tp *ti
f = &builtinLikeSig{base}
case tipb.ScalarFuncSig_JsonLengthSig:
f = &builtinJSONLengthSig{base}

case tipb.ScalarFuncSig_JsonValidJsonSig:
f = &builtinJSONValidJSONSig{base}
case tipb.ScalarFuncSig_JsonValidStringSig:
f = &builtinJSONValidStringSig{base}
case tipb.ScalarFuncSig_JsonValidOthersSig:
f = &builtinJSONValidOthersSig{base}
case tipb.ScalarFuncSig_InInt:
f = &builtinInIntSig{base}
case tipb.ScalarFuncSig_InReal:
Expand Down
33 changes: 33 additions & 0 deletions expression/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3292,6 +3292,39 @@ func (s *testIntegrationSuite) TestJSONBuiltin(c *C) {
tk.MustExec("CREATE TABLE `my_collection` ( `doc` json DEFAULT NULL, `_id` varchar(32) GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(doc,'$._id'))) STORED NOT NULL, PRIMARY KEY (`_id`))")
_, err := tk.Exec("UPDATE `test`.`my_collection` SET doc=JSON_SET(doc) WHERE (JSON_EXTRACT(doc,'$.name') = 'clare');")
c.Assert(err, NotNil)

r := tk.MustQuery("select json_valid(null);")
r.Check(testkit.Rows("<nil>"))

r = tk.MustQuery(`select json_valid("null");`)
r.Check(testkit.Rows("1"))

r = tk.MustQuery("select json_valid(0);")
r.Check(testkit.Rows("0"))

r = tk.MustQuery(`select json_valid("0");`)
r.Check(testkit.Rows("1"))

r = tk.MustQuery(`select json_valid("hello");`)
r.Check(testkit.Rows("0"))

r = tk.MustQuery(`select json_valid('"hello"');`)
r.Check(testkit.Rows("1"))

r = tk.MustQuery(`select json_valid('{"a":1}');`)
r.Check(testkit.Rows("1"))

r = tk.MustQuery("select json_valid('{}');")
r.Check(testkit.Rows("1"))

r = tk.MustQuery(`select json_valid('[]');`)
r.Check(testkit.Rows("1"))

r = tk.MustQuery("select json_valid('2019-8-19');")
r.Check(testkit.Rows("0"))

r = tk.MustQuery(`select json_valid('"2019-8-19"');`)
r.Check(testkit.Rows("1"))
}

func (s *testIntegrationSuite) TestTimeLiteral(c *C) {
Expand Down
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,7 @@ require (
gopkg.in/natefinch/lumberjack.v2 v2.0.0
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
)

go 1.13

replace github.com/pingcap/tipb => github.com/SunRunAway/tipb v0.0.0-20191015062550-6a2d99cc954a
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/SunRunAway/tipb v0.0.0-20191015054648-a49ece5914c0 h1:AdohlGmxzxLVDKHkvbj7z8WpCnqvnRwqCjr5VSAEeCk=
github.com/SunRunAway/tipb v0.0.0-20191015054648-a49ece5914c0/go.mod h1:myhDmco5Sn/B+RTZYjXbgqmlywhxgM8Gdfy44NfarHw=
github.com/SunRunAway/tipb v0.0.0-20191015062550-6a2d99cc954a h1:1a1ZaqiYNxpINKwF43UORmt/hbKBe4oYAXMvvITRXtk=
github.com/SunRunAway/tipb v0.0.0-20191015062550-6a2d99cc954a/go.mod h1:myhDmco5Sn/B+RTZYjXbgqmlywhxgM8Gdfy44NfarHw=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/apache/thrift v0.0.0-20161221203622-b2a4d4ae21c7 h1:Fv9bK1Q+ly/ROk4aJsVMeuIwPel4bEnD8EPiI91nZMg=
github.com/apache/thrift v0.0.0-20161221203622-b2a4d4ae21c7/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
Expand Down

0 comments on commit 2cdf1b7

Please sign in to comment.