This is an automated email from the ASF dual-hosted git repository.
mbudiu pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite.git
The following commit(s) were added to refs/heads/main by this push:
new a1caa65297 [CALCITE-5094] Calcite JDBC Adapter and Avatica should
support MySQL UNSIGNED types of TINYINT, SMALLINT, INT, BIGINT
a1caa65297 is described below
commit a1caa65297b0ba8aa150ef96265afbb8da87e207
Author: Zhengqiang Duan <[email protected]>
AuthorDate: Tue Sep 30 22:00:11 2025 +0800
[CALCITE-5094] Calcite JDBC Adapter and Avatica should support MySQL
UNSIGNED types of TINYINT, SMALLINT, INT, BIGINT
---
.../apache/calcite/adapter/jdbc/JdbcSchema.java | 4 +-
.../apache/calcite/prepare/CalcitePrepareImpl.java | 2 +-
.../org/apache/calcite/sql/type/SqlTypeName.java | 22 ++++++++++
.../apache/calcite/sql/test/SqlTypeNameTest.java | 28 +++++++++++++
.../java/org/apache/calcite/test/JdbcTest.java | 33 +++++++++++++++
.../apache/calcite/linq4j/tree/UnsignedType.java | 2 +-
.../org/apache/calcite/test/CalciteAssert.java | 49 +++++++++++++++++++++-
7 files changed, 136 insertions(+), 4 deletions(-)
diff --git a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcSchema.java
b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcSchema.java
index dad1294075..eb1f11b74d 100644
--- a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcSchema.java
+++ b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcSchema.java
@@ -423,7 +423,9 @@ private static RelDataType sqlType(RelDataTypeFactory
typeFactory, int dataType,
int precision, int scale, @Nullable String typeString) {
// Fall back to ANY if type is unknown
final SqlTypeName sqlTypeName =
- Util.first(SqlTypeName.getNameForJdbcType(dataType), SqlTypeName.ANY);
+ Util.first(typeString != null &&
typeString.toUpperCase(Locale.ROOT).contains("UNSIGNED")
+ ? SqlTypeName.getNameForUnsignedJdbcType(dataType)
+ : SqlTypeName.getNameForJdbcType(dataType), SqlTypeName.ANY);
switch (sqlTypeName) {
case ARRAY:
RelDataType component = null;
diff --git
a/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
b/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
index 1f036986f4..6bb21e1ae2 100644
--- a/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
+++ b/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
@@ -793,7 +793,7 @@ private static ColumnMetaData metaData(JavaTypeFactory
typeFactory, int ordinal,
type.isNullable()
? DatabaseMetaData.columnNullable
: DatabaseMetaData.columnNoNulls,
- true,
+ !SqlTypeName.UNSIGNED_TYPES.contains(type.getSqlTypeName()),
type.getPrecision(),
fieldName,
origin(origins, 0),
diff --git a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java
b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java
index 79eeaa95bd..73557737ca 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/SqlTypeName.java
@@ -289,6 +289,18 @@ public enum SqlTypeName {
.put(Types.ARRAY, ARRAY)
.build();
+ /**
+ * Mapping between JDBC type codes and their corresponding
+ * {@link org.apache.calcite.sql.type.SqlTypeName} for unsigned types.
+ */
+ private static final Map<Integer, SqlTypeName> UNSIGNED_JDBC_TYPE_TO_NAME =
+ ImmutableMap.<Integer, SqlTypeName>builder()
+ .put(Types.TINYINT, UTINYINT)
+ .put(Types.SMALLINT, USMALLINT)
+ .put(Types.INTEGER, UINTEGER)
+ .put(Types.BIGINT, UBIGINT)
+ .build();
+
/**
* Bitwise-or of flags indicating allowable precision/scale combinations.
*/
@@ -447,6 +459,16 @@ public int getDefaultScale() {
return JDBC_TYPE_TO_NAME.get(jdbcType);
}
+ /**
+ * Gets the SqlTypeName corresponding to an unsigned JDBC type.
+ *
+ * @param jdbcType the unsigned JDBC type of interest
+ * @return corresponding SqlTypeName, or null if the type is not known
+ */
+ public static @Nullable SqlTypeName getNameForUnsignedJdbcType(int jdbcType)
{
+ return UNSIGNED_JDBC_TYPE_TO_NAME.get(jdbcType);
+ }
+
/**
* Returns the limit of this datatype. For example,
*
diff --git
a/core/src/test/java/org/apache/calcite/sql/test/SqlTypeNameTest.java
b/core/src/test/java/org/apache/calcite/sql/test/SqlTypeNameTest.java
index 4281179b0d..457c627278 100644
--- a/core/src/test/java/org/apache/calcite/sql/test/SqlTypeNameTest.java
+++ b/core/src/test/java/org/apache/calcite/sql/test/SqlTypeNameTest.java
@@ -40,6 +40,10 @@
import static org.apache.calcite.sql.type.SqlTypeName.TIME;
import static org.apache.calcite.sql.type.SqlTypeName.TIMESTAMP;
import static org.apache.calcite.sql.type.SqlTypeName.TINYINT;
+import static org.apache.calcite.sql.type.SqlTypeName.UBIGINT;
+import static org.apache.calcite.sql.type.SqlTypeName.UINTEGER;
+import static org.apache.calcite.sql.type.SqlTypeName.USMALLINT;
+import static org.apache.calcite.sql.type.SqlTypeName.UTINYINT;
import static org.apache.calcite.sql.type.SqlTypeName.VARBINARY;
import static org.apache.calcite.sql.type.SqlTypeName.VARCHAR;
@@ -81,6 +85,30 @@ class SqlTypeNameTest {
assertThat("BIGINT did not map to BIGINT", tn, is(BIGINT));
}
+ @Test void testUnsignedTinyint() {
+ SqlTypeName tn =
+ SqlTypeName.getNameForUnsignedJdbcType(Types.TINYINT);
+ assertThat("TINYINT did not map to UTINYINT", tn, is(UTINYINT));
+ }
+
+ @Test void testUnsignedSmallint() {
+ SqlTypeName tn =
+ SqlTypeName.getNameForUnsignedJdbcType(Types.SMALLINT);
+ assertThat("SMALLINT did not map to USMALLINT", tn, is(USMALLINT));
+ }
+
+ @Test void testUnsignedInteger() {
+ SqlTypeName tn =
+ SqlTypeName.getNameForUnsignedJdbcType(Types.INTEGER);
+ assertThat("INTEGER did not map to UINTEGER", tn, is(UINTEGER));
+ }
+
+ @Test void testUnsignedBigint() {
+ SqlTypeName tn =
+ SqlTypeName.getNameForUnsignedJdbcType(Types.BIGINT);
+ assertThat("BIGINT did not map to UBIGINT", tn, is(UBIGINT));
+ }
+
@Test void testFloat() {
SqlTypeName tn =
SqlTypeName.getNameForJdbcType(Types.FLOAT);
diff --git a/core/src/test/java/org/apache/calcite/test/JdbcTest.java
b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
index 64fb5b0caf..de66b636f5 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
@@ -9077,6 +9077,39 @@ void checkCalciteSchemaGetSubSchemaMap(boolean cache) {
});
}
+ /** Test case for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-5094">[CALCITE-5094]
+ * Calcite JDBC Adapter and Avatica should support
+ * MySQL UNSIGNED types of TINYINT, SMALLINT, INT, BIGINT</a>. */
+ @Test void testMySQLUnsignedType() {
+ CalciteAssert.that()
+ .with(CalciteAssert.SchemaSpec.UNSIGNED_TYPE)
+ .with(Lex.MYSQL)
+ .query("SELECT * FROM test_unsigned WHERE utiny_value = ? "
+ + "AND usmall_value = ? AND uint_value = ? AND ubig_value = ?")
+ .consumesPreparedStatement(p -> {
+ p.setInt(1, 255);
+ p.setInt(2, 65535);
+ p.setLong(3, 4294967295L);
+ p.setBigDecimal(4, new BigDecimal("18446744073709551615"));
+ })
+ .returns(resultSet -> {
+ try {
+ assertTrue(resultSet.next());
+ final Integer uTinyInt = resultSet.getInt(1);
+ final Integer uSmallInt = resultSet.getInt(2);
+ final Long uInteger = resultSet.getLong(3);
+ final BigDecimal uBigInt = resultSet.getBigDecimal(4);
+ assertThat(uTinyInt, is(255));
+ assertThat(uSmallInt, is(65535));
+ assertThat(uInteger, is(4294967295L));
+ assertThat(uBigInt, is(new BigDecimal("18446744073709551615")));
+ } catch (SQLException e) {
+ throw TestUtil.rethrow(e);
+ }
+ });
+ }
+
@Test void bindByteParameter() {
for (SqlTypeName tpe : SqlTypeName.INT_TYPES) {
final String sql =
diff --git
a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/UnsignedType.java
b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/UnsignedType.java
index b1cbbcc2e6..32622315f7 100644
--- a/linq4j/src/main/java/org/apache/calcite/linq4j/tree/UnsignedType.java
+++ b/linq4j/src/main/java/org/apache/calcite/linq4j/tree/UnsignedType.java
@@ -278,7 +278,7 @@ public static ULong toULong(double n) {
private static ULong toULongNonNull(Number n, RoundingMode mode) {
if (n instanceof BigDecimal) {
BigDecimal d = ((BigDecimal) n).setScale(0, mode);
- return Unsigned.ulong(d.longValueExact());
+ return Unsigned.ulong(d.toBigIntegerExact());
} else if (n instanceof Byte || n instanceof Short || n instanceof Integer
|| n instanceof Long || n instanceof Float || n instanceof Double
|| n instanceof UByte || n instanceof UShort || n instanceof UInteger)
{
diff --git a/testkit/src/main/java/org/apache/calcite/test/CalciteAssert.java
b/testkit/src/main/java/org/apache/calcite/test/CalciteAssert.java
index a01501ce23..8a846da87e 100644
--- a/testkit/src/main/java/org/apache/calcite/test/CalciteAssert.java
+++ b/testkit/src/main/java/org/apache/calcite/test/CalciteAssert.java
@@ -30,6 +30,8 @@
import org.apache.calcite.jdbc.CalciteMetaImpl;
import org.apache.calcite.jdbc.CalcitePrepare;
import org.apache.calcite.jdbc.CalciteSchema;
+import org.apache.calcite.linq4j.Enumerable;
+import org.apache.calcite.linq4j.Linq4j;
import org.apache.calcite.linq4j.tree.Expression;
import org.apache.calcite.materialize.Lattice;
import org.apache.calcite.model.ModelHandler;
@@ -47,6 +49,7 @@
import org.apache.calcite.runtime.Hook;
import org.apache.calcite.runtime.SpatialTypeFunctions;
import org.apache.calcite.runtime.UnionOperation;
+import org.apache.calcite.schema.ScannableTable;
import org.apache.calcite.schema.Schema;
import org.apache.calcite.schema.SchemaPlus;
import org.apache.calcite.schema.SchemaVersion;
@@ -90,6 +93,7 @@
import org.apache.calcite.util.Util;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultiset;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
@@ -105,6 +109,7 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
+import java.math.BigDecimal;
import java.net.URL;
import java.sql.Connection;
import java.sql.PreparedStatement;
@@ -865,7 +870,8 @@ static SchemaPlus addSchema_(SchemaPlus rootSchema,
SchemaSpec schema) {
case MY_DB:
return rootSchema.add(schema.schemaName, MY_DB_SCHEMA);
-
+ case UNSIGNED_TYPE:
+ return rootSchema.add(schema.schemaName, UNSIGNED_TYPE_SCHEMA);
case SCOTT:
jdbcScott = addSchemaIfNotExists(rootSchema, SchemaSpec.JDBC_SCOTT);
return rootSchema.add(schema.schemaName, new CloneSchema(jdbcScott));
@@ -2077,6 +2083,7 @@ public enum SchemaSpec {
GEO("GEO"),
HR("hr"),
MY_DB("myDb"),
+ UNSIGNED_TYPE("UNSIGNED_TYPE"),
JDBC_SCOTT("JDBC_SCOTT"),
SCOTT("scott"),
SCOTT_WITH_TEMPORAL("scott_temporal"),
@@ -2355,4 +2362,44 @@ static List<String> unwrap(String java) {
throw new UnsupportedOperationException("snapshot");
}
};
+
+ /** Schema instance for {@link SchemaSpec#UNSIGNED_TYPE}. */
+ private static final Schema UNSIGNED_TYPE_SCHEMA = new AbstractSchema() {
+
+ @Override protected Map<String, Table> getTableMap() {
+ return ImmutableMap.of("test_unsigned", new UnsingedScannableTable());
+ }
+ };
+
+ /** Scannable table for unsigned types test. */
+ private static class UnsingedScannableTable extends AbstractTable implements
ScannableTable {
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>Table schema is as follows:
+ *
+ * <pre>{@code
+ * test_unsigned(
+ * utiny_value: TINYINT UNSIGNED,
+ * usmall_value: SMALLINT UNSIGNED,
+ * uint_value: INT UNSIGNED,
+ * ubig_value: BIGINT UNSIGNED)
+ * }</pre>
+ */
+ @Override public RelDataType getRowType(RelDataTypeFactory typeFactory) {
+ return typeFactory.builder()
+ .add("utiny_value", typeFactory.createSqlType(SqlTypeName.UTINYINT))
+ .add("usmall_value",
typeFactory.createSqlType(SqlTypeName.USMALLINT))
+ .add("uint_value", typeFactory.createSqlType(SqlTypeName.UINTEGER))
+ .add("ubig_value", typeFactory.createSqlType(SqlTypeName.UBIGINT))
+ .build();
+ }
+
+ @Override public Enumerable<@Nullable Object[]> scan(DataContext root) {
+ return Linq4j.asEnumerable(new Object[][] {
+ {255, 65535, 4294967295L, new BigDecimal("18446744073709551615")}
+ });
+ }
+ }
}