diff --git a/core/src/main/java/org/apache/calcite/sql/type/JavaToSqlTypeConversionRules.java b/core/src/main/java/org/apache/calcite/sql/type/JavaToSqlTypeConversionRules.java index f1941b29fef1..7b4b8a2a8779 100644 --- a/core/src/main/java/org/apache/calcite/sql/type/JavaToSqlTypeConversionRules.java +++ b/core/src/main/java/org/apache/calcite/sql/type/JavaToSqlTypeConversionRules.java @@ -17,6 +17,7 @@ package org.apache.calcite.sql.type; import org.apache.calcite.avatica.util.ArrayImpl; +import org.apache.calcite.avatica.util.ByteString; import com.google.common.collect.ImmutableMap; @@ -62,6 +63,7 @@ public class JavaToSqlTypeConversionRules { .put(boolean.class, SqlTypeName.BOOLEAN) .put(Boolean.class, SqlTypeName.BOOLEAN) .put(byte[].class, SqlTypeName.VARBINARY) + .put(ByteString.class, SqlTypeName.VARBINARY) .put(String.class, SqlTypeName.VARCHAR) .put(char[].class, SqlTypeName.VARCHAR) .put(Character.class, SqlTypeName.CHAR) diff --git a/core/src/test/java/org/apache/calcite/sql/type/SqlTypeUtilTest.java b/core/src/test/java/org/apache/calcite/sql/type/SqlTypeUtilTest.java index ac0bda1b87eb..37cd26aca6f9 100644 --- a/core/src/test/java/org/apache/calcite/sql/type/SqlTypeUtilTest.java +++ b/core/src/test/java/org/apache/calcite/sql/type/SqlTypeUtilTest.java @@ -16,6 +16,7 @@ */ package org.apache.calcite.sql.type; +import org.apache.calcite.avatica.util.ByteString; import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rel.type.RelDataTypeFactory; import org.apache.calcite.sql.SqlBasicTypeNameSpec; @@ -293,4 +294,10 @@ private static void assertCanCast(RelDataType toType, RelDataType fromType) { "Expected to be able to cast from %s to %s without coercion.", fromType, toType), SqlTypeUtil.canCastFrom(toType, fromType, /* coerce= */ defaultRules), is(true)); } + + @Test void testJavaToSqlByteStringMapping() { + SqlTypeName sqlTypeName = JavaToSqlTypeConversionRules.instance().lookup(ByteString.class); + assertThat("ByteString.class should map to SqlTypeName.VARBINARY", + sqlTypeName, is(SqlTypeName.VARBINARY)); + } } diff --git a/core/src/test/java/org/apache/calcite/test/UdfTest.java b/core/src/test/java/org/apache/calcite/test/UdfTest.java index 674b1e6c2aad..df70a1d185b1 100644 --- a/core/src/test/java/org/apache/calcite/test/UdfTest.java +++ b/core/src/test/java/org/apache/calcite/test/UdfTest.java @@ -182,6 +182,12 @@ private CalciteAssert.AssertThat withUdf() { + Smalls.AllTypesFunction.class.getName() + "',\n" + " methodName: '*'\n" + + " },\n" + + " {\n" + + " name: 'UNBASE64',\n" + + " className: '" + + Smalls.MyUnbase64Function.class.getName() + + "'\n" + " }\n" + " ]\n" + " }\n" @@ -1075,6 +1081,22 @@ private static CalciteAssert.AssertThat withBadUdf(Class clazz) { } } + /** Test case for + * [CALCITE-7073] + * Tests that the UNBASE64 user-defined function correctly decodes a Base64 string + * and its return type (VARBINARY, mapped from ByteString) is fully + * compatible for direct comparison with SQL VARBINARY literals (X'...') + * within queries. */ + @Test void testUnbase64DirectComparison() { + final String testHex = "74657374"; // "test" in bytes + final String testBase64 = "dGVzdA=="; // Base64 for "test" + + final String sql = "select \"adhoc\".unbase64(cast('" + testBase64 + "' as varchar))" + + " = x'" + testHex + "' as C\n"; + + withUdf().query(sql).returns("C=true\n"); + } + /** * Base class for functions that append arrays. */ diff --git a/testkit/src/main/java/org/apache/calcite/util/Smalls.java b/testkit/src/main/java/org/apache/calcite/util/Smalls.java index 963b11590f66..3ffd3efda98d 100644 --- a/testkit/src/main/java/org/apache/calcite/util/Smalls.java +++ b/testkit/src/main/java/org/apache/calcite/util/Smalls.java @@ -19,6 +19,7 @@ import org.apache.calcite.DataContext; import org.apache.calcite.adapter.enumerable.EnumerableTableScan; import org.apache.calcite.adapter.java.AbstractQueryableTable; +import org.apache.calcite.avatica.util.ByteString; import org.apache.calcite.config.CalciteConnectionConfig; import org.apache.calcite.linq4j.AbstractEnumerable; import org.apache.calcite.linq4j.BaseQueryable; @@ -1477,4 +1478,18 @@ private Object[] convertRow(Object[] full) { BuiltInMethod.QUERYABLE_AS_ENUMERABLE.method); } } + + /** User-defined function that decodes a Base64 string to bytes. */ + public static class MyUnbase64Function { + public static ByteString eval(String s) { + if (s == null) { + return null; + } + try { + return ByteString.ofBase64(s); + } catch (Exception e) { + return null; + } + } + } }