From e0b8b29bebb1e746100558d7dc3368d85c62aebf Mon Sep 17 00:00:00 2001 From: Arthur Peng Date: Sun, 4 Jan 2015 17:28:29 +0000 Subject: [PATCH] [IMPALA-1660]Netezza math functions support 1.Add cot fucntion 2.Add double type support in truncate 3.Add factorial operator support --- be/src/exprs/expr-test.cc | 16 ++++++++++++++++ be/src/exprs/math-functions.cc | 6 ++++++ be/src/exprs/math-functions.h | 2 ++ be/src/exprs/operators.cc | 17 +++++++++++++++++ be/src/exprs/operators.h | 5 +++++ common/function-registry/impala_functions.py | 2 ++ fe/src/main/cup/sql-parser.y | 2 ++ .../cloudera/impala/analysis/ArithmeticExpr.java | 20 ++++++++++++-------- 8 files changed, 62 insertions(+), 8 deletions(-) diff --git a/be/src/exprs/expr-test.cc b/be/src/exprs/expr-test.cc index e092748..4d24259 100644 --- a/be/src/exprs/expr-test.cc +++ b/be/src/exprs/expr-test.cc @@ -682,6 +682,13 @@ class ExprTest : public testing::Test { TestIsNull("~NULL", TYPE_INT); } + // Test factorial operator. + void TestFactorialArithmeticOp() { + TestValue("4!", TYPE_DOUBLE, static_cast(24)); + TestValue("0!", TYPE_DOUBLE, static_cast(1)); + TestValue("500!", TYPE_DOUBLE, numeric_limits::infinity()); + TestIsNull("NULL!", TYPE_DOUBLE); + } // Test casting stmt to all types. Expected result is val. template void TestCast(const string& stmt, T val) { @@ -959,6 +966,9 @@ TEST_F(ExprTest, ArithmeticExprs) { // Test all arithmetic exprs with only NULL operands. TestNullOperandsArithmeticOps(); + + // Test behavior of factorial operator. + TestFactorialArithmeticOp(); } TEST_F(ExprTest, DecimalArithmeticExprs) { @@ -2084,6 +2094,8 @@ TEST_F(ExprTest, MathTrigonometricFunctions) { TestValue("tan(pi())", TYPE_DOUBLE, tan(M_PI)); TestValue("atan(pi())", TYPE_DOUBLE, atan(M_PI)); TestValue("atan(pi() * - 1.0)", TYPE_DOUBLE, atan(M_PI * -1.0)); + TestValue("cot(pi() / 2.0)", TYPE_DOUBLE, tan(0.0)); + TestValue("cot(pi())", TYPE_DOUBLE, tan(M_PI_2 - M_PI)); // this gets a very very small number rather than 0. // TestValue("radians(0)", TYPE_DOUBLE, 0); TestValue("radians(180.0)", TYPE_DOUBLE, M_PI); @@ -2097,6 +2109,7 @@ TEST_F(ExprTest, MathTrigonometricFunctions) { TestIsNull("acos(NULL)", TYPE_DOUBLE); TestIsNull("tan(NULL)", TYPE_DOUBLE); TestIsNull("atan(NULL)", TYPE_DOUBLE); + TestIsNull("cot(NULL)", TYPE_DOUBLE); TestIsNull("radians(NULL)", TYPE_DOUBLE); TestIsNull("degrees(NULL)", TYPE_DOUBLE); } @@ -2503,6 +2516,8 @@ TEST_F(ExprTest, MathRoundingFunctions) { TestValue("ceiling(cast(-10.05 as double))", TYPE_BIGINT, -10); TestValue("floor(cast(0.1 as double))", TYPE_BIGINT, 0); TestValue("floor(cast(-10.007 as double))", TYPE_BIGINT, -11); + TestValue("truncate(cast(0.1 as double))", TYPE_BIGINT, 0); + TestValue("truncate(cast(-10.007 as double))", TYPE_BIGINT, -10); TestValue("round(cast(1.499999 as double))", TYPE_BIGINT, 1); TestValue("round(cast(1.5 as double))", TYPE_BIGINT, 2); @@ -2528,6 +2543,7 @@ TEST_F(ExprTest, MathRoundingFunctions) { TestIsNull("ceil(cast(NULL as double))", TYPE_BIGINT); TestIsNull("ceiling(cast(NULL as double))", TYPE_BIGINT); TestIsNull("floor(cast(NULL as double))", TYPE_BIGINT); + TestIsNull("truncate(cast(NULL as double))", TYPE_BIGINT); TestIsNull("round(cast(NULL as double))", TYPE_BIGINT); TestIsNull("round(cast(NULL as double), 1)", TYPE_DOUBLE); TestIsNull("round(cast(3.14159265 as double), NULL)", TYPE_DOUBLE); diff --git a/be/src/exprs/math-functions.cc b/be/src/exprs/math-functions.cc index bbb71ed..3523489 100644 --- a/be/src/exprs/math-functions.cc +++ b/be/src/exprs/math-functions.cc @@ -59,10 +59,16 @@ ONE_ARG_MATH_FN(Atan, DoubleVal, DoubleVal, atan); ONE_ARG_MATH_FN(Sqrt, DoubleVal, DoubleVal, sqrt); ONE_ARG_MATH_FN(Ceil, BigIntVal, DoubleVal, ceil); ONE_ARG_MATH_FN(Floor, BigIntVal, DoubleVal, floor); +ONE_ARG_MATH_FN(Truncate, BigIntVal, DoubleVal, trunc); ONE_ARG_MATH_FN(Ln, DoubleVal, DoubleVal, log); ONE_ARG_MATH_FN(Log10, DoubleVal, DoubleVal, log10); ONE_ARG_MATH_FN(Exp, DoubleVal, DoubleVal, exp); +DoubleVal MathFunctions::Cot(FunctionContext* ctx, const DoubleVal& v) { + if (v.is_null) return DoubleVal::null(); + return DoubleVal(tan(M_PI_2 - v.val)); +} + FloatVal MathFunctions::Sign(FunctionContext* ctx, const DoubleVal& v) { if (v.is_null) return FloatVal::null(); return FloatVal((v.val > 0) ? 1.0f : ((v.val < 0) ? -1.0f : 0.0f)); diff --git a/be/src/exprs/math-functions.h b/be/src/exprs/math-functions.h index 6d775f3..b66533d 100644 --- a/be/src/exprs/math-functions.h +++ b/be/src/exprs/math-functions.h @@ -44,11 +44,13 @@ class MathFunctions { static DoubleVal Cos(FunctionContext*, const DoubleVal&); static DoubleVal Acos(FunctionContext*, const DoubleVal&); static DoubleVal Tan(FunctionContext*, const DoubleVal&); + static DoubleVal Cot(FunctionContext*, const DoubleVal&); static DoubleVal Atan(FunctionContext*, const DoubleVal&); static DoubleVal Sqrt(FunctionContext*, const DoubleVal&); static DoubleVal Exp(FunctionContext*, const DoubleVal&); static BigIntVal Ceil(FunctionContext*, const DoubleVal&); static BigIntVal Floor(FunctionContext*, const DoubleVal&); + static BigIntVal Truncate(FunctionContext*, const DoubleVal&); static DoubleVal Ln(FunctionContext*, const DoubleVal&); static DoubleVal Log10(FunctionContext*, const DoubleVal&); static FloatVal Sign(FunctionContext*, const DoubleVal&); diff --git a/be/src/exprs/operators.cc b/be/src/exprs/operators.cc index 97c978e..bea8b44 100644 --- a/be/src/exprs/operators.cc +++ b/be/src/exprs/operators.cc @@ -17,6 +17,9 @@ #include "runtime/string-value.h" #include "runtime/timestamp-value.h" +#include +#include + #define BINARY_OP_FN(NAME, TYPE, OP) \ TYPE Operators::NAME##_##TYPE##_##TYPE(\ FunctionContext* c, const TYPE& v1, const TYPE& v2) {\ @@ -37,6 +40,15 @@ return TYPE(~v.val);\ } +// Return infinity if overflow. +#define FACTORIAL_FN(TYPE)\ + DoubleVal Operators::Factorial_##TYPE(FunctionContext* c, const TYPE& v) {\ + namespace policies = boost::math::policies;\ + if (v.is_null) return DoubleVal::null();\ + return DoubleVal(boost::math::factorial(\ + v.val, policies::policy >())); \ + } + #define BINARY_PREDICATE_NUMERIC_FN(NAME, TYPE, OP) \ BooleanVal Operators::NAME##_##TYPE##_##TYPE(\ FunctionContext* c, const TYPE& v1, const TYPE& v2) {\ @@ -117,6 +129,11 @@ BITNOT_FN(SmallIntVal); BITNOT_FN(IntVal); BITNOT_FN(BigIntVal); +FACTORIAL_FN(TinyIntVal); +FACTORIAL_FN(SmallIntVal); +FACTORIAL_FN(IntVal); +FACTORIAL_FN(BigIntVal); + BINARY_PREDICATE_ALL_TYPES(Eq, ==); BINARY_PREDICATE_ALL_TYPES(Ne, !=); BINARY_PREDICATE_ALL_TYPES(Gt, >); diff --git a/be/src/exprs/operators.h b/be/src/exprs/operators.h index e76f9bf..3df3ce9 100644 --- a/be/src/exprs/operators.h +++ b/be/src/exprs/operators.h @@ -29,6 +29,11 @@ class Operators { static IntVal Bitnot_IntVal(FunctionContext*, const IntVal&); static BigIntVal Bitnot_BigIntVal(FunctionContext*, const BigIntVal&); + static DoubleVal Factorial_TinyIntVal(FunctionContext*, const TinyIntVal&); + static DoubleVal Factorial_SmallIntVal(FunctionContext*, const SmallIntVal&); + static DoubleVal Factorial_IntVal(FunctionContext*, const IntVal&); + static DoubleVal Factorial_BigIntVal(FunctionContext*, const BigIntVal&); + static TinyIntVal Add_TinyIntVal_TinyIntVal( FunctionContext*, const TinyIntVal&, const TinyIntVal&); static SmallIntVal Add_SmallIntVal_SmallIntVal( diff --git a/common/function-registry/impala_functions.py b/common/function-registry/impala_functions.py index 094d040..abf1f7c 100755 --- a/common/function-registry/impala_functions.py +++ b/common/function-registry/impala_functions.py @@ -239,10 +239,12 @@ functions = [ [['acos'], 'DOUBLE', ['DOUBLE'], 'impala::MathFunctions::Acos'], [['tan'], 'DOUBLE', ['DOUBLE'], 'impala::MathFunctions::Tan'], [['atan'], 'DOUBLE', ['DOUBLE'], 'impala::MathFunctions::Atan'], + [['cot'], 'DOUBLE', ['DOUBLE'], 'impala::MathFunctions::Cot'], [['radians'], 'DOUBLE', ['DOUBLE'], 'impala::MathFunctions::Radians'], [['degrees'], 'DOUBLE', ['DOUBLE'], 'impala::MathFunctions::Degrees'], [['ceil', 'ceiling'], 'BIGINT', ['DOUBLE'], 'impala::MathFunctions::Ceil'], [['floor'], 'BIGINT', ['DOUBLE'], 'impala::MathFunctions::Floor'], + [['truncate'], 'BIGINT', ['DOUBLE'], 'impala::MathFunctions::Truncate'], [['round'], 'BIGINT', ['DOUBLE'], 'impala::MathFunctions::Round'], [['round'], 'DOUBLE', ['DOUBLE', 'INT'], 'impala::MathFunctions::RoundUpTo'], [['exp'], 'DOUBLE', ['DOUBLE'], 'impala::MathFunctions::Exp'], diff --git a/fe/src/main/cup/sql-parser.y b/fe/src/main/cup/sql-parser.y index 28e5413..f984231 100644 --- a/fe/src/main/cup/sql-parser.y +++ b/fe/src/main/cup/sql-parser.y @@ -2196,6 +2196,8 @@ arithmetic_expr ::= {: RESULT = new ArithmeticExpr(ArithmeticExpr.Operator.BITXOR, e1, e2); :} | BITNOT expr:e {: RESULT = new ArithmeticExpr(ArithmeticExpr.Operator.BITNOT, e, null); :} + | expr:e NOT + {: RESULT = new ArithmeticExpr(ArithmeticExpr.Operator.FACTORIAL, e, null); :} ; // We use IDENT for the temporal unit to avoid making DAY, YEAR, etc. keywords. diff --git a/fe/src/main/java/com/cloudera/impala/analysis/ArithmeticExpr.java b/fe/src/main/java/com/cloudera/impala/analysis/ArithmeticExpr.java index 63e29a2..523a1c5 100644 --- a/fe/src/main/java/com/cloudera/impala/analysis/ArithmeticExpr.java +++ b/fe/src/main/java/com/cloudera/impala/analysis/ArithmeticExpr.java @@ -37,7 +37,8 @@ public class ArithmeticExpr extends Expr { BITAND("&", "bitand"), BITOR("|", "bitor"), BITXOR("^", "bitxor"), - BITNOT("~", "bitnot"); + BITNOT("~", "bitnot"), + FACTORIAL("!", "factorial"); private final String description_; private final String name_; @@ -61,8 +62,9 @@ public class ArithmeticExpr extends Expr { this.op_ = op; Preconditions.checkNotNull(e1); children_.add(e1); - Preconditions.checkArgument(op == Operator.BITNOT && e2 == null - || op != Operator.BITNOT && e2 != null); + Preconditions.checkArgument((op == Operator.BITNOT || + op == Operator.FACTORIAL) && e2 == null || + (op != Operator.BITNOT && op != Operator.FACTORIAL && e2 != null)); if (e2 != null) children_.add(e2); } @@ -105,6 +107,8 @@ public class ArithmeticExpr extends Expr { Operator.BITXOR.getName(), Lists.newArrayList(t, t), t)); db.addBuiltin(ScalarFunction.createBuiltinOperator( Operator.BITNOT.getName(), Lists.newArrayList(t), t)); + db.addBuiltin(ScalarFunction.createBuiltinOperator( + Operator.FACTORIAL.getName(), Lists.newArrayList(t), Type.DOUBLE)); } db.addBuiltin(ScalarFunction.createBuiltinOperator( Operator.MOD.getName(), Lists.newArrayList( @@ -166,12 +170,12 @@ public class ArithmeticExpr extends Expr { } Type t0 = getChild(0).getType(); - // bitnot is the only unary op, deal with it here - if (op_ == Operator.BITNOT) { + // bitnot and factorial are unary op, deal with them here + if (op_ == Operator.BITNOT || op_ == Operator.FACTORIAL) { // Special case ~NULL to resolve to TYPE_INT. if (!t0.isNull() && !t0.isIntegerType()) { - throw new AnalysisException("Bitwise operations only allowed on integer " + - "types: " + toSql()); + throw new AnalysisException(op_.toString() + + " operations only allowed on integer types: " + toSql()); } if (t0.isNull()) castChild(0, Type.INT); fn_ = getBuiltinFunction(analyzer, op_.getName(), collectChildReturnTypes(), @@ -181,7 +185,7 @@ public class ArithmeticExpr extends Expr { type_ = fn_.getReturnType(); return; } - + Preconditions.checkState(children_.size() == 2); // only bitnot is unary convertNumericLiteralsFromDecimal(analyzer); t0 = getChild(0).getType(); -- 1.7.9.5