diff --git a/bom/build.gradle.kts b/bom/build.gradle.kts index da4b7326c0..e5ebd2bf5c 100644 --- a/bom/build.gradle.kts +++ b/bom/build.gradle.kts @@ -54,6 +54,7 @@ dependencies { apiv("com.google.protobuf:protobuf-java", "protobuf") apiv("com.h2database:h2") apiv("javax.servlet:javax.servlet-api", "servlet") + apiv("org.jooq:joou-java-6", "joou-java-6") apiv("junit:junit") apiv("net.bytebuddy:byte-buddy", "bytebuddy") apiv("net.bytebuddy:byte-buddy-agent", "bytebuddy") diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 0374aaaf66..0868348b90 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -34,6 +34,7 @@ dependencies { api("com.fasterxml.jackson.core:jackson-annotations") api("com.fasterxml.jackson.core:jackson-databind") api("com.google.protobuf:protobuf-java") + api("org.jooq:joou-java-6") implementation("com.fasterxml.jackson.core:jackson-core") implementation("org.apache.httpcomponents.client5:httpclient5") implementation("org.apache.httpcomponents.core5:httpcore5") diff --git a/core/src/main/java/org/apache/calcite/avatica/AvaticaResultSet.java b/core/src/main/java/org/apache/calcite/avatica/AvaticaResultSet.java index 33ed5be0bb..80e9431135 100644 --- a/core/src/main/java/org/apache/calcite/avatica/AvaticaResultSet.java +++ b/core/src/main/java/org/apache/calcite/avatica/AvaticaResultSet.java @@ -392,7 +392,7 @@ public ResultSetMetaData getMetaData() throws SQLException { public Object getObject(int columnIndex) throws SQLException { final Cursor.Accessor accessor = getAccessor(columnIndex); final ColumnMetaData metaData = columnMetaDataList.get(columnIndex - 1); - return AvaticaSite.get(accessor, metaData.type.id, localCalendar); + return AvaticaSite.get(accessor, metaData.type.id, metaData.signed, localCalendar); } public Object getObject(String columnLabel) throws SQLException { diff --git a/core/src/main/java/org/apache/calcite/avatica/AvaticaSite.java b/core/src/main/java/org/apache/calcite/avatica/AvaticaSite.java index e70aa87c0a..18e12f0824 100644 --- a/core/src/main/java/org/apache/calcite/avatica/AvaticaSite.java +++ b/core/src/main/java/org/apache/calcite/avatica/AvaticaSite.java @@ -291,7 +291,7 @@ public void setObject(Object x, int targetSqlType) { /** Similar logic to {@link #setObject}. */ public static Object get(Cursor.Accessor accessor, int targetSqlType, - Calendar localCalendar) throws SQLException { + boolean signed, Calendar localCalendar) throws SQLException { switch (targetSqlType) { case Types.CLOB: case Types.DATALINK: @@ -304,6 +304,9 @@ public static Object get(Cursor.Accessor accessor, int targetSqlType, case Types.ARRAY: return accessor.getArray(); case Types.BIGINT: + if (!signed) { + return accessor.wasNull() ? null : accessor.getULong(); + } final long aLong = accessor.getLong(); if (aLong == 0 && accessor.wasNull()) { return null; @@ -337,6 +340,9 @@ public static Object get(Cursor.Accessor accessor, int targetSqlType, } return aDouble; case Types.INTEGER: + if (!signed) { + return accessor.wasNull() ? null : accessor.getUInt(); + } final int anInt = accessor.getInt(); if (anInt == 0 && accessor.wasNull()) { return null; @@ -362,6 +368,9 @@ public static Object get(Cursor.Accessor accessor, int targetSqlType, case Types.ROWID: throw notImplemented(); case Types.SMALLINT: + if (!signed) { + return accessor.wasNull() ? null : accessor.getUShort(); + } final short aShort = accessor.getShort(); if (aShort == 0 && accessor.wasNull()) { return null; @@ -372,6 +381,9 @@ public static Object get(Cursor.Accessor accessor, int targetSqlType, case Types.TIMESTAMP: return accessor.getTimestamp(localCalendar); case Types.TINYINT: + if (!signed) { + return accessor.wasNull() ? null : accessor.getUByte(); + } final byte aByte = accessor.getByte(); if (aByte == 0 && accessor.wasNull()) { return null; diff --git a/core/src/main/java/org/apache/calcite/avatica/ColumnMetaData.java b/core/src/main/java/org/apache/calcite/avatica/ColumnMetaData.java index 2c5d55b71c..5399bcdd04 100644 --- a/core/src/main/java/org/apache/calcite/avatica/ColumnMetaData.java +++ b/core/src/main/java/org/apache/calcite/avatica/ColumnMetaData.java @@ -25,6 +25,11 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.google.protobuf.Descriptors.FieldDescriptor; +import org.joou.UByte; +import org.joou.UInteger; +import org.joou.ULong; +import org.joou.UShort; + import java.lang.reflect.Type; import java.math.BigDecimal; import java.sql.Array; @@ -327,10 +332,14 @@ public enum Rep { PRIMITIVE_DOUBLE(double.class, Types.DOUBLE), BOOLEAN(Boolean.class, Types.BOOLEAN), BYTE(Byte.class, Types.TINYINT), + UBYTE(UByte.class, Types.TINYINT), CHARACTER(Character.class, Types.CHAR), SHORT(Short.class, Types.SMALLINT), + USHORT(UShort.class, Types.SMALLINT), INTEGER(Integer.class, Types.INTEGER), + UINTEGER(UInteger.class, Types.INTEGER), LONG(Long.class, Types.BIGINT), + ULONG(ULong.class, Types.BIGINT), FLOAT(Float.class, Types.FLOAT), DOUBLE(Double.class, Types.DOUBLE), JAVA_SQL_TIME(Time.class, Types.TIME), diff --git a/core/src/main/java/org/apache/calcite/avatica/MetaImpl.java b/core/src/main/java/org/apache/calcite/avatica/MetaImpl.java index 4eb6a2ebe6..4822082329 100644 --- a/core/src/main/java/org/apache/calcite/avatica/MetaImpl.java +++ b/core/src/main/java/org/apache/calcite/avatica/MetaImpl.java @@ -221,7 +221,12 @@ public static ColumnMetaData columnMetaData(String name, int index, public static ColumnMetaData columnMetaData(String name, int index, AvaticaType type, boolean columnNullable) { - return columnMetaData(name, index, type, intForColumnNullable(columnNullable)); + return columnMetaData(name, index, type, intForColumnNullable(columnNullable), true); + } + + public static ColumnMetaData columnMetaData(String name, int index, AvaticaType type, + boolean columnNullable, boolean signed) { + return columnMetaData(name, index, type, intForColumnNullable(columnNullable), signed); } public static ColumnMetaData columnMetaData(String name, int index, @@ -231,15 +236,20 @@ public static ColumnMetaData columnMetaData(String name, int index, ColumnMetaData.Rep.VALUE_MAP.get(type); ColumnMetaData.AvaticaType scalarType = ColumnMetaData.scalar(pair.sqlType, pair.sqlTypeName, rep); - return columnMetaData(name, index, scalarType, columnNullable); + return columnMetaData(name, index, scalarType, columnNullable, true); } public static ColumnMetaData columnMetaData(String name, int index, AvaticaType type, int columnNullable) { + return columnMetaData(name, index, type, columnNullable, true); + } + + public static ColumnMetaData columnMetaData(String name, int index, AvaticaType type, + int columnNullable, boolean signed) { return new ColumnMetaData( index, false, true, false, false, columnNullable, - true, -1, name, name, null, + signed, -1, name, name, null, 0, 0, null, null, type, true, false, false, type.columnClassName()); } diff --git a/core/src/main/java/org/apache/calcite/avatica/util/AbstractCursor.java b/core/src/main/java/org/apache/calcite/avatica/util/AbstractCursor.java index 2c5653ae09..e63ac71722 100644 --- a/core/src/main/java/org/apache/calcite/avatica/util/AbstractCursor.java +++ b/core/src/main/java/org/apache/calcite/avatica/util/AbstractCursor.java @@ -20,6 +20,11 @@ import org.apache.calcite.avatica.AvaticaUtils; import org.apache.calcite.avatica.ColumnMetaData; +import org.joou.UByte; +import org.joou.UInteger; +import org.joou.ULong; +import org.joou.UShort; + import java.io.InputStream; import java.io.Reader; import java.lang.reflect.Field; @@ -102,13 +107,13 @@ protected Accessor createAccessor(ColumnMetaData columnMetaData, } switch (columnMetaData.type.id) { case Types.TINYINT: - return new ByteAccessor(getter); + return columnMetaData.signed ? new ByteAccessor(getter) : new UByteAccessor(getter); case Types.SMALLINT: - return new ShortAccessor(getter); + return columnMetaData.signed ? new ShortAccessor(getter) : new UShortAccessor(getter); case Types.INTEGER: - return new IntAccessor(getter); + return columnMetaData.signed ? new IntAccessor(getter) : new UIntAccessor(getter); case Types.BIGINT: - return new LongAccessor(getter); + return columnMetaData.signed ? new LongAccessor(getter) : new ULongAccessor(getter); case Types.BOOLEAN: case Types.BIT: return new BooleanAccessor(getter); @@ -288,18 +293,38 @@ public byte getByte() throws SQLException { return (byte) getLong(); } + @Override + public UByte getUByte() throws SQLException { + return UByte.valueOf(getLong()); + } + public short getShort() throws SQLException { return (short) getLong(); } + @Override + public UShort getUShort() throws SQLException { + return UShort.valueOf(String.valueOf(getLong())); + } + public int getInt() throws SQLException { return (int) getLong(); } + @Override + public UInteger getUInt() throws SQLException { + return UInteger.valueOf(getLong()); + } + public long getLong() throws SQLException { throw cannotConvert("long"); } + @Override + public ULong getULong() throws SQLException { + return ULong.valueOf(getBigDecimal().toBigInteger()); + } + public float getFloat() throws SQLException { return (float) getDouble(); } @@ -485,6 +510,30 @@ public long getLong() throws SQLException { } } + /** + * Accessor that assumes that the underlying value is a {@link UByte}; + * corresponds to {@link java.sql.Types#TINYINT} with unsigned flag. + */ + private static class UByteAccessor extends ExactNumericAccessor { + private UByteAccessor(Getter getter) { + super(getter); + } + + public UByte getUByte() throws SQLException { + Object obj = getObject(); + if (null == obj) { + return UByte.valueOf(0); + } else if (obj instanceof Integer) { + return UByte.valueOf(((Integer) obj).byteValue()); + } + return UByte.valueOf(String.valueOf(obj)); + } + + public long getLong() throws SQLException { + return getUByte().longValue(); + } + } + /** * Accessor that assumes that the underlying value is a {@link Short}; * corresponds to {@link java.sql.Types#SMALLINT}. @@ -509,6 +558,30 @@ public long getLong() throws SQLException { } } + /** + * Accessor that assumes that the underlying value is a {@link UShort}; + * corresponds to {@link java.sql.Types#SMALLINT} with unsigned flag. + */ + private static class UShortAccessor extends ExactNumericAccessor { + private UShortAccessor(Getter getter) { + super(getter); + } + + public UShort getUShort() throws SQLException { + Object obj = getObject(); + if (null == obj) { + return UShort.valueOf(0); + } else if (obj instanceof Integer) { + return UShort.valueOf(((Integer) obj).shortValue()); + } + return UShort.valueOf(String.valueOf(obj)); + } + + public long getLong() throws SQLException { + return getUShort().longValue(); + } + } + /** * Accessor that assumes that the underlying value is an {@link Integer}; * corresponds to {@link java.sql.Types#INTEGER}. @@ -528,6 +601,25 @@ public long getLong() throws SQLException { } } + /** + * Accessor that assumes that the underlying value is an {@link UInteger}; + * corresponds to {@link java.sql.Types#INTEGER} with unsigned flag. + */ + private static class UIntAccessor extends ExactNumericAccessor { + private UIntAccessor(Getter getter) { + super(getter); + } + + public UInteger getUInt() throws SQLException { + Object o = getObject(); + return UInteger.valueOf(o == null ? "0" : String.valueOf(o)); + } + + public long getLong() throws SQLException { + return getUInt().longValue(); + } + } + /** * Accessor that assumes that the underlying value is a {@link Long}; * corresponds to {@link java.sql.Types#BIGINT}. @@ -543,6 +635,26 @@ public long getLong() throws SQLException { } } + /** + * Accessor that assumes that the underlying value is a {@link ULong}; + * corresponds to {@link java.sql.Types#BIGINT} with unsigned flag. + */ + private static class ULongAccessor extends ExactNumericAccessor { + private ULongAccessor(Getter getter) { + super(getter); + } + + public ULong getULong() throws SQLException { + Object o = getObject(); + return ULong.valueOf(o == null ? "0" : String.valueOf(o)); + } + + @Override + public long getLong() throws SQLException { + return getULong().longValue(); + } + } + /** * Accessor of values that are {@link Double} or null. */ diff --git a/core/src/main/java/org/apache/calcite/avatica/util/Cursor.java b/core/src/main/java/org/apache/calcite/avatica/util/Cursor.java index 9eeb2df7c9..8d9be8dad8 100644 --- a/core/src/main/java/org/apache/calcite/avatica/util/Cursor.java +++ b/core/src/main/java/org/apache/calcite/avatica/util/Cursor.java @@ -18,6 +18,11 @@ import org.apache.calcite.avatica.ColumnMetaData; +import org.joou.UByte; +import org.joou.UInteger; +import org.joou.ULong; +import org.joou.UShort; + import java.io.InputStream; import java.io.Reader; import java.math.BigDecimal; @@ -87,12 +92,20 @@ interface Accessor { byte getByte() throws SQLException; + UByte getUByte() throws SQLException; + short getShort() throws SQLException; + UShort getUShort() throws SQLException; + int getInt() throws SQLException; + UInteger getUInt() throws SQLException; + long getLong() throws SQLException; + ULong getULong() throws SQLException; + float getFloat() throws SQLException; double getDouble() throws SQLException; diff --git a/core/src/test/java/org/apache/calcite/avatica/AvaticaResultSetConversionsTest.java b/core/src/test/java/org/apache/calcite/avatica/AvaticaResultSetConversionsTest.java index 065ebf43b4..d427a2fd88 100644 --- a/core/src/test/java/org/apache/calcite/avatica/AvaticaResultSetConversionsTest.java +++ b/core/src/test/java/org/apache/calcite/avatica/AvaticaResultSetConversionsTest.java @@ -33,6 +33,7 @@ import java.io.InputStream; import java.io.Reader; import java.math.BigDecimal; +import java.math.BigInteger; import java.net.URL; import java.sql.Array; import java.sql.Blob; @@ -147,46 +148,70 @@ public TestMetaImpl(AvaticaConnection connection) { ColumnMetaData.scalar(Types.SMALLINT, "SMALLINT", ColumnMetaData.Rep.PRIMITIVE_SHORT), DatabaseMetaData.columnNoNulls), - columnMetaData("int", 3, + columnMetaData("medium", 3, + ColumnMetaData.scalar(Types.INTEGER, "MEDIUMINT", + ColumnMetaData.Rep.PRIMITIVE_INT), + DatabaseMetaData.columnNoNulls), + columnMetaData("int", 4, ColumnMetaData.scalar(Types.INTEGER, "INTEGER", ColumnMetaData.Rep.PRIMITIVE_INT), DatabaseMetaData.columnNoNulls), - columnMetaData("long", 4, + columnMetaData("long", 5, ColumnMetaData.scalar(Types.BIGINT, "BIGINT", ColumnMetaData.Rep.PRIMITIVE_LONG), DatabaseMetaData.columnNoNulls), - columnMetaData("float", 5, + columnMetaData("byte_unsigned", 6, + ColumnMetaData.scalar(Types.TINYINT, "TINYINT_UNSIGNED", + ColumnMetaData.Rep.PRIMITIVE_SHORT), + DatabaseMetaData.columnNoNulls, false), + columnMetaData("short_unsigned", 7, + ColumnMetaData.scalar(Types.SMALLINT, "SMALLINT_UNSIGNED", + ColumnMetaData.Rep.PRIMITIVE_INT), + DatabaseMetaData.columnNoNulls, false), + columnMetaData("medium_unsigned", 8, + ColumnMetaData.scalar(Types.INTEGER, "MEDIUMINT_UNSIGNED", + ColumnMetaData.Rep.PRIMITIVE_INT), + DatabaseMetaData.columnNoNulls, false), + columnMetaData("int_unsigned", 9, + ColumnMetaData.scalar(Types.INTEGER, "INTEGER_UNSIGNED", + ColumnMetaData.Rep.PRIMITIVE_LONG), + DatabaseMetaData.columnNoNulls, false), + columnMetaData("long_unsigned", 10, + ColumnMetaData.scalar(Types.BIGINT, "BIGINT_UNSIGNED", + ColumnMetaData.Rep.NUMBER), + DatabaseMetaData.columnNoNulls, false), + columnMetaData("float", 11, ColumnMetaData.scalar(Types.REAL, "REAL", ColumnMetaData.Rep.FLOAT), DatabaseMetaData.columnNoNulls), - columnMetaData("double", 6, + columnMetaData("double", 12, ColumnMetaData.scalar(Types.FLOAT, "FLOAT", ColumnMetaData.Rep.DOUBLE), DatabaseMetaData.columnNoNulls), - columnMetaData("string", 7, + columnMetaData("string", 13, ColumnMetaData.scalar(Types.VARCHAR, "VARCHAR", ColumnMetaData.Rep.STRING), DatabaseMetaData.columnNoNulls), - columnMetaData("date", 8, + columnMetaData("date", 14, ColumnMetaData.scalar(Types.DATE, "DATE", ColumnMetaData.Rep.JAVA_SQL_DATE), DatabaseMetaData.columnNoNulls), - columnMetaData("time", 9, + columnMetaData("time", 15, ColumnMetaData.scalar(Types.TIME, "TIME", ColumnMetaData.Rep.JAVA_SQL_TIME), DatabaseMetaData.columnNoNulls), - columnMetaData("timestamp", 10, + columnMetaData("timestamp", 16, ColumnMetaData.scalar(Types.TIMESTAMP, "TIMESTAMP", ColumnMetaData.Rep.JAVA_SQL_TIMESTAMP), DatabaseMetaData.columnNoNulls), - columnMetaData("array", 11, + columnMetaData("array", 17, ColumnMetaData.array( ColumnMetaData.scalar(Types.INTEGER, "INTEGER", ColumnMetaData.Rep.PRIMITIVE_INT), "ARRAY", ColumnMetaData.Rep.ARRAY), DatabaseMetaData.columnNoNulls), - columnMetaData("struct", 12, + columnMetaData("struct", 18, ColumnMetaData.struct( Arrays.asList( columnMetaData("int", 0, @@ -198,36 +223,36 @@ public TestMetaImpl(AvaticaConnection connection) { ColumnMetaData.Rep.PRIMITIVE_BOOLEAN), DatabaseMetaData.columnNoNulls))), DatabaseMetaData.columnNoNulls), - columnMetaData("bit", 13, + columnMetaData("bit", 19, ColumnMetaData.scalar(Types.BIT, "BIT", ColumnMetaData.Rep.PRIMITIVE_BOOLEAN), DatabaseMetaData.columnNoNulls), - columnMetaData("null", 14, + columnMetaData("null", 20, ColumnMetaData.scalar(Types.NULL, "NULL", ColumnMetaData.Rep.OBJECT), DatabaseMetaData.columnNullable), - columnMetaData("date_array", 15, + columnMetaData("date_array", 21, ColumnMetaData.array( ColumnMetaData.scalar(Types.DATE, "DATE", ColumnMetaData.Rep.PRIMITIVE_INT), "ARRAY", ColumnMetaData.Rep.ARRAY), DatabaseMetaData.columnNoNulls), - columnMetaData("timestamp_array", 16, + columnMetaData("timestamp_array", 22, ColumnMetaData.array( ColumnMetaData.scalar(Types.TIMESTAMP, "TIMESTAMP", ColumnMetaData.Rep.PRIMITIVE_LONG), "ARRAY", ColumnMetaData.Rep.ARRAY), DatabaseMetaData.columnNoNulls), - columnMetaData("time_array", 17, + columnMetaData("time_array", 23, ColumnMetaData.array( ColumnMetaData.scalar(Types.TIME, "TIME", ColumnMetaData.Rep.NUMBER), "ARRAY", ColumnMetaData.Rep.ARRAY), DatabaseMetaData.columnNoNulls), - columnMetaData("decimal_array", 18, + columnMetaData("decimal_array", 24, ColumnMetaData.array( ColumnMetaData.scalar(Types.DECIMAL, "DECIMAL", ColumnMetaData.Rep.PRIMITIVE_DOUBLE), @@ -237,7 +262,8 @@ public TestMetaImpl(AvaticaConnection connection) { List row = Collections.singletonList( new Object[] { - true, (byte) 1, (short) 2, 3, 4L, 5.0f, 6.0d, "testvalue", + true, (byte) 1, (short) 2, 3, 3, 4L, (short) 2, 3, 4L, 4L, + new BigInteger("7"), 5.0f, 6.0d, "testvalue", new Date(DST_INSTANT), new Time(DST_INSTANT), new Timestamp(DST_INSTANT), Arrays.asList(1, 2, 3), @@ -910,6 +936,51 @@ private LongAccessorTestHelper(Getter g) { } } + /** + * Accessor test helper for the long unsigned column. + */ + private static final class LongUnsingedAccessorTestHelper extends AccessorTestHelper { + private LongUnsingedAccessorTestHelper(Getter g) { + super(g); + } + + @Override public void testGetString(ResultSet resultSet) throws SQLException { + assertEquals("7", g.getString(resultSet)); + } + + @Override public void testGetBoolean(ResultSet resultSet) throws SQLException { + assertEquals(true, g.getBoolean(resultSet)); + } + + @Override public void testGetByte(ResultSet resultSet) throws SQLException { + assertEquals((byte) 7, g.getByte(resultSet)); + } + + @Override public void testGetShort(ResultSet resultSet) throws SQLException { + assertEquals((short) 7, g.getShort(resultSet)); + } + + @Override public void testGetInt(ResultSet resultSet) throws SQLException { + assertEquals(7, g.getInt(resultSet)); + } + + @Override public void testGetLong(ResultSet resultSet) throws SQLException { + assertEquals(7L, g.getLong(resultSet)); + } + + @Override public void testGetDecimal(ResultSet resultSet) throws SQLException { + assertEquals(new BigDecimal(7), g.getBigDecimal(resultSet)); + } + + @Override public void testGetFloat(ResultSet resultSet) throws SQLException { + assertEquals(7.0f, g.getFloat(resultSet), 0); + } + + @Override public void testGetDouble(ResultSet resultSet) throws SQLException { + assertEquals(7.0d, g.getDouble(resultSet), 0); + } + } + /** * accessor test helper for the float column */ @@ -1169,36 +1240,48 @@ public static Collection data() { new ShortAccessorTestHelper(new OrdinalGetter(3)), new ShortAccessorTestHelper(new LabelGetter("short")), new IntAccessorTestHelper(new OrdinalGetter(4)), + new IntAccessorTestHelper(new LabelGetter("medium")), + new IntAccessorTestHelper(new OrdinalGetter(5)), new IntAccessorTestHelper(new LabelGetter("int")), - new LongAccessorTestHelper(new OrdinalGetter(5)), + new LongAccessorTestHelper(new OrdinalGetter(6)), new LongAccessorTestHelper(new LabelGetter("long")), - new FloatAccessorTestHelper(new OrdinalGetter(6)), + new ShortAccessorTestHelper(new OrdinalGetter(7)), + new ShortAccessorTestHelper(new LabelGetter("byte_unsigned")), + new IntAccessorTestHelper(new OrdinalGetter(8)), + new IntAccessorTestHelper(new LabelGetter("short_unsigned")), + new LongAccessorTestHelper(new OrdinalGetter(9)), + new LongAccessorTestHelper(new LabelGetter("medium_unsigned")), + new LongAccessorTestHelper(new OrdinalGetter(10)), + new LongAccessorTestHelper(new LabelGetter("int_unsigned")), + new LongUnsingedAccessorTestHelper(new OrdinalGetter(11)), + new LongUnsingedAccessorTestHelper(new LabelGetter("long_unsigned")), + new FloatAccessorTestHelper(new OrdinalGetter(12)), new FloatAccessorTestHelper(new LabelGetter("float")), - new DoubleAccessorTestHelper(new OrdinalGetter(7)), + new DoubleAccessorTestHelper(new OrdinalGetter(13)), new DoubleAccessorTestHelper(new LabelGetter("double")), - new StringAccessorTestHelper(new OrdinalGetter(8)), + new StringAccessorTestHelper(new OrdinalGetter(14)), new StringAccessorTestHelper(new LabelGetter("string")), - new DateAccessorTestHelper(new OrdinalGetter(9)), + new DateAccessorTestHelper(new OrdinalGetter(15)), new DateAccessorTestHelper(new LabelGetter("date")), - new TimeAccessorTestHelper(new OrdinalGetter(10)), + new TimeAccessorTestHelper(new OrdinalGetter(16)), new TimeAccessorTestHelper(new LabelGetter("time")), - new TimestampAccessorTestHelper(new OrdinalGetter(11)), + new TimestampAccessorTestHelper(new OrdinalGetter(17)), new TimestampAccessorTestHelper(new LabelGetter("timestamp")), - new ArrayAccessorTestHelper(new OrdinalGetter(12)), + new ArrayAccessorTestHelper(new OrdinalGetter(18)), new ArrayAccessorTestHelper(new LabelGetter("array")), - new StructAccessorTestHelper(new OrdinalGetter(13)), + new StructAccessorTestHelper(new OrdinalGetter(19)), new StructAccessorTestHelper(new LabelGetter("struct")), - new BooleanAccessorTestHelper(new OrdinalGetter(14)), + new BooleanAccessorTestHelper(new OrdinalGetter(20)), new BooleanAccessorTestHelper(new LabelGetter("bit")), - new NullObjectAccessorTestHelper(new OrdinalGetter(15)), + new NullObjectAccessorTestHelper(new OrdinalGetter(21)), new NullObjectAccessorTestHelper(new LabelGetter("null")), - new DateArrayAccessorTestHelper(new OrdinalGetter(16)), + new DateArrayAccessorTestHelper(new OrdinalGetter(22)), new DateArrayAccessorTestHelper(new LabelGetter("date_array")), - new TimestampArrayAccessorTestHelper(new OrdinalGetter(17)), + new TimestampArrayAccessorTestHelper(new OrdinalGetter(23)), new TimestampArrayAccessorTestHelper(new LabelGetter("timestamp_array")), - new TimeArrayAccessorTestHelper(new OrdinalGetter(18)), + new TimeArrayAccessorTestHelper(new OrdinalGetter(24)), new TimeArrayAccessorTestHelper(new LabelGetter("time_array")), - new DecimalArrayAccessorTestHelper(new OrdinalGetter(19)), + new DecimalArrayAccessorTestHelper(new OrdinalGetter(25)), new DecimalArrayAccessorTestHelper(new LabelGetter("decimal_array"))); } diff --git a/core/src/test/java/org/apache/calcite/avatica/AvaticaResultSetThrowsSqlExceptionTest.java b/core/src/test/java/org/apache/calcite/avatica/AvaticaResultSetThrowsSqlExceptionTest.java index 1eebdd9794..4725fed5a5 100644 --- a/core/src/test/java/org/apache/calcite/avatica/AvaticaResultSetThrowsSqlExceptionTest.java +++ b/core/src/test/java/org/apache/calcite/avatica/AvaticaResultSetThrowsSqlExceptionTest.java @@ -138,28 +138,44 @@ private boolean getColumn(final ResultSet resultSet, final int index, case 3: // SMALLINT resultSet.getShort(index); break; - case 4: // INTEGER + case 4: // MEDIUMINT + case 5: // INTEGER resultSet.getInt(index); break; - case 5: // BIGINT + case 6: // BIGINT resultSet.getLong(index); break; - case 6: // REAL + case 7: // TINYINT_UNSIGNED + resultSet.getShort(index); + break; + case 8: // SMALLINT_UNSIGNED + resultSet.getInt(index); + break; + case 9: // MEDIUMINT_UNSIGNED + resultSet.getLong(index); + break; + case 10: // INTEGER_UNSIGNED + resultSet.getLong(index); + break; + case 11: // BIGINT_UNSIGNED + resultSet.getBigDecimal(index); + break; + case 12: // REAL resultSet.getFloat(index); break; - case 7: // FLOAT + case 13: // FLOAT resultSet.getDouble(index); break; - case 8: // VARCHAR + case 14: // VARCHAR resultSet.getString(index); break; - case 9: // DATE + case 15: // DATE resultSet.getDate(index); break; - case 10: // TIME + case 16: // TIME resultSet.getTime(index); break; - case 11: // TIMESTAMP + case 17: // TIMESTAMP resultSet.getTimestamp(index); break; default: diff --git a/core/src/test/java/org/apache/calcite/avatica/CursorFactoryDeduceTest.java b/core/src/test/java/org/apache/calcite/avatica/CursorFactoryDeduceTest.java index 6050ba425e..3ef769d285 100644 --- a/core/src/test/java/org/apache/calcite/avatica/CursorFactoryDeduceTest.java +++ b/core/src/test/java/org/apache/calcite/avatica/CursorFactoryDeduceTest.java @@ -21,6 +21,7 @@ import org.junit.Test; +import java.math.BigInteger; import java.sql.Types; import java.util.Arrays; import java.util.Collections; @@ -49,19 +50,29 @@ public class CursorFactoryDeduceTest { ColumnMetaData.scalar(Types.DOUBLE, "DOUBLE", ColumnMetaData.Rep.DOUBLE); static final List ROWS = IntStream.range(1, 5) - .mapToObj(i -> (Object) new SimplePOJO(i, Integer.toString(i), (double) i)) + .mapToObj(i -> (Object) new SimplePOJO((byte) i, (short) i, i, i, + new BigInteger(Integer.toString(i)), Integer.toString(i), (double) i)) .collect(Collectors.toList()); /** * Simple POJO for testing cursors over Java objects. */ protected static class SimplePOJO { + public byte byteField; + public short shortField; public int intField; + public long longField; + public BigInteger bigIntField; public String stringField; public Double doubleField; - SimplePOJO(int intField, String stringField, Double doubleField) { + SimplePOJO(byte byteField, short shortField, int intField, long longField, + BigInteger bigIntField, String stringField, Double doubleField) { + this.byteField = byteField; + this.shortField = shortField; this.intField = intField; + this.longField = longField; + this.bigIntField = bigIntField; this.stringField = stringField; this.doubleField = doubleField; } @@ -81,13 +92,18 @@ protected static class SimplePOJO { SimplePOJO pjo = (SimplePOJO) o; - return Objects.equals(stringField, pjo.stringField) + return Objects.equals(byteField, pjo.byteField) + && Objects.equals(shortField, pjo.shortField) && Objects.equals(intField, pjo.intField) + && Objects.equals(longField, pjo.longField) + && Objects.equals(bigIntField, pjo.bigIntField) + && Objects.equals(stringField, pjo.stringField) && Objects.equals(doubleField, pjo.doubleField); } @Override public int hashCode() { - return Objects.hash(stringField, intField, doubleField); + return Objects.hash(byteField, shortField, intField, + longField, bigIntField, stringField, doubleField); } } @@ -244,6 +260,88 @@ protected static class SimplePOJO { assertFalse(cursor.next()); } } + + @Test public void deduceRecordCursorFactoryProjectedSignedField() throws Exception { + List columnsMetaDataList = Arrays.asList( + MetaImpl.columnMetaData("byteField", 1, ColumnMetaData.scalar( + Types.TINYINT, "TINYINT", ColumnMetaData.Rep.BYTE), true, true), + MetaImpl.columnMetaData("shortField", 2, ColumnMetaData.scalar( + Types.SMALLINT, "SMALLINT", ColumnMetaData.Rep.SHORT), true, true), + MetaImpl.columnMetaData("intField", 3, ColumnMetaData.scalar( + Types.INTEGER, "MEDIUMINT", ColumnMetaData.Rep.INTEGER), true, true), + MetaImpl.columnMetaData("intField", 4, ColumnMetaData.scalar( + Types.INTEGER, "INT", ColumnMetaData.Rep.INTEGER), true, true), + MetaImpl.columnMetaData("longField", 5, ColumnMetaData.scalar( + Types.BIGINT, "BIGINT", ColumnMetaData.Rep.LONG), true, true) + ); + Meta.CursorFactory cursorFactory = + Meta.CursorFactory.deduce(columnsMetaDataList, SimplePOJO.class); + + try (Cursor cursor = MetaImpl.createCursor(cursorFactory, ROWS)) { + List accessors = + cursor.createAccessors(columnsMetaDataList, Unsafe.localCalendar(), null); + + assertEquals(columnsMetaDataList.size(), accessors.size()); + Cursor.Accessor byteAccessor = accessors.get(0); + Cursor.Accessor shortAccessor = accessors.get(1); + Cursor.Accessor intAccessor = accessors.get(2); + Cursor.Accessor intAccessor2 = accessors.get(3); + Cursor.Accessor longAccessor = accessors.get(4); + + for (Object row : ROWS) { + assertTrue(cursor.next()); + SimplePOJO pjo = (SimplePOJO) row; + assertEquals(pjo.byteField, byteAccessor.getObject()); + assertEquals(pjo.shortField, shortAccessor.getObject()); + assertEquals(pjo.intField, intAccessor.getObject()); + assertEquals(pjo.intField, intAccessor2.getObject()); + assertEquals(pjo.longField, longAccessor.getObject()); + } + + assertFalse(cursor.next()); + } + } + + @Test public void deduceRecordCursorFactoryProjectedUnsignedField() throws Exception { + List columnsMetaDataList = Arrays.asList( + MetaImpl.columnMetaData("shortField", 1, ColumnMetaData.scalar( + Types.TINYINT, "TINYINT_UNSIGNED", ColumnMetaData.Rep.UBYTE), true, false), + MetaImpl.columnMetaData("intField", 2, ColumnMetaData.scalar( + Types.SMALLINT, "SMALLINT_UNSIGNED", ColumnMetaData.Rep.USHORT), true, false), + MetaImpl.columnMetaData("longField", 3, ColumnMetaData.scalar( + Types.INTEGER, "MEDIUMINT_UNSIGNED", ColumnMetaData.Rep.UINTEGER), true, false), + MetaImpl.columnMetaData("longField", 4, ColumnMetaData.scalar( + Types.INTEGER, "INT_UNSIGNED", ColumnMetaData.Rep.UINTEGER), true, false), + MetaImpl.columnMetaData("bigIntField", 5, ColumnMetaData.scalar( + Types.BIGINT, "BIGINT_UNSIGNED", ColumnMetaData.Rep.ULONG), true, false) + ); + Meta.CursorFactory cursorFactory = + Meta.CursorFactory.deduce(columnsMetaDataList, SimplePOJO.class); + + try (Cursor cursor = MetaImpl.createCursor(cursorFactory, ROWS)) { + List accessors = + cursor.createAccessors(columnsMetaDataList, Unsafe.localCalendar(), null); + + assertEquals(columnsMetaDataList.size(), accessors.size()); + Cursor.Accessor shortAccessor = accessors.get(0); + Cursor.Accessor intAccessor = accessors.get(1); + Cursor.Accessor longAccessor = accessors.get(2); + Cursor.Accessor longAccessor2 = accessors.get(3); + Cursor.Accessor bigIntAccessor = accessors.get(4); + + for (Object row : ROWS) { + assertTrue(cursor.next()); + SimplePOJO pjo = (SimplePOJO) row; + assertEquals(pjo.shortField, shortAccessor.getObject()); + assertEquals(pjo.intField, intAccessor.getObject()); + assertEquals(pjo.longField, longAccessor.getObject()); + assertEquals(pjo.longField, longAccessor2.getObject()); + assertEquals(pjo.bigIntField, bigIntAccessor.getObject()); + } + + assertFalse(cursor.next()); + } + } } // End CursorFactoryDeduceTest.java diff --git a/gradle.properties b/gradle.properties index d2359c1dfc..a252b718ac 100644 --- a/gradle.properties +++ b/gradle.properties @@ -71,6 +71,7 @@ jackson.version=2.15.4 jcip-annotations.version=1.0-1 jcommander.version=1.72 jetty.version=9.4.56.v20240826 +joou-java-6.version=0.9.4 junit.version=4.13.2 kerby.version=2.1.0 log4j2.version=2.17.1 diff --git a/shaded/core/src/test/java/org/apache/calcite/avatica/shadetest/ShadingTest.java b/shaded/core/src/test/java/org/apache/calcite/avatica/shadetest/ShadingTest.java index 7a0e565c6a..0be81419de 100644 --- a/shaded/core/src/test/java/org/apache/calcite/avatica/shadetest/ShadingTest.java +++ b/shaded/core/src/test/java/org/apache/calcite/avatica/shadetest/ShadingTest.java @@ -35,7 +35,7 @@ public class ShadingTest { // Note that many of these files could be excluded. // This is for regression testing, and not the minimum file set. String[] allowedPathPatterns = { "^META-INF", "^org/apache/calcite/", ".*\\.proto", - "^org/slf4j/", "^org/publicsuffix/" }; + "^org/slf4j/", "^org/publicsuffix/", "^org/joou/" }; @Test public void validateShadedJar() throws Exception { diff --git a/standalone-server/src/test/java/org/apache/calcite/avatica/shadetest/ShadingTest.java b/standalone-server/src/test/java/org/apache/calcite/avatica/shadetest/ShadingTest.java index 0774d45de6..5b1a27a61a 100644 --- a/standalone-server/src/test/java/org/apache/calcite/avatica/shadetest/ShadingTest.java +++ b/standalone-server/src/test/java/org/apache/calcite/avatica/shadetest/ShadingTest.java @@ -39,7 +39,7 @@ public class ShadingTest { "^google/protobuf/", "^about.html$", "^org/eclipse/jetty/", "^jetty-dir.css", "^com/google/thirdparty/", "^org/checkerframework/", "^javax/annotation/", "^com/google/errorprone/", "^Log4j-.*\\.xsd$", "^Log4j-.*\\.dtd$", "^Log4j-.*\\.properties$", - "^org/apache/logging/log4j/", "^org/codehaus/mojo/animal_sniffer/" }; + "^org/apache/logging/log4j/", "^org/codehaus/mojo/animal_sniffer/", "^org/joou/" }; @Test public void validateShadedJar() throws Exception {