Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

expression: add builtin function json_valid #12596

Merged
merged 17 commits into from
Oct 15, 2019
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 86 additions & 1 deletion expression/builtin_json.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
package expression

import (
json2 "encoding/json"
"strings"

"github.com/pingcap/errors"
Expand All @@ -22,6 +23,7 @@ import (
"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/tidb/util/stringutil"
"github.com/pingcap/tipb/go-tipb"
)
Expand Down Expand Up @@ -72,6 +74,9 @@ var (
_ builtinFunc = &builtinJSONKeysSig{}
_ builtinFunc = &builtinJSONKeys2ArgsSig{}
_ builtinFunc = &builtinJSONLengthSig{}
_ builtinFunc = &builtinJSONValidJSONSig{}
_ builtinFunc = &builtinJSONValidStringSig{}
_ builtinFunc = &builtinJSONValidOthersSig{}
)

type jsonTypeFunctionClass struct {
Expand Down Expand Up @@ -716,7 +721,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) {
Reminiscent marked this conversation as resolved.
Show resolved Hide resolved
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 @@ -940,3 +940,34 @@ func (s *testEvaluatorSuite) TestJSONArrayInsert(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])
}
}
6 changes: 6 additions & 0 deletions expression/distsql_builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,12 @@ func getSignatureByPB(ctx sessionctx.Context, sigCode tipb.ScalarFuncSig, tp *ti
f = &builtinJSONDepthSig{base}
case tipb.ScalarFuncSig_JsonSearchSig:
f = &builtinJSONSearchSig{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}
Expand Down
33 changes: 33 additions & 0 deletions expression/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3687,6 +3687,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
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ require (
github.com/pingcap/parser v0.0.0-20191012071233-32876040fefb
github.com/pingcap/pd v1.1.0-beta.0.20190923032047-5c648dc365e0
github.com/pingcap/tidb-tools v2.1.3-0.20190321065848-1e8b48f5c168+incompatible
github.com/pingcap/tipb v0.0.0-20191008064422-018b2fadf414
github.com/pingcap/tipb v0.0.0-20191015023537-709b39e7f8bb
github.com/prometheus/client_golang v0.9.0
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910
github.com/prometheus/common v0.0.0-20181020173914-7e9e6cabbd39 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,8 @@ github.com/pingcap/pd v1.1.0-beta.0.20190923032047-5c648dc365e0 h1:GIEq+wZfrl2bc
github.com/pingcap/pd v1.1.0-beta.0.20190923032047-5c648dc365e0/go.mod h1:G/6rJpnYwM0LKMec2rI82/5Kg6GaZMvlfB+e6/tvYmI=
github.com/pingcap/tidb-tools v2.1.3-0.20190321065848-1e8b48f5c168+incompatible h1:MkWCxgZpJBgY2f4HtwWMMFzSBb3+JPzeJgF3VrXE/bU=
github.com/pingcap/tidb-tools v2.1.3-0.20190321065848-1e8b48f5c168+incompatible/go.mod h1:XGdcy9+yqlDSEMTpOXnwf3hiTeqrV6MN/u1se9N8yIM=
github.com/pingcap/tipb v0.0.0-20191008064422-018b2fadf414 h1:1HaTk+HEzn0rVcsAbVz9GLB9Ft3/KeSl2Sy452tDkHk=
github.com/pingcap/tipb v0.0.0-20191008064422-018b2fadf414/go.mod h1:RtkHW8WbcNxj8lsbzjaILci01CtYnYbIkQhjyZWrWVI=
github.com/pingcap/tipb v0.0.0-20191015023537-709b39e7f8bb h1:miVtIZZhjpJ2bD+HZ99Mkdq8MuTr6QBHGmJtjqderBA=
github.com/pingcap/tipb v0.0.0-20191015023537-709b39e7f8bb/go.mod h1:RtkHW8WbcNxj8lsbzjaILci01CtYnYbIkQhjyZWrWVI=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
Expand Down