Skip to content

Commit

Permalink
Fix CAST(interval day to second as varchar)
Browse files Browse the repository at this point in the history
Summary:
CAST expression used to handle INTERVAL DAY TO SECOND values as BIGINT. This allowed invalid casts (e.g. from varchar, to double, etc.) and produced incorrect results when casting to varchar.

Casting 1 second interval used to return '1000' instead of '0 00:00:01.000'.

Fixes facebookincubator#9384

Differential Revision: D55796600
  • Loading branch information
mbasmanova authored and facebook-github-bot committed Apr 5, 2024
1 parent 60aa8f8 commit b34a61e
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 0 deletions.
51 changes: 51 additions & 0 deletions velox/expression/CastExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ VectorPtr CastExpr::castFromDate(
auto* resultFlatVector = castResult->as<FlatVector<StringView>>();
applyToSelectedNoThrowLocal(context, rows, castResult, [&](int row) {
try {
// TODO Optimize to avoid creating an intermediate string.
auto output = DATE()->toString(inputFlatVector->valueAt(row));
auto writer = exec::StringWriter<>(resultFlatVector, row);
writer.resize(output.size());
Expand Down Expand Up @@ -247,6 +248,49 @@ VectorPtr CastExpr::castToDate(
}
}

VectorPtr CastExpr::castFromIntervalDayTime(
const SelectivityVector& rows,
const BaseVector& input,
exec::EvalCtx& context,
const TypePtr& toType) {
VectorPtr castResult;
context.ensureWritable(rows, toType, castResult);
(*castResult).clearNulls(rows);

auto* inputFlatVector = input.as<SimpleVector<int64_t>>();
switch (toType->kind()) {
case TypeKind::VARCHAR: {
auto* resultFlatVector = castResult->as<FlatVector<StringView>>();
applyToSelectedNoThrowLocal(context, rows, castResult, [&](int row) {
try {
// TODO Optimize to avoid creating an intermediate string.
auto output =
INTERVAL_DAY_TIME()->valueToString(inputFlatVector->valueAt(row));
auto writer = exec::StringWriter<>(resultFlatVector, row);
writer.resize(output.size());
std::memcpy(writer.data(), output.data(), output.size());
writer.finalize();
} catch (const VeloxException& ue) {
if (!ue.isUserError()) {
throw;
}
VELOX_USER_FAIL(
makeErrorMessage(input, row, toType) + " " + ue.message());
} catch (const std::exception& e) {
VELOX_USER_FAIL(
makeErrorMessage(input, row, toType) + " " + e.what());
}
});
return castResult;
}
default:
VELOX_UNSUPPORTED(
"Cast from {} to {} is not supported",
INTERVAL_DAY_TIME()->toString(),
toType->toString());
}
}

namespace {
void propagateErrorsOrSetNulls(
bool setNullInResultAtError,
Expand Down Expand Up @@ -677,6 +721,13 @@ void CastExpr::applyPeeled(
result = castFromDate(rows, input, context, toType);
} else if (toType->isDate()) {
result = castToDate(rows, input, context, fromType);
} else if (fromType->isIntervalDayTime()) {
result = castFromIntervalDayTime(rows, input, context, toType);
} else if (toType->isIntervalDayTime()) {
VELOX_UNSUPPORTED(
"Cast from {} to {} is not supported",
fromType->toString(),
toType->toString());
} else if (toType->isShortDecimal()) {
result = applyDecimal<int64_t>(rows, input, context, fromType, toType);
} else if (toType->isLongDecimal()) {
Expand Down
6 changes: 6 additions & 0 deletions velox/expression/CastExpr.h
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,12 @@ class CastExpr : public SpecialForm {
exec::EvalCtx& context,
const TypePtr& fromType);

VectorPtr castFromIntervalDayTime(
const SelectivityVector& rows,
const BaseVector& input,
exec::EvalCtx& context,
const TypePtr& toType);

template <typename TInput, typename TOutput>
void applyDecimalCastKernel(
const SelectivityVector& rows,
Expand Down
30 changes: 30 additions & 0 deletions velox/expression/tests/CastExprTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2582,5 +2582,35 @@ TEST_F(CastExprTest, skipCastEvaluation) {
assertEqualVectors(result, expected);
}
}

TEST_F(CastExprTest, intervalDayTimeToVarchar) {
auto data = makeRowVector({
makeFlatVector<int64_t>(
{kMillisInDay,
kMillisInHour,
kMillisInMinute,
kMillisInSecond,
5 * kMillisInDay + 14 * kMillisInHour + 20 * kMillisInMinute +
52 * kMillisInSecond + 88},
INTERVAL_DAY_TIME()),
});

auto result = evaluate("cast(c0 as varchar)", data);
auto expected = makeFlatVector<std::string>({
"1 00:00:00.000",
"0 01:00:00.000",
"0 00:01:00.000",
"0 00:00:01.000",
"5 14:20:52.088",
});

assertEqualVectors(result, expected);

// Reverse cast is not supported.
VELOX_ASSERT_THROW(
evaluate("cast('5 14:20:52.088' as interval day to second)", data),
"Cast from VARCHAR to INTERVAL DAY TO SECOND is not supported");
}

} // namespace
} // namespace facebook::velox::test

0 comments on commit b34a61e

Please sign in to comment.