diff --git a/go/vt/vtgate/evalengine/func.go b/go/vt/vtgate/evalengine/func.go index 47c63ad2d4c..d5d51c89cc5 100644 --- a/go/vt/vtgate/evalengine/func.go +++ b/go/vt/vtgate/evalengine/func.go @@ -30,12 +30,15 @@ import ( ) var builtinFunctions = map[string]builtin{ - "coalesce": builtinCoalesce{}, - "greatest": &builtinMultiComparison{name: "GREATEST", cmp: 1}, - "least": &builtinMultiComparison{name: "LEAST", cmp: -1}, - "collation": builtinCollation{}, - "bit_count": builtinBitCount{}, - "hex": builtinHex{}, + "coalesce": builtinCoalesce{}, + "greatest": &builtinMultiComparison{name: "GREATEST", cmp: 1}, + "least": &builtinMultiComparison{name: "LEAST", cmp: -1}, + "collation": builtinCollation{}, + "bit_count": builtinBitCount{}, + "hex": builtinHex{}, + "ascii": builtinAscii{}, + "bin": builtinBin{}, + "bit_length": builtinBitLength{}, } var builtinFunctionsRewrite = map[string]builtinRewrite{ diff --git a/go/vt/vtgate/evalengine/integration/string_func_test.go b/go/vt/vtgate/evalengine/integration/string_func_test.go new file mode 100644 index 00000000000..3ed26f8ed01 --- /dev/null +++ b/go/vt/vtgate/evalengine/integration/string_func_test.go @@ -0,0 +1,102 @@ +/* +Copyright 2021 The Vitess Authors. + +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, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package integration + +import ( + "fmt" + "testing" +) + +func TestBuiltinAscii(t *testing.T) { + var elems = []string{ + "NULL", + "\"a\"", + "\"abc\"", + "1", + "-1", + "0123", + "0xAACC", + "3.1415926", + "9223372036854775807", + "-9223372036854775808", + "999999999999999999999999", + "-999999999999999999999999", + } + + var conn = mysqlconn(t) + defer conn.Close() + + t.Run("ASCII", func(t *testing.T) { + for i := 0; i < len(elems); i++ { + query := fmt.Sprintf("SELECT ASCII(%s)", elems[i]) + compareRemoteQuery(t, conn, query) + } + }) +} + +func TestBuiltinBin(t *testing.T) { + var elems = []string{ + "NULL", + "\"a\"", + "\"101\"", + "\"-101\"", + "1", + "-1", + "20", + "-100", + "3.1415926", + "9223372036854775807", + "-9223372036854775808", + } + + var conn = mysqlconn(t) + defer conn.Close() + + t.Run("BIN", func(t *testing.T) { + for i := 0; i < len(elems); i++ { + query := fmt.Sprintf("SELECT BIN(%s)", elems[i]) + compareRemoteQuery(t, conn, query) + } + }) +} + +func TestBuiltinBitLength(t *testing.T) { + var elems = []string{ + "NULL", + "\"a\"", + "\"abc\"", + "1", + "-1", + "20", + "-100", + "3.1415926", + "9223372036854775807", + "-9223372036854775808", + "999999999999999999999999", + "-999999999999999999999999", + } + + var conn = mysqlconn(t) + defer conn.Close() + + t.Run("BIT_LENGTH", func(t *testing.T) { + for i := 0; i < len(elems); i++ { + query := fmt.Sprintf("SELECT BIT_LENGTH(%s)", elems[i]) + compareRemoteQuery(t, conn, query) + } + }) +} diff --git a/go/vt/vtgate/evalengine/string.go b/go/vt/vtgate/evalengine/string.go new file mode 100644 index 00000000000..3188f3758d0 --- /dev/null +++ b/go/vt/vtgate/evalengine/string.go @@ -0,0 +1,108 @@ +/* +Copyright 2022 The Vitess Authors. + +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, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package evalengine + +import ( + "strconv" + + "vitess.io/vitess/go/mysql/collations" + "vitess.io/vitess/go/sqltypes" + vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" + "vitess.io/vitess/go/vt/vterrors" +) + +type ( + builtinAscii struct{} + builtinBin struct{} + builtinBitLength struct{} +) + +func (builtinAscii) call(env *ExpressionEnv, args []EvalResult, result *EvalResult) { + toascii := &args[0] + if toascii.null() { + result.setNull() + return + } + + toascii.makeBinary() + result.setInt64(int64(toascii.bytes()[0])) +} + +func (builtinAscii) typeof(env *ExpressionEnv, args []Expr) (sqltypes.Type, flag) { + if len(args) != 1 { + throwArgError("ASCII") + } + _, f := args[0].typeof(env) + return sqltypes.Int64, f +} + +func (builtinBin) call(env *ExpressionEnv, args []EvalResult, result *EvalResult) { + tobin := &args[0] + if tobin.null() { + result.setNull() + return + } + + var str string + switch t := tobin.typeof(); { + case sqltypes.IsNumber(t): + tobin.makeUnsignedIntegral() + str = strconv.FormatUint(tobin.uint64(), 2) + case sqltypes.IsText(t): + toint64, _ := strconv.ParseInt(tobin.string(), 10, 64) + str = strconv.FormatUint(uint64(toint64), 2) + default: + throwEvalError(vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "Unsupported BIN argument: %s", t.String())) + } + + result.setString(str, collations.TypedCollation{ + Collation: env.DefaultCollation, + Coercibility: collations.CoerceImplicit, + Repertoire: collations.RepertoireASCII, + }) +} + +func (builtinBin) typeof(env *ExpressionEnv, args []Expr) (sqltypes.Type, flag) { + if len(args) != 1 { + throwArgError("BIN") + } + _, f := args[0].typeof(env) + return sqltypes.VarChar, f +} + +func (builtinBitLength) call(env *ExpressionEnv, args []EvalResult, result *EvalResult) { + arg1 := &args[0] + if arg1.null() { + result.setNull() + return + } + + switch t := arg1.typeof(); { + case sqltypes.IsQuoted(t): + result.setInt64(int64(8 * len(arg1.bytes()))) + default: + result.setInt64(int64(8 * len(arg1.toRawBytes()))) + } +} + +func (builtinBitLength) typeof(env *ExpressionEnv, args []Expr) (sqltypes.Type, flag) { + if len(args) != 1 { + throwArgError("BIT_LENGTH") + } + _, f := args[0].typeof(env) + return sqltypes.Int64, f +}