Skip to content

Commit

Permalink
Fix cast from date to varchar
Browse files Browse the repository at this point in the history
Fail if the resulting string does not fit in the
bounded length of the varchar type.
  • Loading branch information
kasiafi committed Jan 19, 2022
1 parent 168bc0f commit 3701f92
Show file tree
Hide file tree
Showing 4 changed files with 21 additions and 17 deletions.
12 changes: 10 additions & 2 deletions core/trino-main/src/main/java/io/trino/type/DateOperators.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import io.airlift.slice.Slice;
import io.trino.spi.TrinoException;
import io.trino.spi.function.LiteralParameter;
import io.trino.spi.function.LiteralParameters;
import io.trino.spi.function.ScalarFunction;
import io.trino.spi.function.ScalarOperator;
Expand All @@ -27,6 +28,7 @@
import static io.trino.spi.function.OperatorType.CAST;
import static io.trino.util.DateTimeUtils.parseDate;
import static io.trino.util.DateTimeUtils.printDate;
import static java.lang.String.format;

public final class DateOperators
{
Expand All @@ -35,9 +37,15 @@ private DateOperators() {}
@ScalarOperator(CAST)
@LiteralParameters("x")
@SqlType("varchar(x)")
public static Slice castToSlice(@SqlType(StandardTypes.DATE) long value)
public static Slice castToSlice(@LiteralParameter("x") long x, @SqlType(StandardTypes.DATE) long value)
{
return utf8Slice(printDate((int) value));
String stringValue = printDate((int) value);
// String is all-ASCII, so String.length() here returns actual code points count
if (stringValue.length() <= x) {
return utf8Slice(stringValue);
}

throw new TrinoException(INVALID_CAST_ARGUMENT, format("Value %s cannot be represented as varchar(%s)", stringValue, x));
}

@ScalarFunction("date")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -839,9 +839,13 @@ public void testCastDateToBoundedVarchar()
assertEvaluatedEquals("CAST(DATE '2013-02-02' AS varchar(10))", "'2013-02-02'");
assertEvaluatedEquals("CAST(DATE '-2013-02-02' AS varchar(50))", "'-2013-02-02'");

// incorrect behavior: the result value does not fit in the type
assertEvaluatedEquals("CAST(DATE '2013-02-02' AS varchar(9))", "'2013-02-02'");
assertEvaluatedEquals("CAST(DATE '-2013-02-02' AS varchar(9))", "'-2013-02-02'");
// the result value does not fit in the type
assertTrinoExceptionThrownBy(() -> evaluate("CAST(DATE '2013-02-02' AS varchar(9))"))
.hasErrorCode(INVALID_CAST_ARGUMENT)
.hasMessage("Value 2013-02-02 cannot be represented as varchar(9)");
assertTrinoExceptionThrownBy(() -> evaluate("CAST(DATE '-2013-02-02' AS varchar(9))"))
.hasErrorCode(INVALID_CAST_ARGUMENT)
.hasMessage("Value -2013-02-02 cannot be represented as varchar(9)");
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@
import static io.trino.sql.planner.TypeAnalyzer.createTestingTypeAnalyzer;
import static io.trino.sql.planner.iterative.rule.SimplifyExpressions.rewrite;
import static java.util.stream.Collectors.toList;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.testng.Assert.assertEquals;

public class TestSimplifyExpressions
Expand Down Expand Up @@ -250,14 +249,9 @@ public void testCastDateToBoundedVarchar()
assertSimplifies("CAST(DATE '2013-02-02' AS varchar(10))", "'2013-02-02'");
assertSimplifies("CAST(DATE '2013-02-02' AS varchar(50))", "CAST('2013-02-02' AS varchar(50))");

// the cast operator returns a value that is too long for the expected type ('2013-02-02' for varchar(3))
// the LiteralEncoder detects the mismatch and fails
assertThatThrownBy(() -> simplify("CAST(DATE '2013-02-02' AS varchar(3))"))
.hasMessage("Value [2013-02-02] does not fit in type varchar(3)");

// the cast operator returns a value that is too long for the expected type ('2013-02-02' for varchar(3))
// the value is nested in a comparison expression, so the mismatch is not detected by the LiteralEncoder
assertSimplifies("CAST(DATE '2013-02-02' AS varchar(3)) = '2013-02-02'", "true");
// cast from date to varchar fails, so the expression is not modified
assertSimplifies("CAST(DATE '2013-02-02' AS varchar(3))", "CAST(DATE '2013-02-02' AS varchar(3))");
assertSimplifies("CAST(DATE '2013-02-02' AS varchar(3)) = '2013-02-02'", "CAST(DATE '2013-02-02' AS varchar(3)) = '2013-02-02'");
}

private static void assertSimplifies(String expression, String expected)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -272,9 +272,7 @@ public void testDateCastToVarchar()
assertFunction("cast(DATE '13-2-2' AS varchar)", VARCHAR, "0013-02-02");
assertFunction("cast(DATE '2013-02-02' AS varchar(50))", createVarcharType(50), "2013-02-02");
assertFunction("cast(DATE '2013-02-02' AS varchar(10))", createVarcharType(10), "2013-02-02");

// cast operator returns a value that does not fit in the result type. this causes error in the LiteralEncoder
assertFunctionThrowsIncorrectly("cast(DATE '2013-02-02' AS varchar(9))", IllegalArgumentException.class, "Value .2013-02-02. does not fit in type varchar.9.");
assertInvalidCast("cast(DATE '2013-02-02' AS varchar(9))", "Value 2013-02-02 cannot be represented as varchar(9)");
}

private static SqlDate toDate(DateTime dateTime)
Expand Down

0 comments on commit 3701f92

Please sign in to comment.