Skip to content

Commit

Permalink
Test casts of approximate numeric types to bounded varchar
Browse files Browse the repository at this point in the history
Cherry-pick of trinodb/trino@631a958
This change applies to double and real

Co-authored-by: kasiafi <30203062+kasiafi@users.noreply.github.com>
  • Loading branch information
2 people authored and zhenxiao committed Mar 2, 2022
1 parent 4350f22 commit 0ae645b
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -587,41 +587,127 @@ public void testCastToString()
}

@Test
public void testCastBigintToBoundedVarchar()
{
public void testCastBigintToBoundedVarchar() {
assertEvaluatedEquals("CAST(12300000000 AS varchar(11))", "'12300000000'");
assertEvaluatedEquals("CAST(12300000000 AS varchar(50))", "'12300000000'");

try {
evaluate("CAST(12300000000 AS varchar(3))", true);
fail("Expected to throw an INVALID_CAST_ARGUMENT exception");
}
catch (PrestoException e) {
} catch (PrestoException e) {
try {
assertEquals(e.getErrorCode(), INVALID_CAST_ARGUMENT.toErrorCode());
assertEquals(e.getMessage(), "Value 12300000000 cannot be represented as varchar(3)");
}
catch (Throwable failure) {
} catch (Throwable failure) {
failure.addSuppressed(e);
throw failure;
}
}

try {
evaluate("CAST(-12300000000 AS varchar(3))", true);
}
catch (PrestoException e) {
} catch (PrestoException e) {
try {
assertEquals(e.getErrorCode(), INVALID_CAST_ARGUMENT.toErrorCode());
assertEquals(e.getMessage(), "Value -12300000000 cannot be represented as varchar(3)");
}
catch (Throwable failure) {
} catch (Throwable failure) {
failure.addSuppressed(e);
throw failure;
}
}
}

@Test
public void testCastDoubleToBoundedVarchar()
{
// NaN
assertEvaluatedEquals("CAST(0e0 / 0e0 AS varchar(3))", "'NaN'");
assertEvaluatedEquals("CAST(0e0 / 0e0 AS varchar(50))", "'NaN'");

// Infinity
assertEvaluatedEquals("CAST(DOUBLE 'Infinity' AS varchar(8))", "'Infinity'");
assertEvaluatedEquals("CAST(DOUBLE 'Infinity' AS varchar(50))", "'Infinity'");

// incorrect behavior: the string representation is not compliant with the SQL standard
assertEvaluatedEquals("CAST(0e0 AS varchar(3))", "'0.0'");
assertEvaluatedEquals("CAST(DOUBLE '0' AS varchar(3))", "'0.0'");
assertEvaluatedEquals("CAST(DOUBLE '-0' AS varchar(4))", "'-0.0'");
assertEvaluatedEquals("CAST(DOUBLE '0' AS varchar(50))", "'0.0'");

assertEvaluatedEquals("CAST(12e0 AS varchar(4))", "'12.0'");
assertEvaluatedEquals("CAST(12e2 AS varchar(6))", "'1200.0'");
assertEvaluatedEquals("CAST(12e-2 AS varchar(4))", "'0.12'");

assertEvaluatedEquals("CAST(12e0 AS varchar(50))", "'12.0'");
assertEvaluatedEquals("CAST(12e2 AS varchar(50))", "'1200.0'");
assertEvaluatedEquals("CAST(12e-2 AS varchar(50))", "'0.12'");

assertEvaluatedEquals("CAST(-12e0 AS varchar(5))", "'-12.0'");
assertEvaluatedEquals("CAST(-12e2 AS varchar(7))", "'-1200.0'");
assertEvaluatedEquals("CAST(-12e-2 AS varchar(5))", "'-0.12'");

assertEvaluatedEquals("CAST(-12e0 AS varchar(50))", "'-12.0'");
assertEvaluatedEquals("CAST(-12e2 AS varchar(50))", "'-1200.0'");
assertEvaluatedEquals("CAST(-12e-2 AS varchar(50))", "'-0.12'");

// the string representation is compliant with the SQL standard
assertEvaluatedEquals("CAST(12345678.9e0 AS varchar(12))", "'1.23456789E7'");
assertEvaluatedEquals("CAST(0.00001e0 AS varchar(6))", "'1.0E-5'");

// incorrect behavior: the result value does not fit in the type (also, it is not compliant with the SQL standard)
assertEvaluatedEquals("CAST(12e0 AS varchar(1))", "'12.0'");
assertEvaluatedEquals("CAST(-12e2 AS varchar(1))", "'-1200.0'");
assertEvaluatedEquals("CAST(0e0 AS varchar(1))", "'0.0'");
assertEvaluatedEquals("CAST(0e0 / 0e0 AS varchar(1))", "'NaN'");
assertEvaluatedEquals("CAST(DOUBLE 'Infinity' AS varchar(1))", "'Infinity'");
assertEvaluatedEquals("CAST(1200000e0 AS varchar(5))", "'1200000.0'");
}

@Test
public void testCastRealToBoundedVarchar()
{
// NaN
assertEvaluatedEquals("CAST(REAL '0e0' / REAL '0e0' AS varchar(3))", "'NaN'");
assertEvaluatedEquals("CAST(REAL '0e0' / REAL '0e0' AS varchar(50))", "'NaN'");

// Infinity
assertEvaluatedEquals("CAST(REAL 'Infinity' AS varchar(8))", "'Infinity'");
assertEvaluatedEquals("CAST(REAL 'Infinity' AS varchar(50))", "'Infinity'");

// incorrect behavior: the string representation is not compliant with the SQL standard
assertEvaluatedEquals("CAST(REAL '0' AS varchar(3))", "'0.0'");
assertEvaluatedEquals("CAST(REAL '-0' AS varchar(4))", "'-0.0'");
assertEvaluatedEquals("CAST(REAL '0' AS varchar(50))", "'0.0'");

assertEvaluatedEquals("CAST(REAL '12' AS varchar(4))", "'12.0'");
assertEvaluatedEquals("CAST(REAL '12e2' AS varchar(6))", "'1200.0'");
assertEvaluatedEquals("CAST(REAL '12e-2' AS varchar(4))", "'0.12'");

assertEvaluatedEquals("CAST(REAL '12' AS varchar(50))", "'12.0'");
assertEvaluatedEquals("CAST(REAL '12e2' AS varchar(50))", "'1200.0'");
assertEvaluatedEquals("CAST(REAL '12e-2' AS varchar(50))", "'0.12'");

assertEvaluatedEquals("CAST(REAL '-12' AS varchar(5))", "'-12.0'");
assertEvaluatedEquals("CAST(REAL '-12e2' AS varchar(7))", "'-1200.0'");
assertEvaluatedEquals("CAST(REAL '-12e-2' AS varchar(5))", "'-0.12'");

assertEvaluatedEquals("CAST(REAL '-12' AS varchar(50))", "'-12.0'");
assertEvaluatedEquals("CAST(REAL '-12e2' AS varchar(50))", "'-1200.0'");
assertEvaluatedEquals("CAST(REAL '-12e-2' AS varchar(50))", "'-0.12'");

// the string representation is compliant with the SQL standard
assertEvaluatedEquals("CAST(REAL '12345678.9e0' AS varchar(12))", "'1.2345679E7'");
assertEvaluatedEquals("CAST(REAL '0.00001e0' AS varchar(6))", "'1.0E-5'");

// incorrect behavior: the result value does not fit in the type (also, it is not compliant with the SQL standard)
assertEvaluatedEquals("CAST(REAL '12' AS varchar(1))", "'12.0'");
assertEvaluatedEquals("CAST(REAL '-12e2' AS varchar(1))", "'-1200.0'");
assertEvaluatedEquals("CAST(REAL '0' AS varchar(1))", "'0.0'");
assertEvaluatedEquals("CAST(REAL '0e0' / REAL '0e0' AS varchar(1))", "'NaN'");
assertEvaluatedEquals("CAST(REAL 'Infinity' AS varchar(1))", "'Infinity'");
assertEvaluatedEquals("CAST(REAL '1200000' AS varchar(5))", "'1200000.0'");
}

@Test
public void testCastToBoolean()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,7 @@ public void testExtractCommonPredicates()
}

