Bläddra i källkod

feat(math): add common SQL math funcs/aliases (#2082)

* feat(math): add common SQL math funcs/aliases

Signed-off-by: xjasonlyu <xjasonlyu@gmail.com>

* fix test errors for pow

Signed-off-by: xjasonlyu <xjasonlyu@gmail.com>

* add test cases for new funcs

Signed-off-by: xjasonlyu <xjasonlyu@gmail.com>

---------

Signed-off-by: xjasonlyu <xjasonlyu@gmail.com>
Jason Lyu 1 år sedan
förälder
incheckning
c6dbe589fd

+ 25 - 1
docs/en_US/sqls/functions/mathematical_functions.md

@@ -77,8 +77,12 @@ Performs a bitwise NOT on the bit representations of the Int(-converted) argumen
 
 ## CEIL
 
+`CEIL()` is a synonym for [`CEILING()`](#ceiling).
+
+## CEILING
+
 ```text
-ceil(col)
+ceiling(col)
 ```
 
 The smallest integer value that is greater than or equal to the argument.
@@ -107,6 +111,14 @@ exp(col)
 
 Returns Euler's number e raised to the power of a double value.
 
+## FLOOR
+
+```text
+floor(col)
+```
+
+Returns the largest integer value not greater than X.
+
 ## LN
 
 ```text
@@ -131,6 +143,18 @@ mod(col1, col2)
 
 Returns the remainder of the division of the first argument by the second argument.
 
+## PI
+
+```text
+pi()
+```
+
+Returns the value of π (pi).
+
+## POW
+
+`POW()` is a synonym for [`POWER()`](#power).
+
 ## POWER
 
 ```text

+ 25 - 1
docs/zh_CN/sqls/functions/mathematical_functions.md

@@ -76,8 +76,12 @@ bitnot(col)
 
 ## CEIL
 
+`CEIL()` 是 [`CEILING()`](#ceiling) 的别名。
+
+## CEILING
+
 ```text
-ceil(col)
+ceiling(col)
 ```
 
 将值舍入到最接近的 BIGINT 值。
@@ -106,6 +110,14 @@ exp(col)
 
 返回小数点参数的 e。
 
+## FLOOR
+
+```text
+floor(col)
+```
+
+返回小于 X 的最大整数值。
+
 ## LN
 
 ```text
@@ -130,6 +142,18 @@ mod(col1, col2)
 
 返回第一个参数除以第二个参数的余数。
 
+## PI
+
+```text
+pi()
+```
+
+返回 π (pi) 的值。
+
+## POW
+
+`POW()` 是函数 [`POWER()`](#power) 的别名。
+
 ## POWER
 
 ```text

+ 1 - 1
internal/binder/function/funcs_array_test.go

@@ -562,7 +562,7 @@ func TestArrayCommonFunctions(t *testing.T) {
 			args: []interface{}{
 				"pow", []interface{}{0, -0.4, 1.2},
 			},
-			result: fmt.Errorf("unknown built-in function: pow."),
+			result: fmt.Errorf("validate function arguments failed."),
 		},
 		{
 			name: "array_map",

+ 23 - 1
internal/binder/function/funcs_math.go

@@ -188,7 +188,7 @@ func registerMathFunc() {
 		},
 		check: returnNilIfHasAnyNil,
 	}
-	builtins["ceil"] = builtinFunc{
+	builtins["ceiling"] = builtinFunc{
 		fType: ast.FuncTypeScalar,
 		exec: func(ctx api.FunctionContext, args []interface{}) (interface{}, bool) {
 			if v, e := cast.ToFloat64(args[0], cast.CONVERT_SAMEKIND); e == nil {
@@ -200,6 +200,7 @@ func registerMathFunc() {
 		val:   ValidateOneNumberArg,
 		check: returnNilIfHasAnyNil,
 	}
+	builtins["ceil"] = builtins["ceiling"] // Synonym for CEILING.
 	builtins["cos"] = builtinFunc{
 		fType: ast.FuncTypeScalar,
 		exec: func(ctx api.FunctionContext, args []interface{}) (interface{}, bool) {
@@ -251,6 +252,18 @@ func registerMathFunc() {
 		val:   ValidateOneNumberArg,
 		check: returnNilIfHasAnyNil,
 	}
+	builtins["floor"] = builtinFunc{
+		fType: ast.FuncTypeScalar,
+		exec: func(ctx api.FunctionContext, args []interface{}) (interface{}, bool) {
+			if v, e := cast.ToFloat64(args[0], cast.CONVERT_SAMEKIND); e == nil {
+				return math.Floor(v), true
+			} else {
+				return e, false
+			}
+		},
+		val:   ValidateOneNumberArg,
+		check: returnNilIfHasAnyNil,
+	}
 	builtins["ln"] = builtinFunc{
 		fType: ast.FuncTypeScalar,
 		exec: func(ctx api.FunctionContext, args []interface{}) (interface{}, bool) {
@@ -301,6 +314,14 @@ func registerMathFunc() {
 		val:   ValidateTwoNumberArg,
 		check: returnNilIfHasAnyNil,
 	}
+	builtins["pi"] = builtinFunc{
+		fType: ast.FuncTypeScalar,
+		exec: func(_ api.FunctionContext, _ []interface{}) (interface{}, bool) {
+			return math.Pi, true
+		},
+		val:   ValidateNoArg,
+		check: returnNilIfHasAnyNil,
+	}
 	builtins["power"] = builtinFunc{
 		fType: ast.FuncTypeScalar,
 		exec: func(ctx api.FunctionContext, args []interface{}) (interface{}, bool) {
@@ -317,6 +338,7 @@ func registerMathFunc() {
 		val:   ValidateTwoNumberArg,
 		check: returnNilIfHasAnyNil,
 	}
+	builtins["pow"] = builtins["power"] // Synonym for POWER.
 	builtins["rand"] = builtinFunc{
 		fType: ast.FuncTypeScalar,
 		exec: func(ctx api.FunctionContext, args []interface{}) (interface{}, bool) {

+ 26 - 0
internal/binder/function/funcs_math_test.go

@@ -41,6 +41,10 @@ func TestFuncMath(t *testing.T) {
 	if !ok {
 		t.Fatal("builtin not found")
 	}
+	fFloor, ok := builtins["floor"]
+	if !ok {
+		t.Fatal("builtin not found")
+	}
 	fLn, ok := builtins["ln"]
 	if !ok {
 		t.Fatal("builtin not found")
@@ -101,6 +105,10 @@ func TestFuncMath(t *testing.T) {
 	if !ok {
 		t.Fatal("builtin not found")
 	}
+	fPi, ok := builtins["pi"]
+	if !ok {
+		t.Fatal("builtin not found")
+	}
 	fRound, ok := builtins["round"]
 	if !ok {
 		t.Fatal("builtin not found")
@@ -162,6 +170,8 @@ func TestFuncMath(t *testing.T) {
 				math.Sinh(-10),
 				math.Tan(-10),
 				math.Tanh(-10),
+				math.Floor(-10),
+				math.Pi,
 			},
 		}, { // 1
 			args: []interface{}{
@@ -192,6 +202,8 @@ func TestFuncMath(t *testing.T) {
 				math.Sinh(10),
 				math.Tan(10),
 				math.Tanh(10),
+				math.Floor(10),
+				math.Pi,
 			},
 		}, { // 2
 			args: []interface{}{
@@ -222,6 +234,8 @@ func TestFuncMath(t *testing.T) {
 				math.Sinh(-10.5),
 				math.Tan(-10.5),
 				math.Tanh(-10.5),
+				math.Floor(-10.5),
+				math.Pi,
 			},
 		}, { // 3
 			args: []interface{}{
@@ -252,6 +266,8 @@ func TestFuncMath(t *testing.T) {
 				math.Sinh(10.5),
 				math.Tan(10.5),
 				math.Tanh(10.5),
+				math.Floor(10.5),
+				math.Pi,
 			},
 		}, { // 4
 			args: []interface{}{
@@ -282,6 +298,8 @@ func TestFuncMath(t *testing.T) {
 				math.Sinh(0),
 				math.Tan(0),
 				math.Tanh(0),
+				float64(0),
+				math.Pi,
 			},
 		},
 	}
@@ -382,6 +400,14 @@ func TestFuncMath(t *testing.T) {
 		if !reflect.DeepEqual(rTanh, tt.res[23]) {
 			t.Errorf("%d.23 tanh result mismatch,\ngot:\t%v \nwant:\t%v", i, rTanh, tt.res[23])
 		}
+		rFloor, _ := fFloor.exec(fctx, tt.args)
+		if !reflect.DeepEqual(rFloor, tt.res[24]) {
+			t.Errorf("%d.24 exp result mismatch,\ngot:\t%v \nwant:\t%v", i, rFloor, tt.res[24])
+		}
+		rPi, _ := fPi.exec(fctx, tt.args)
+		if !reflect.DeepEqual(rPi, tt.res[25]) {
+			t.Errorf("%d.25 exp result mismatch,\ngot:\t%v \nwant:\t%v", i, rPi, tt.res[25])
+		}
 	}
 }