From f5ebc6dc61304c174861200283754a017c7c3afa Mon Sep 17 00:00:00 2001 From: ou yuanning <45346669+ouyuanning@users.noreply.github.com> Date: Tue, 16 Aug 2022 14:20:11 +0800 Subject: [PATCH] Add bit or/and/xor function (#4471) Add bit or/and/xor function support. (they are bit operator, not bool, not agg) Approved by: @aressu1985, @aunjgr --- pkg/sql/plan/base_binder.go | 6 + pkg/sql/plan/function/function_id.go | 6 + pkg/sql/plan/function/operator/op_bit.go | 102 +++++++ pkg/sql/plan/function/operator/op_bit_test.go | 237 +++++++++++++++ pkg/sql/plan/function/operators.go | 282 ++++++++++++++++++ .../cases/function/func_string_lpad_rpad.test | 2 +- 6 files changed, 634 insertions(+), 1 deletion(-) create mode 100644 pkg/sql/plan/function/operator/op_bit.go create mode 100644 pkg/sql/plan/function/operator/op_bit_test.go diff --git a/pkg/sql/plan/base_binder.go b/pkg/sql/plan/base_binder.go index d7b96790f..043f1de3a 100644 --- a/pkg/sql/plan/base_binder.go +++ b/pkg/sql/plan/base_binder.go @@ -409,6 +409,12 @@ func (b *baseBinder) bindBinaryExpr(astExpr *tree.BinaryExpr, depth int32, isRoo return b.bindFuncExprImplByAstExpr("/", []tree.Expr{astExpr.Left, astExpr.Right}, depth) case tree.INTEGER_DIV: return b.bindFuncExprImplByAstExpr("div", []tree.Expr{astExpr.Left, astExpr.Right}, depth) + case tree.BIT_XOR: + return b.bindFuncExprImplByAstExpr("^", []tree.Expr{astExpr.Left, astExpr.Right}, depth) + case tree.BIT_OR: + return b.bindFuncExprImplByAstExpr("|", []tree.Expr{astExpr.Left, astExpr.Right}, depth) + case tree.BIT_AND: + return b.bindFuncExprImplByAstExpr("&", []tree.Expr{astExpr.Left, astExpr.Right}, depth) } return nil, errors.New("", fmt.Sprintf("'%v' operator is not supported now", astExpr.Op.ToString())) } diff --git a/pkg/sql/plan/function/function_id.go b/pkg/sql/plan/function/function_id.go index a307d69e6..b956472ca 100644 --- a/pkg/sql/plan/function/function_id.go +++ b/pkg/sql/plan/function/function_id.go @@ -50,6 +50,9 @@ const ( ISNOT //ISNOT ISNULL //ISNULL ISNOTNULL //ISNOTNULL + OP_BIT_AND // & + OP_BIT_OR // | + OP_BIT_XOR // ^ ABS // ABS ACOS // ACOS @@ -273,6 +276,9 @@ var functionIdRegister = map[string]int32{ "ifnull": ISNULL, "is_not_null": ISNOTNULL, "isnotnull": ISNOTNULL, + "&": OP_BIT_AND, + "|": OP_BIT_OR, + "^": OP_BIT_XOR, // aggregate "max": MAX, "min": MIN, diff --git a/pkg/sql/plan/function/operator/op_bit.go b/pkg/sql/plan/function/operator/op_bit.go new file mode 100644 index 000000000..f466da69a --- /dev/null +++ b/pkg/sql/plan/function/operator/op_bit.go @@ -0,0 +1,102 @@ +// Copyright 2022 Matrix Origin +// +// 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 operator + +import ( + "github.com/matrixorigin/matrixone/pkg/container/nulls" + "github.com/matrixorigin/matrixone/pkg/container/vector" + "github.com/matrixorigin/matrixone/pkg/vm/process" + "golang.org/x/exp/constraints" +) + +type opBitT interface { + constraints.Integer +} + +type opBitFun[T opBitT] func(v1, v2 T) T + +func opBitXor[T opBitT](v1, v2 T) T { + return v1 ^ v2 +} + +func opBitOr[T opBitT](v1, v2 T) T { + return v1 | v2 +} + +func opBitAnd[T opBitT](v1, v2 T) T { + return v1 & v2 +} + +func OpBitAndFun[T opBitT](args []*vector.Vector, proc *process.Process) (*vector.Vector, error) { + return Arith[T, T](args, proc, args[0].GetType(), func(xs, ys, rs *vector.Vector) error { + return goOpBitGeneral(xs, ys, rs, opBitAnd[T]) + }) +} + +func OpBitOrFun[T opBitT](args []*vector.Vector, proc *process.Process) (*vector.Vector, error) { + return Arith[T, T](args, proc, args[0].GetType(), func(xs, ys, rs *vector.Vector) error { + return goOpBitGeneral(xs, ys, rs, opBitOr[T]) + }) +} + +func OpBitXorFun[T opBitT](args []*vector.Vector, proc *process.Process) (*vector.Vector, error) { + return Arith[T, T](args, proc, args[0].GetType(), func(xs, ys, rs *vector.Vector) error { + return goOpBitGeneral(xs, ys, rs, opBitXor[T]) + }) +} + +func goOpBitGeneral[T opBitT](xs, ys, rs *vector.Vector, bfn opBitFun[T]) error { + xt, yt, rt := vector.MustTCols[T](xs), vector.MustTCols[T](ys), vector.MustTCols[T](rs) + if xs.IsScalar() { + if nulls.Any(ys.Nsp) { + for i, y := range yt { + if !nulls.Contains(rs.Nsp, uint64(i)) { + rt[i] = bfn(xt[0], y) + } + } + } else { + for i, y := range yt { + rt[i] = bfn(xt[0], y) + } + } + return nil + } else if ys.IsScalar() { + if nulls.Any(xs.Nsp) { + for i, x := range xt { + if !nulls.Contains(rs.Nsp, uint64(i)) { + rt[i] = bfn(x, yt[0]) + } + } + } else { + for i, x := range xt { + rt[i] = bfn(x, yt[0]) + } + } + return nil + } else { + if nulls.Any(rs.Nsp) { + for i, x := range xt { + if !nulls.Contains(rs.Nsp, uint64(i)) { + rt[i] = bfn(x, yt[i]) + } + } + } else { + for i, x := range xt { + rt[i] = bfn(x, yt[i]) + } + } + return nil + } +} diff --git a/pkg/sql/plan/function/operator/op_bit_test.go b/pkg/sql/plan/function/operator/op_bit_test.go new file mode 100644 index 000000000..8d77f2d0d --- /dev/null +++ b/pkg/sql/plan/function/operator/op_bit_test.go @@ -0,0 +1,237 @@ +// Copyright 2022 Matrix Origin +// +// 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 operator + +import ( + "fmt" + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/vector" + "github.com/matrixorigin/matrixone/pkg/testutil" + "github.com/stretchr/testify/require" +) + +func TestOpXorGeneral(t *testing.T) { + testCases := []arg{ + { + info: "1 ^ 2", proc: testutil.NewProc(), + vs: []*vector.Vector{ + testutil.MakeScalarInt64(1, 1), + testutil.MakeScalarInt64(2, 1), + }, + match: true, + err: false, + expect: testutil.MakeScalarInt64(3, 1), + }, + + { + info: "-1 ^ 2", proc: testutil.NewProc(), + vs: []*vector.Vector{ + testutil.MakeScalarInt64(-1, 1), + testutil.MakeScalarInt64(2, 1), + }, + match: true, + err: false, + expect: testutil.MakeScalarInt64(-3, 1), + }, + + { + info: "null ^ 2", proc: testutil.NewProc(), + vs: []*vector.Vector{ + testutil.MakeScalarNull(1), + testutil.MakeScalarInt64(2, 1), + }, + match: true, + err: false, + expect: testutil.MakeScalarNull(1), + }, + + { + info: "a ^ 2", proc: testutil.NewProc(), + vs: []*vector.Vector{ + testutil.MakeInt64Vector([]int64{-1, 0, 3, 0}, []uint64{1, 3}), + testutil.MakeScalarInt64(2, 1), + }, + match: true, + err: false, + expect: testutil.MakeInt64Vector([]int64{-3, 0, 1, 0}, []uint64{1, 3}), + }, + + { + info: "a ^ b", proc: testutil.NewProc(), + vs: []*vector.Vector{ + testutil.MakeInt64Vector([]int64{-1, 0, 3, 0}, []uint64{1, 3}), + testutil.MakeInt64Vector([]int64{2, 3, 2, 4}, []uint64{1, 3}), + }, + match: true, + err: false, + expect: testutil.MakeInt64Vector([]int64{-3, 0, 1, 0}, []uint64{1, 3}), + }, + } + + for i, tc := range testCases { + t.Run(tc.info, func(t *testing.T) { + got, ergot := OpBitXorFun[int64](tc.vs, tc.proc) + if tc.err { + require.Errorf(t, ergot, fmt.Sprintf("case '%d' expected error, but no error happens", i)) + } else { + require.NoError(t, ergot) + require.True(t, testutil.CompareVectors(tc.expect, got), "got vector is different with expected") + } + }) + } +} + +func TestOpOrGeneral(t *testing.T) { + testCases := []arg{ + { + info: "1 | 2", proc: testutil.NewProc(), + vs: []*vector.Vector{ + testutil.MakeScalarInt64(1, 1), + testutil.MakeScalarInt64(2, 1), + }, + match: true, + err: false, + expect: testutil.MakeScalarInt64(3, 1), + }, + + { + info: "-1 | 2", proc: testutil.NewProc(), + vs: []*vector.Vector{ + testutil.MakeScalarInt64(-1, 1), + testutil.MakeScalarInt64(2, 1), + }, + match: true, + err: false, + expect: testutil.MakeScalarInt64(-1, 1), + }, + + { + info: "null | 2", proc: testutil.NewProc(), + vs: []*vector.Vector{ + testutil.MakeScalarNull(1), + testutil.MakeScalarInt64(2, 1), + }, + match: true, + err: false, + expect: testutil.MakeScalarNull(1), + }, + + { + info: "a | 2", proc: testutil.NewProc(), + vs: []*vector.Vector{ + testutil.MakeInt64Vector([]int64{1, 0, 3, 0}, []uint64{1, 3}), + testutil.MakeScalarInt64(2, 1), + }, + match: true, + err: false, + expect: testutil.MakeInt64Vector([]int64{3, 0, 3, 0}, []uint64{1, 3}), + }, + + { + info: "a | b", proc: testutil.NewProc(), + vs: []*vector.Vector{ + testutil.MakeInt64Vector([]int64{1, 0, 3, 0}, []uint64{1, 3}), + testutil.MakeInt64Vector([]int64{2, 3, 2, 4}, []uint64{1, 3}), + }, + match: true, + err: false, + expect: testutil.MakeInt64Vector([]int64{3, 0, 3, 0}, []uint64{1, 3}), + }, + } + + for i, tc := range testCases { + t.Run(tc.info, func(t *testing.T) { + got, ergot := OpBitOrFun[int64](tc.vs, tc.proc) + if tc.err { + require.Errorf(t, ergot, fmt.Sprintf("case '%d' expected error, but no error happens", i)) + } else { + require.NoError(t, ergot) + require.True(t, testutil.CompareVectors(tc.expect, got), "got vector is different with expected") + } + }) + } +} + +func TestOpAndGeneral(t *testing.T) { + testCases := []arg{ + { + info: "1 & 2", proc: testutil.NewProc(), + vs: []*vector.Vector{ + testutil.MakeScalarInt64(1, 1), + testutil.MakeScalarInt64(2, 1), + }, + match: true, + err: false, + expect: testutil.MakeScalarInt64(0, 1), + }, + + { + info: "-1 & 2", proc: testutil.NewProc(), + vs: []*vector.Vector{ + testutil.MakeScalarInt64(-1, 1), + testutil.MakeScalarInt64(2, 1), + }, + match: true, + err: false, + expect: testutil.MakeScalarInt64(2, 1), + }, + + { + info: "null & 2", proc: testutil.NewProc(), + vs: []*vector.Vector{ + testutil.MakeScalarNull(1), + testutil.MakeScalarInt64(2, 1), + }, + match: true, + err: false, + expect: testutil.MakeScalarNull(1), + }, + + { + info: "a & 2", proc: testutil.NewProc(), + vs: []*vector.Vector{ + testutil.MakeInt64Vector([]int64{1, 0, 3, 0}, []uint64{1, 3}), + testutil.MakeScalarInt64(2, 1), + }, + match: true, + err: false, + expect: testutil.MakeInt64Vector([]int64{0, 0, 2, 0}, []uint64{1, 3}), + }, + + { + info: "a & b", proc: testutil.NewProc(), + vs: []*vector.Vector{ + testutil.MakeInt64Vector([]int64{1, 0, 3, 0}, []uint64{1, 3}), + testutil.MakeInt64Vector([]int64{2, 3, 2, 4}, []uint64{1, 3}), + }, + match: true, + err: false, + expect: testutil.MakeInt64Vector([]int64{0, 0, 2, 0}, []uint64{1, 3}), + }, + } + + for i, tc := range testCases { + t.Run(tc.info, func(t *testing.T) { + got, ergot := OpBitAndFun[int64](tc.vs, tc.proc) + if tc.err { + require.Errorf(t, ergot, fmt.Sprintf("case '%d' expected error, but no error happens", i)) + } else { + require.NoError(t, ergot) + require.True(t, testutil.CompareVectors(tc.expect, got), "got vector is different with expected") + } + }) + } +} diff --git a/pkg/sql/plan/function/operators.go b/pkg/sql/plan/function/operators.go index bd9b1b13f..b8493f093 100644 --- a/pkg/sql/plan/function/operators.go +++ b/pkg/sql/plan/function/operators.go @@ -427,6 +427,288 @@ var operators = map[int]Functions{ }, }, + OP_BIT_XOR: { + Id: OP_BIT_XOR, + Overloads: []Function{ + { + Index: 0, + Flag: plan.Function_STRICT, + Layout: COMPARISON_OPERATOR, + Args: []types.T{ + types.T_uint8, + types.T_uint8, + }, + ReturnTyp: types.T_uint8, + Fn: operator.OpBitXorFun[uint8], + }, + { + Index: 1, + Flag: plan.Function_STRICT, + Layout: COMPARISON_OPERATOR, + Args: []types.T{ + types.T_int8, + types.T_int8, + }, + ReturnTyp: types.T_int8, + Fn: operator.OpBitXorFun[int8], + }, + { + Index: 2, + Flag: plan.Function_STRICT, + Layout: COMPARISON_OPERATOR, + Args: []types.T{ + types.T_uint16, + types.T_uint16, + }, + ReturnTyp: types.T_uint16, + Fn: operator.OpBitXorFun[uint16], + }, + { + Index: 3, + Flag: plan.Function_STRICT, + Layout: COMPARISON_OPERATOR, + Args: []types.T{ + types.T_int16, + types.T_int16, + }, + ReturnTyp: types.T_int16, + Fn: operator.OpBitXorFun[int16], + }, + { + Index: 4, + Flag: plan.Function_STRICT, + Layout: COMPARISON_OPERATOR, + Args: []types.T{ + types.T_uint32, + types.T_uint32, + }, + ReturnTyp: types.T_uint32, + Fn: operator.OpBitXorFun[uint32], + }, + { + Index: 5, + Flag: plan.Function_STRICT, + Layout: COMPARISON_OPERATOR, + Args: []types.T{ + types.T_int32, + types.T_int32, + }, + ReturnTyp: types.T_int32, + Fn: operator.OpBitXorFun[int32], + }, + { + Index: 6, + Flag: plan.Function_STRICT, + Layout: COMPARISON_OPERATOR, + Args: []types.T{ + types.T_uint64, + types.T_uint64, + }, + ReturnTyp: types.T_uint64, + Fn: operator.OpBitXorFun[uint64], + }, + { + Index: 7, + Flag: plan.Function_STRICT, + Layout: COMPARISON_OPERATOR, + Args: []types.T{ + types.T_int64, + types.T_int64, + }, + ReturnTyp: types.T_int64, + Fn: operator.OpBitXorFun[int64], + }, + }, + }, + + OP_BIT_OR: { + Id: OP_BIT_OR, + Overloads: []Function{ + { + Index: 0, + Flag: plan.Function_STRICT, + Layout: COMPARISON_OPERATOR, + Args: []types.T{ + types.T_uint8, + types.T_uint8, + }, + ReturnTyp: types.T_uint8, + Fn: operator.OpBitOrFun[uint8], + }, + { + Index: 1, + Flag: plan.Function_STRICT, + Layout: COMPARISON_OPERATOR, + Args: []types.T{ + types.T_int8, + types.T_int8, + }, + ReturnTyp: types.T_int8, + Fn: operator.OpBitOrFun[int8], + }, + { + Index: 2, + Flag: plan.Function_STRICT, + Layout: COMPARISON_OPERATOR, + Args: []types.T{ + types.T_uint16, + types.T_uint16, + }, + ReturnTyp: types.T_uint16, + Fn: operator.OpBitOrFun[uint16], + }, + { + Index: 3, + Flag: plan.Function_STRICT, + Layout: COMPARISON_OPERATOR, + Args: []types.T{ + types.T_int16, + types.T_int16, + }, + ReturnTyp: types.T_int16, + Fn: operator.OpBitOrFun[int16], + }, + { + Index: 4, + Flag: plan.Function_STRICT, + Layout: COMPARISON_OPERATOR, + Args: []types.T{ + types.T_uint32, + types.T_uint32, + }, + ReturnTyp: types.T_uint32, + Fn: operator.OpBitOrFun[uint32], + }, + { + Index: 5, + Flag: plan.Function_STRICT, + Layout: COMPARISON_OPERATOR, + Args: []types.T{ + types.T_int32, + types.T_int32, + }, + ReturnTyp: types.T_int32, + Fn: operator.OpBitOrFun[int32], + }, + { + Index: 6, + Flag: plan.Function_STRICT, + Layout: COMPARISON_OPERATOR, + Args: []types.T{ + types.T_uint64, + types.T_uint64, + }, + ReturnTyp: types.T_uint64, + Fn: operator.OpBitOrFun[uint64], + }, + { + Index: 7, + Flag: plan.Function_STRICT, + Layout: COMPARISON_OPERATOR, + Args: []types.T{ + types.T_int64, + types.T_int64, + }, + ReturnTyp: types.T_int64, + Fn: operator.OpBitOrFun[int64], + }, + }, + }, + + OP_BIT_AND: { + Id: OP_BIT_AND, + Overloads: []Function{ + { + Index: 0, + Flag: plan.Function_STRICT, + Layout: COMPARISON_OPERATOR, + Args: []types.T{ + types.T_uint8, + types.T_uint8, + }, + ReturnTyp: types.T_uint8, + Fn: operator.OpBitAndFun[uint8], + }, + { + Index: 1, + Flag: plan.Function_STRICT, + Layout: COMPARISON_OPERATOR, + Args: []types.T{ + types.T_int8, + types.T_int8, + }, + ReturnTyp: types.T_int8, + Fn: operator.OpBitAndFun[int8], + }, + { + Index: 2, + Flag: plan.Function_STRICT, + Layout: COMPARISON_OPERATOR, + Args: []types.T{ + types.T_uint16, + types.T_uint16, + }, + ReturnTyp: types.T_uint16, + Fn: operator.OpBitAndFun[uint16], + }, + { + Index: 3, + Flag: plan.Function_STRICT, + Layout: COMPARISON_OPERATOR, + Args: []types.T{ + types.T_int16, + types.T_int16, + }, + ReturnTyp: types.T_int16, + Fn: operator.OpBitAndFun[int16], + }, + { + Index: 4, + Flag: plan.Function_STRICT, + Layout: COMPARISON_OPERATOR, + Args: []types.T{ + types.T_uint32, + types.T_uint32, + }, + ReturnTyp: types.T_uint32, + Fn: operator.OpBitAndFun[uint32], + }, + { + Index: 5, + Flag: plan.Function_STRICT, + Layout: COMPARISON_OPERATOR, + Args: []types.T{ + types.T_int32, + types.T_int32, + }, + ReturnTyp: types.T_int32, + Fn: operator.OpBitAndFun[int32], + }, + { + Index: 6, + Flag: plan.Function_STRICT, + Layout: COMPARISON_OPERATOR, + Args: []types.T{ + types.T_uint64, + types.T_uint64, + }, + ReturnTyp: types.T_uint64, + Fn: operator.OpBitAndFun[uint64], + }, + { + Index: 7, + Flag: plan.Function_STRICT, + Layout: COMPARISON_OPERATOR, + Args: []types.T{ + types.T_int64, + types.T_int64, + }, + ReturnTyp: types.T_int64, + Fn: operator.OpBitAndFun[int64], + }, + }, + }, + ISNOT: { Id: ISNOT, Overloads: []Function{ diff --git a/test/cases/function/func_string_lpad_rpad.test b/test/cases/function/func_string_lpad_rpad.test index 8d31fe2c0..3e05ac8cd 100644 --- a/test/cases/function/func_string_lpad_rpad.test +++ b/test/cases/function/func_string_lpad_rpad.test @@ -89,7 +89,7 @@ SELECT ((+0) IN (32767.1))); --- @bvt:issue#3619 +-- @bvt:issue#3588 SELECT ((rpad(1.0,2048,1)) = ('4(') ^ (0.1)); -- @bvt:issue -- GitLab