@Test
public void testCastBigintToBoundedVarchar()
{
public void testCastBigintToBoundedVarchar() {
// the varchar type length is enough to contain the number's representation
assertSimplifies("CAST(12300000000 AS varchar(11))", "'12300000000'");
// The last argument "'-12300000000'" is varchar(12). Need varchar(50) to the following test pass.
Expand All @@ -148,33 +147,79 @@ public void testCastBigintToBoundedVarchar()
try {
assertSimplifies("CAST(12300000000 AS varchar(3))", "CAST(12300000000 AS varchar(3))");
fail("Expected to throw an PrestoException exception");
}
catch (PrestoException e) {
} catch (PrestoException e) {
try {
assertEquals(e.getErrorCode(), INVALID_CAST_ARGUMENT.toErrorCode());
assertEquals(e.getMessage(), "Value 12300000000 cannot be represented as varchar(3)");
}
catch (Throwable failure) {
} catch (Throwable failure) {
failure.addSuppressed(e);
throw failure;
}
}

try {
assertSimplifies("CAST(-12300000000 AS varchar(3))", "CAST(-12300000000 AS varchar(3))");
}
catch (PrestoException e) {
} catch (PrestoException e) {
try {
assertEquals(e.getErrorCode(), INVALID_CAST_ARGUMENT.toErrorCode());
assertEquals(e.getMessage(), "Value -12300000000 cannot be represented as varchar(3)");
}
catch (Throwable failure) {
} catch (Throwable failure) {
failure.addSuppressed(e);
throw failure;
}
}
}

@Test
public void testCastDoubleToBoundedVarchar()
{
// the varchar type length is enough to contain the number's representation
assertSimplifies("CAST(0e0 AS varchar(3))", "'0.0'");
assertSimplifies("CAST(-0e0 AS varchar(4))", "'-0.0'");
assertSimplifies("CAST(0e0 / 0e0 AS varchar(3))", "'NaN'");
assertSimplifies("CAST(DOUBLE 'Infinity' AS varchar(8))", "'Infinity'");
assertSimplifies("CAST(12e2 AS varchar(6))", "'1200.0'");
//assertSimplifies("CAST(-12e2 AS varchar(50))", "CAST('-1200.0' AS varchar(50))");

// the varchar type length is not enough to contain the number's representation:
// the cast operator returns a value that is too long for the expected type ('1200.0' for varchar(3))
// the value is then wrapped in another cast by the LiteralEncoder (CAST('1200.0' AS varchar(3))),
// so eventually we get a truncated string '120'
assertSimplifies("CAST(12e2 AS varchar(3))", "CAST('1200.0' AS varchar(3))");
assertSimplifies("CAST(-12e2 AS varchar(3))", "CAST('-1200.0' AS varchar(3))");
assertSimplifies("CAST(DOUBLE 'NaN' AS varchar(2))", "CAST('NaN' AS varchar(2))");
assertSimplifies("CAST(DOUBLE 'Infinity' AS varchar(7))", "CAST('Infinity' AS varchar(7))");

// the cast operator returns a value that is too long for the expected type ('1200.0' for varchar(3))
// the value is nested in a comparison expression, so it is not truncated by the LiteralEncoder
assertSimplifies("CAST(12e2 AS varchar(3)) = '1200.0'", "true");
}

@Test
public void testCastRealToBoundedVarchar()
{
// the varchar type length is enough to contain the number's representation
assertSimplifies("CAST(REAL '0e0' AS varchar(3))", "'0.0'");
assertSimplifies("CAST(REAL '-0e0' AS varchar(4))", "'-0.0'");
assertSimplifies("CAST(REAL '0e0' / REAL '0e0' AS varchar(3))", "'NaN'");
assertSimplifies("CAST(REAL 'Infinity' AS varchar(8))", "'Infinity'");
assertSimplifies("CAST(REAL '12e2' AS varchar(6))", "'1200.0'");
assertSimplifies("CAST(REAL '-12e2' AS varchar(50))", "CAST('-1200.0' AS varchar(50))");

// the varchar type length is not enough to contain the number's representation:
// the cast operator returns a value that is too long for the expected type ('1200.0' for varchar(3))
// the value is then wrapped in another cast by the LiteralEncoder (CAST('1200.0' AS varchar(3))),
// so eventually we get a truncated string '120'
assertSimplifies("CAST(REAL '12e2' AS varchar(3))", "CAST('1200.0' AS varchar(3))");
assertSimplifies("CAST(REAL '-12e2' AS varchar(3))", "CAST('-1200.0' AS varchar(3))");
assertSimplifies("CAST(REAL 'NaN' AS varchar(2))", "CAST('NaN' AS varchar(2))");
assertSimplifies("CAST(REAL 'Infinity' AS varchar(7))", "CAST('Infinity' AS varchar(7))");

// the cast operator returns a value that is too long for the expected type ('1200.0' for varchar(3))
// the value is nested in a comparison expression, so it is not truncated by the LiteralEncoder
assertSimplifies("CAST(REAL '12e2' AS varchar(3)) = '1200.0'", "true");
}

private static void assertSimplifies(String expression, String expected)
{
assertSimplifies(expression, expected, null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import static com.facebook.presto.common.type.DoubleType.DOUBLE;
import static com.facebook.presto.common.type.RealType.REAL;
import static com.facebook.presto.common.type.VarcharType.VARCHAR;
import static com.facebook.presto.common.type.VarcharType.createVarcharType;
import static com.facebook.presto.spi.StandardErrorCode.INVALID_CAST_ARGUMENT;
import static java.lang.Double.doubleToLongBits;
import static java.lang.Double.doubleToRawLongBits;
Expand Down Expand Up @@ -178,6 +179,17 @@ public void testCastToVarchar()
{
assertFunction("cast(37.7E0 as varchar)", VARCHAR, "37.7");
assertFunction("cast(17.1E0 as varchar)", VARCHAR, "17.1");
assertFunction("cast(12e2 as varchar(6))", createVarcharType(6), "1200.0");
assertFunction("cast(12e2 as varchar(50))", createVarcharType(50), "1200.0");
assertFunction("cast(12345678.9e0 as varchar(50))", createVarcharType(50), "1.23456789E7");
assertFunction("cast(DOUBLE 'NaN' as varchar(3))", createVarcharType(3), "NaN");
assertFunction("cast(DOUBLE 'Infinity' as varchar(50))", createVarcharType(50), "Infinity");
assertFunctionThrowsIncorrectly("cast(12e2 as varchar(5))", IllegalArgumentException.class, "Character count exceeds length limit 5.*");
assertFunctionThrowsIncorrectly("cast(12e2 as varchar(4))", IllegalArgumentException.class, "Character count exceeds length limit 4.*");
assertFunctionThrowsIncorrectly("cast(0e0 as varchar(2))", IllegalArgumentException.class, "Character count exceeds length limit 2.*");
assertFunctionThrowsIncorrectly("cast(-0e0 as varchar(3))", IllegalArgumentException.class, "Character count exceeds length limit 3.*");
assertFunctionThrowsIncorrectly("cast(0e0 / 0e0 as varchar(2))", IllegalArgumentException.class, "Character count exceeds length limit 2.*");
assertFunctionThrowsIncorrectly("cast(DOUBLE 'Infinity' as varchar(7))", IllegalArgumentException.class, "Character count exceeds length limit 7.*");
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import static com.facebook.presto.common.type.SmallintType.SMALLINT;
import static com.facebook.presto.common.type.TinyintType.TINYINT;
import static com.facebook.presto.common.type.VarcharType.VARCHAR;
import static com.facebook.presto.common.type.VarcharType.createVarcharType;
import static java.lang.Float.floatToIntBits;
import static java.lang.Float.intBitsToFloat;
import static java.lang.Float.isNaN;
Expand Down Expand Up @@ -174,6 +175,17 @@ public void testCastToVarchar()
assertFunction("CAST(REAL'-754.2008' as VARCHAR)", VARCHAR, "-754.2008");
assertFunction("CAST(REAL'Infinity' as VARCHAR)", VARCHAR, "Infinity");
assertFunction("CAST(REAL'0.0' / REAL'0.0' as VARCHAR)", VARCHAR, "NaN");
assertFunction("cast(REAL '12e2' as varchar(6))", createVarcharType(6), "1200.0");
assertFunction("cast(REAL '12e2' as varchar(50))", createVarcharType(50), "1200.0");
assertFunction("cast(REAL '12345678.9e0' as varchar(50))", createVarcharType(50), "1.2345679E7");
assertFunction("cast(REAL 'NaN' as varchar(3))", createVarcharType(3), "NaN");
assertFunction("cast(REAL 'Infinity' as varchar(50))", createVarcharType(50), "Infinity");
assertFunctionThrowsIncorrectly("cast(REAL '12e2' as varchar(5))", IllegalArgumentException.class, "Character count exceeds length limit 5.*");
assertFunctionThrowsIncorrectly("cast(REAL '12e2' as varchar(4))", IllegalArgumentException.class, "Character count exceeds length limit 4.*");
assertFunctionThrowsIncorrectly("cast(REAL '0e0' as varchar(2))", IllegalArgumentException.class, "Character count exceeds length limit 2.*");
assertFunctionThrowsIncorrectly("cast(REAL '-0e0' as varchar(3))", IllegalArgumentException.class, "Character count exceeds length limit 3.*");
assertFunctionThrowsIncorrectly("cast(REAL '0e0' / REAL '0e0' as varchar(2))", IllegalArgumentException.class, "Character count exceeds length limit 2.*");
assertFunctionThrowsIncorrectly("cast(REAL 'Infinity' as varchar(7))", IllegalArgumentException.class, "Character count exceeds length limit 7.*");
}

@Test
Expand Down

0 comments on commit 0ae645b

Please sign in to comment.