Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -315,8 +315,8 @@ object V2ExpressionUtils extends SQLConfHelper with Logging {
case "EXP" => convertUnaryExpr(expr, Exp)
case "POWER" => convertBinaryExpr(expr, Pow)
case "SQRT" => convertUnaryExpr(expr, Sqrt)
case "FLOOR" => convertUnaryExpr(expr, Floor)
case "CEIL" => convertUnaryExpr(expr, Ceil)
case "FLOOR" => convertUnaryExpr(expr, Floor(_))
case "CEIL" => convertUnaryExpr(expr, Ceil(_))
case "ROUND" => convertBinaryExpr(expr, Round(_, _, ansiEnabled = true))
case "CBRT" => convertUnaryExpr(expr, Cbrt)
case "DEGREES" => convertUnaryExpr(expr, ToDegrees)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,19 +252,36 @@ case class Cbrt(child: Expression) extends UnaryMathExpression(math.cbrt, "CBRT"
override protected def withNewChildInternal(newChild: Expression): Cbrt = copy(child = newChild)
}

case class Ceil(child: Expression) extends UnaryMathExpression(math.ceil, "CEIL") {
private object CeilFloor {
def doubleToLong(value: Double, context: QueryContext): Long = {
if (!value.isNaN &&
(value < Long.MinValue.toDouble || value >= Long.MaxValue.toDouble)) {
throw QueryExecutionErrors.arithmeticOverflowError("long overflow", context = context)
}
value.toLong
}
}

case class Ceil(child: Expression, failOnError: Boolean = SQLConf.get.ansiEnabled)
extends UnaryMathExpression(math.ceil, "CEIL") with SupportQueryContext {
override def dataType: DataType = child.dataType match {
case dt @ DecimalType.Fixed(_, 0) => dt
case DecimalType.Fixed(precision, scale) =>
DecimalType.bounded(precision - scale + 1, 0)
case _ => LongType
}

override def initQueryContext(): Option[QueryContext] = {
if (failOnError) Some(origin.context) else None
}

override def inputTypes: Seq[AbstractDataType] =
Seq(TypeCollection(DoubleType, DecimalType, LongType))

protected override def nullSafeEval(input: Any): Any = child.dataType match {
case LongType => input.asInstanceOf[Long]
case DoubleType if failOnError =>
CeilFloor.doubleToLong(f(input.asInstanceOf[Double]), getContextOrNull())
case DoubleType => f(input.asInstanceOf[Double]).toLong
case DecimalType.Fixed(_, _) => input.asInstanceOf[Decimal].ceil
}
Expand All @@ -275,6 +292,21 @@ case class Ceil(child: Expression) extends UnaryMathExpression(math.ceil, "CEIL"
case DecimalType.Fixed(_, _) =>
defineCodeGen(ctx, ev, c => s"$c.ceil()")
case LongType => defineCodeGen(ctx, ev, c => s"$c")
case DoubleType if failOnError =>
nullSafeCodeGen(ctx, ev, c => {
val roundedValue = ctx.freshName("roundedValue")
val errorContext = getContextOrNullCode(ctx)
s"""
|double $roundedValue = java.lang.Math.${funcName}($c);
|if (!java.lang.Double.isNaN($roundedValue) &&
| ($roundedValue < (double) java.lang.Long.MIN_VALUE ||
| $roundedValue >= (double) java.lang.Long.MAX_VALUE)) {
| throw QueryExecutionErrors.arithmeticOverflowError(
| "long overflow", "", $errorContext);
|}
|${ev.value} = (long) $roundedValue;
|""".stripMargin
})
case _ => defineCodeGen(ctx, ev, c => s"(long)(java.lang.Math.${funcName}($c))")
}
}
Expand Down Expand Up @@ -531,19 +563,26 @@ case class Expm1(child: Expression) extends UnaryMathExpression(StrictMath.expm1
override protected def withNewChildInternal(newChild: Expression): Expm1 = copy(child = newChild)
}

case class Floor(child: Expression) extends UnaryMathExpression(math.floor, "FLOOR") {
case class Floor(child: Expression, failOnError: Boolean = SQLConf.get.ansiEnabled)
extends UnaryMathExpression(math.floor, "FLOOR") with SupportQueryContext {
override def dataType: DataType = child.dataType match {
case dt @ DecimalType.Fixed(_, 0) => dt
case DecimalType.Fixed(precision, scale) =>
DecimalType.bounded(precision - scale + 1, 0)
case _ => LongType
}

override def initQueryContext(): Option[QueryContext] = {
if (failOnError) Some(origin.context) else None
}

override def inputTypes: Seq[AbstractDataType] =
Seq(TypeCollection(DoubleType, DecimalType, LongType))

protected override def nullSafeEval(input: Any): Any = child.dataType match {
case LongType => input.asInstanceOf[Long]
case DoubleType if failOnError =>
CeilFloor.doubleToLong(f(input.asInstanceOf[Double]), getContextOrNull())
case DoubleType => f(input.asInstanceOf[Double]).toLong
case DecimalType.Fixed(_, _) => input.asInstanceOf[Decimal].floor
}
Expand All @@ -554,11 +593,26 @@ case class Floor(child: Expression) extends UnaryMathExpression(math.floor, "FLO
case DecimalType.Fixed(_, _) =>
defineCodeGen(ctx, ev, c => s"$c.floor()")
case LongType => defineCodeGen(ctx, ev, c => s"$c")
case DoubleType if failOnError =>
nullSafeCodeGen(ctx, ev, c => {
val roundedValue = ctx.freshName("roundedValue")
val errorContext = getContextOrNullCode(ctx)
s"""
|double $roundedValue = java.lang.Math.${funcName}($c);
|if (!java.lang.Double.isNaN($roundedValue) &&
| ($roundedValue < (double) java.lang.Long.MIN_VALUE ||
| $roundedValue >= (double) java.lang.Long.MAX_VALUE)) {
| throw QueryExecutionErrors.arithmeticOverflowError(
| "long overflow", "", $errorContext);
|}
|${ev.value} = (long) $roundedValue;
|""".stripMargin
})
case _ => defineCodeGen(ctx, ev, c => s"(long)(java.lang.Math.${funcName}($c))")
}
}
override protected def withNewChildInternal(newChild: Expression): Floor =
copy(child = newChild)
}
override protected def withNewChildInternal(newChild: Expression): Floor =
copy(child = newChild)
}

// scalastyle:off line.size.limit
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -393,13 +393,21 @@ class MathExpressionsSuite extends SparkFunSuite with ExpressionEvalHelper {
}

test("ceil") {
testUnary(Ceil, (d: Double) => math.ceil(d).toLong)
checkConsistencyBetweenInterpretedAndCodegen(Ceil, DoubleType)

testUnary(Ceil, (d: Decimal) => d.ceil, (-20 to 20).map(x => Decimal(x * 0.1)))
checkConsistencyBetweenInterpretedAndCodegen(Ceil, DecimalType(25, 3))
checkConsistencyBetweenInterpretedAndCodegen(Ceil, DecimalType(25, 0))
checkConsistencyBetweenInterpretedAndCodegen(Ceil, DecimalType(5, 0))
testUnary(Ceil(_), (d: Double) => math.ceil(d).toLong)
checkConsistencyBetweenInterpretedAndCodegenAllowingException(
(child: Expression) => Ceil(child),
DoubleType)

testUnary(Ceil(_), (d: Decimal) => d.ceil, (-20 to 20).map(x => Decimal(x * 0.1)))
checkConsistencyBetweenInterpretedAndCodegen(
(child: Expression) => Ceil(child),
DecimalType(25, 3))
checkConsistencyBetweenInterpretedAndCodegen(
(child: Expression) => Ceil(child),
DecimalType(25, 0))
checkConsistencyBetweenInterpretedAndCodegen(
(child: Expression) => Ceil(child),
DecimalType(5, 0))

val doublePi: Double = 3.1415
val floatPi: Float = 3.1415f
Expand All @@ -420,16 +428,29 @@ class MathExpressionsSuite extends SparkFunSuite with ExpressionEvalHelper {
checkEvaluation(checkDataTypeAndCast(Ceil(1234567890123456L)), 1234567890123456L, EmptyRow)
checkEvaluation(checkDataTypeAndCast(Ceil(0.01)), 1L, EmptyRow)
checkEvaluation(checkDataTypeAndCast(Ceil(-0.10)), 0L, EmptyRow)

checkExceptionInExpression[SparkArithmeticException](
Ceil(Literal(1e30), failOnError = true),
"ARITHMETIC_OVERFLOW")
checkEvaluation(Ceil(Literal(1e30), failOnError = false), Long.MaxValue)
}

test("floor") {
testUnary(Floor, (d: Double) => math.floor(d).toLong)
checkConsistencyBetweenInterpretedAndCodegen(Floor, DoubleType)

testUnary(Floor, (d: Decimal) => d.floor, (-20 to 20).map(x => Decimal(x * 0.1)))
checkConsistencyBetweenInterpretedAndCodegen(Floor, DecimalType(25, 3))
checkConsistencyBetweenInterpretedAndCodegen(Floor, DecimalType(25, 0))
checkConsistencyBetweenInterpretedAndCodegen(Floor, DecimalType(5, 0))
testUnary(Floor(_), (d: Double) => math.floor(d).toLong)
checkConsistencyBetweenInterpretedAndCodegenAllowingException(
(child: Expression) => Floor(child),
DoubleType)

testUnary(Floor(_), (d: Decimal) => d.floor, (-20 to 20).map(x => Decimal(x * 0.1)))
checkConsistencyBetweenInterpretedAndCodegen(
(child: Expression) => Floor(child),
DecimalType(25, 3))
checkConsistencyBetweenInterpretedAndCodegen(
(child: Expression) => Floor(child),
DecimalType(25, 0))
checkConsistencyBetweenInterpretedAndCodegen(
(child: Expression) => Floor(child),
DecimalType(5, 0))

val doublePi: Double = 3.1415
val floatPi: Float = 3.1415f
Expand All @@ -450,6 +471,11 @@ class MathExpressionsSuite extends SparkFunSuite with ExpressionEvalHelper {
checkEvaluation(checkDataTypeAndCast(Floor(1234567890123456L)), 1234567890123456L, EmptyRow)
checkEvaluation(checkDataTypeAndCast(Floor(0.01)), 0L, EmptyRow)
checkEvaluation(checkDataTypeAndCast(Floor(-0.10)), -1L, EmptyRow)

checkExceptionInExpression[SparkArithmeticException](
Floor(Literal(-1e30), failOnError = true),
"ARITHMETIC_OVERFLOW")
checkEvaluation(Floor(Literal(-1e30), failOnError = false), Long.MinValue)
}

test("factorial") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -380,37 +380,73 @@ struct<five:string,f1:double,round_f1:double>
-- !query
select ceil(f1) as ceil_f1 from float8_tbl f
-- !query schema
struct<ceil_f1:bigint>
struct<>
-- !query output
-34
0
1
1005
9223372036854775807
org.apache.spark.SparkArithmeticException
{
"errorClass" : "ARITHMETIC_OVERFLOW",
"sqlState" : "22003",
"messageParameters" : {
"alternative" : "",
"config" : "\"spark.sql.ansi.enabled\"",
"message" : "overflow"
},
"queryContext" : [ {
"objectType" : "",
"objectName" : "",
"startIndex" : 8,
"stopIndex" : 15,
"fragment" : "ceil(f1)"
} ]
}


-- !query
select ceiling(f1) as ceiling_f1 from float8_tbl f
-- !query schema
struct<ceiling_f1:bigint>
struct<>
-- !query output
-34
0
1
1005
9223372036854775807
org.apache.spark.SparkArithmeticException
{
"errorClass" : "ARITHMETIC_OVERFLOW",
"sqlState" : "22003",
"messageParameters" : {
"alternative" : "",
"config" : "\"spark.sql.ansi.enabled\"",
"message" : "overflow"
},
"queryContext" : [ {
"objectType" : "",
"objectName" : "",
"startIndex" : 8,
"stopIndex" : 18,
"fragment" : "ceiling(f1)"
} ]
}


-- !query
select floor(f1) as floor_f1 from float8_tbl f
-- !query schema
struct<floor_f1:bigint>
struct<>
-- !query output
-35
0
0
1004
9223372036854775807
org.apache.spark.SparkArithmeticException
{
"errorClass" : "ARITHMETIC_OVERFLOW",
"sqlState" : "22003",
"messageParameters" : {
"alternative" : "",
"config" : "\"spark.sql.ansi.enabled\"",
"message" : "overflow"
},
"queryContext" : [ {
"objectType" : "",
"objectName" : "",
"startIndex" : 8,
"stopIndex" : 16,
"fragment" : "floor(f1)"
} ]
}


-- !query
Expand Down