From 64c312db55340dec4d57f3c1fb4ad2db90aeb3f2 Mon Sep 17 00:00:00 2001 From: David Li Date: Sat, 27 Jan 2024 14:52:41 -0500 Subject: [PATCH] test(java): add Checker Framework Fixes #624. --- .pre-commit-config.yaml | 2 +- ....arrow.adapter.jdbc.JdbcToArrowUtils.astub | 28 ++++++ ...he.arrow.vector.types.pojo.ArrowType.astub | 31 ++++++ .../org.junit.jupiter.api.Assumptions.astub | 29 ++++++ java/core/pom.xml | 6 ++ .../arrow/adbc/core/AdbcConnection.java | 3 +- .../apache/arrow/adbc/core/AdbcDriver.java | 1 + .../apache/arrow/adbc/core/AdbcException.java | 17 ++-- .../apache/arrow/adbc/core/ErrorDetail.java | 3 +- .../arrow/adbc/core/PartitionDescriptor.java | 3 +- .../arrow/adbc/core/StandardSchemas.java | 96 ++++++++++++++----- .../org/apache/arrow/adbc/core/TypedKey.java | 10 +- java/driver-manager/pom.xml | 6 ++ .../adbc/drivermanager/AdbcDriverManager.java | 14 ++- .../driver/flightsql/FlightSqlQuirks.java | 4 +- java/driver/flight-sql/pom.xml | 6 ++ .../driver/flightsql/FlightInfoReader.java | 12 ++- .../driver/flightsql/FlightSqlConnection.java | 5 +- .../driver/flightsql/FlightSqlDriverUtil.java | 7 +- .../driver/flightsql/FlightSqlStatement.java | 59 +++++++----- .../driver/flightsql/InfoMetadataBuilder.java | 8 +- java/driver/jdbc/pom.xml | 6 ++ .../arrow/adbc/driver/jdbc/JdbcDriver.java | 2 + .../adbc/driver/jdbc/JdbcDriverUtil.java | 3 +- .../arrow/adbc/driver/jdbc/JdbcStatement.java | 2 +- .../adbc/driver/jdbc/StandardJdbcQuirks.java | 46 +-------- .../arrow/adbc/driver/jdbc/UrlDataSource.java | 7 +- .../adapter/JdbcToArrowTypeConverters.java | 5 +- .../AbstractConnectionMetadataTest.java | 3 +- .../testsuite/AbstractTransactionTest.java | 12 +-- .../adbc/driver/testsuite/SqlTestUtil.java | 6 +- java/pom.xml | 32 ++++++- java/sql/pom.xml | 6 ++ .../org/apache/arrow/adbc/sql/SqlQuirks.java | 79 +++++++-------- 34 files changed, 380 insertions(+), 179 deletions(-) create mode 100644 java/.checker-framework/org.apache.arrow.adapter.jdbc.JdbcToArrowUtils.astub create mode 100644 java/.checker-framework/org.apache.arrow.vector.types.pojo.ArrowType.astub create mode 100644 java/.checker-framework/org.junit.jupiter.api.Assumptions.astub diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 480ca074f5..fb3a19cdae 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -65,7 +65,7 @@ repos: entry: bash -c 'cd go/adbc && golangci-lint run --fix --timeout 5m' types_or: [go] - repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks - rev: v2.3.0 + rev: v2.12.0 hooks: - id: pretty-format-golang - id: pretty-format-java diff --git a/java/.checker-framework/org.apache.arrow.adapter.jdbc.JdbcToArrowUtils.astub b/java/.checker-framework/org.apache.arrow.adapter.jdbc.JdbcToArrowUtils.astub new file mode 100644 index 0000000000..0ee9d0ebf7 --- /dev/null +++ b/java/.checker-framework/org.apache.arrow.adapter.jdbc.JdbcToArrowUtils.astub @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.arrow.adapter.jdbc; + +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.Calendar; + +import org.apache.arrow.vector.types.pojo.ArrowType; + +public class JdbcToArrowUtils { + public static ArrowType getArrowTypeFromJdbcType(JdbcFieldInfo fieldInfo, @Nullable Calendar calendar); +} diff --git a/java/.checker-framework/org.apache.arrow.vector.types.pojo.ArrowType.astub b/java/.checker-framework/org.apache.arrow.vector.types.pojo.ArrowType.astub new file mode 100644 index 0000000000..8827b847af --- /dev/null +++ b/java/.checker-framework/org.apache.arrow.vector.types.pojo.ArrowType.astub @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.arrow.vector.types.pojo; + +import org.checkerframework.checker.nullness.qual.Nullable; + +import org.apache.arrow.vector.types.TimeUnit; + +public abstract class ArrowType { + public abstract static class PrimitiveType extends ArrowType { + } + + public static class Timestamp extends PrimitiveType { + public Timestamp(TimeUnit unit, @Nullable String timezone); + } +} diff --git a/java/.checker-framework/org.junit.jupiter.api.Assumptions.astub b/java/.checker-framework/org.junit.jupiter.api.Assumptions.astub new file mode 100644 index 0000000000..46ee85fb35 --- /dev/null +++ b/java/.checker-framework/org.junit.jupiter.api.Assumptions.astub @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.junit.jupiter.api; + +import org.checkerframework.dataflow.qual.AssertMethod; +import org.opentest4j.TestAbortedException; + +public class Assumptions { + @AssertMethod(value = TestAbortedException.class) + public static void assumeTrue(boolean assumption, String message) throws TestAbortedException; + + @AssertMethod(isAssertFalse = true, value = TestAbortedException.class) + public static void assumeFalse(boolean assumption, String message) throws TestAbortedException; +} diff --git a/java/core/pom.xml b/java/core/pom.xml index ac499b9a25..f61c686896 100644 --- a/java/core/pom.xml +++ b/java/core/pom.xml @@ -33,6 +33,12 @@ arrow-vector + + + org.checkerframework + checker-qual + + org.assertj diff --git a/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcConnection.java b/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcConnection.java index c8e897eeee..060c65e816 100644 --- a/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcConnection.java +++ b/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcConnection.java @@ -20,6 +20,7 @@ import org.apache.arrow.vector.VectorSchemaRoot; import org.apache.arrow.vector.ipc.ArrowReader; import org.apache.arrow.vector.types.pojo.Schema; +import org.checkerframework.checker.nullness.qual.Nullable; /** * A connection to a {@link AdbcDatabase}. @@ -79,7 +80,7 @@ default ArrowReader readPartition(ByteBuffer descriptor) throws AdbcException { * * @param infoCodes The metadata items to fetch. */ - ArrowReader getInfo(int[] infoCodes) throws AdbcException; + ArrowReader getInfo(int @Nullable [] infoCodes) throws AdbcException; /** * Get metadata about the driver/database. diff --git a/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcDriver.java b/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcDriver.java index 5e32fd1ed7..797b863586 100644 --- a/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcDriver.java +++ b/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcDriver.java @@ -54,6 +54,7 @@ public interface AdbcDriver { /** ADBC API revision 1.0.0. */ long ADBC_VERSION_1_0_0 = 1_000_000; + /** ADBC API revision 1.1.0. */ long ADBC_VERSION_1_1_0 = 1_001_000; diff --git a/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcException.java b/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcException.java index dce7570e3d..193bbaa96c 100644 --- a/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcException.java +++ b/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcException.java @@ -18,6 +18,7 @@ import java.util.Collection; import java.util.Collections; +import org.checkerframework.checker.nullness.qual.Nullable; /** * An error in the database or ADBC driver. @@ -34,20 +35,24 @@ */ public class AdbcException extends Exception { private final AdbcStatusCode status; - private final String sqlState; + private final @Nullable String sqlState; private final int vendorCode; private Collection details; public AdbcException( - String message, Throwable cause, AdbcStatusCode status, String sqlState, int vendorCode) { + @Nullable String message, + @Nullable Throwable cause, + AdbcStatusCode status, + @Nullable String sqlState, + int vendorCode) { this(message, cause, status, sqlState, vendorCode, Collections.emptyList()); } public AdbcException( - String message, - Throwable cause, + @Nullable String message, + @Nullable Throwable cause, AdbcStatusCode status, - String sqlState, + @Nullable String sqlState, int vendorCode, Collection details) { super(message, cause); @@ -83,7 +88,7 @@ public AdbcStatusCode getStatus() { } /** A SQLSTATE error code, if provided, as defined by the SQL:2003 standard. */ - public String getSqlState() { + public @Nullable String getSqlState() { return sqlState; } diff --git a/java/core/src/main/java/org/apache/arrow/adbc/core/ErrorDetail.java b/java/core/src/main/java/org/apache/arrow/adbc/core/ErrorDetail.java index 13521fb82e..5149b9de77 100644 --- a/java/core/src/main/java/org/apache/arrow/adbc/core/ErrorDetail.java +++ b/java/core/src/main/java/org/apache/arrow/adbc/core/ErrorDetail.java @@ -17,6 +17,7 @@ package org.apache.arrow.adbc.core; import java.util.Objects; +import org.checkerframework.checker.nullness.qual.Nullable; /** Additional details (not necessarily human-readable) contained in an {@link AdbcException}. */ public class ErrorDetail { @@ -37,7 +38,7 @@ public Object getValue() { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/java/core/src/main/java/org/apache/arrow/adbc/core/PartitionDescriptor.java b/java/core/src/main/java/org/apache/arrow/adbc/core/PartitionDescriptor.java index 3f2047801c..5890db9047 100644 --- a/java/core/src/main/java/org/apache/arrow/adbc/core/PartitionDescriptor.java +++ b/java/core/src/main/java/org/apache/arrow/adbc/core/PartitionDescriptor.java @@ -18,6 +18,7 @@ import java.nio.ByteBuffer; import java.util.Objects; +import org.checkerframework.checker.nullness.qual.Nullable; /** An opaque descriptor for a part of a potentially distributed or partitioned result set. */ public final class PartitionDescriptor { @@ -32,7 +33,7 @@ public ByteBuffer getDescriptor() { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/java/core/src/main/java/org/apache/arrow/adbc/core/StandardSchemas.java b/java/core/src/main/java/org/apache/arrow/adbc/core/StandardSchemas.java index c059bb1b57..31f8ddb97b 100644 --- a/java/core/src/main/java/org/apache/arrow/adbc/core/StandardSchemas.java +++ b/java/core/src/main/java/org/apache/arrow/adbc/core/StandardSchemas.java @@ -98,30 +98,64 @@ private StandardSchemas() { public static final List COLUMN_SCHEMA = Arrays.asList( - new Field("column_name", FieldType.notNullable(ArrowType.Utf8.INSTANCE), null), - new Field("ordinal_position", FieldType.nullable(INT32), null), - new Field("remarks", FieldType.nullable(ArrowType.Utf8.INSTANCE), null), - new Field("xdbc_data_type", FieldType.nullable(INT16), null), - new Field("xdbc_type_name", FieldType.nullable(ArrowType.Utf8.INSTANCE), null), - new Field("xdbc_column_size", FieldType.nullable(INT32), null), - new Field("xdbc_decimal_digits", FieldType.nullable(INT16), null), - new Field("xdbc_num_prec_radix", FieldType.nullable(INT16), null), - new Field("xdbc_nullable", FieldType.nullable(INT16), null), - new Field("xdbc_column_def", FieldType.nullable(ArrowType.Utf8.INSTANCE), null), - new Field("xdbc_sql_data_type", FieldType.nullable(INT16), null), - new Field("xdbc_datetime_sub", FieldType.nullable(INT16), null), - new Field("xdbc_char_octet_length", FieldType.nullable(INT32), null), - new Field("xdbc_is_nullable", FieldType.nullable(ArrowType.Utf8.INSTANCE), null), - new Field("xdbc_scope_catalog", FieldType.nullable(ArrowType.Utf8.INSTANCE), null), - new Field("xdbc_scope_schema", FieldType.nullable(ArrowType.Utf8.INSTANCE), null), - new Field("xdbc_scope_table", FieldType.nullable(ArrowType.Utf8.INSTANCE), null), - new Field("xdbc_is_autoincrement", FieldType.nullable(ArrowType.Bool.INSTANCE), null), - new Field("xdbc_is_generatedcolumn", FieldType.nullable(ArrowType.Bool.INSTANCE), null)); + new Field( + "column_name", + FieldType.notNullable(ArrowType.Utf8.INSTANCE), + Collections.emptyList()), + new Field("ordinal_position", FieldType.nullable(INT32), Collections.emptyList()), + new Field( + "remarks", FieldType.nullable(ArrowType.Utf8.INSTANCE), Collections.emptyList()), + new Field("xdbc_data_type", FieldType.nullable(INT16), Collections.emptyList()), + new Field( + "xdbc_type_name", + FieldType.nullable(ArrowType.Utf8.INSTANCE), + Collections.emptyList()), + new Field("xdbc_column_size", FieldType.nullable(INT32), Collections.emptyList()), + new Field("xdbc_decimal_digits", FieldType.nullable(INT16), Collections.emptyList()), + new Field("xdbc_num_prec_radix", FieldType.nullable(INT16), Collections.emptyList()), + new Field("xdbc_nullable", FieldType.nullable(INT16), Collections.emptyList()), + new Field( + "xdbc_column_def", + FieldType.nullable(ArrowType.Utf8.INSTANCE), + Collections.emptyList()), + new Field("xdbc_sql_data_type", FieldType.nullable(INT16), Collections.emptyList()), + new Field("xdbc_datetime_sub", FieldType.nullable(INT16), Collections.emptyList()), + new Field("xdbc_char_octet_length", FieldType.nullable(INT32), Collections.emptyList()), + new Field( + "xdbc_is_nullable", + FieldType.nullable(ArrowType.Utf8.INSTANCE), + Collections.emptyList()), + new Field( + "xdbc_scope_catalog", + FieldType.nullable(ArrowType.Utf8.INSTANCE), + Collections.emptyList()), + new Field( + "xdbc_scope_schema", + FieldType.nullable(ArrowType.Utf8.INSTANCE), + Collections.emptyList()), + new Field( + "xdbc_scope_table", + FieldType.nullable(ArrowType.Utf8.INSTANCE), + Collections.emptyList()), + new Field( + "xdbc_is_autoincrement", + FieldType.nullable(ArrowType.Bool.INSTANCE), + Collections.emptyList()), + new Field( + "xdbc_is_generatedcolumn", + FieldType.nullable(ArrowType.Bool.INSTANCE), + Collections.emptyList())); public static final List TABLE_SCHEMA = Arrays.asList( - new Field("table_name", FieldType.notNullable(ArrowType.Utf8.INSTANCE), null), - new Field("table_type", FieldType.notNullable(ArrowType.Utf8.INSTANCE), null), + new Field( + "table_name", + FieldType.notNullable(ArrowType.Utf8.INSTANCE), + Collections.emptyList()), + new Field( + "table_type", + FieldType.notNullable(ArrowType.Utf8.INSTANCE), + Collections.emptyList()), new Field( "table_columns", FieldType.nullable(ArrowType.List.INSTANCE), @@ -136,7 +170,10 @@ private StandardSchemas() { public static final List DB_SCHEMA_SCHEMA = Arrays.asList( - new Field("db_schema_name", FieldType.notNullable(ArrowType.Utf8.INSTANCE), null), + new Field( + "db_schema_name", + FieldType.notNullable(ArrowType.Utf8.INSTANCE), + Collections.emptyList()), new Field( "db_schema_tables", FieldType.nullable(ArrowType.List.INSTANCE), @@ -150,7 +187,10 @@ private StandardSchemas() { public static final Schema GET_OBJECTS_SCHEMA = new Schema( Arrays.asList( - new Field("catalog_name", FieldType.notNullable(ArrowType.Utf8.INSTANCE), null), + new Field( + "catalog_name", + FieldType.notNullable(ArrowType.Utf8.INSTANCE), + Collections.emptyList()), new Field( "catalog_db_schemas", FieldType.nullable(ArrowType.List.INSTANCE), @@ -180,7 +220,10 @@ private StandardSchemas() { public static final List STATISTICS_DB_SCHEMA_SCHEMA = Arrays.asList( - new Field("db_schema_name", FieldType.notNullable(ArrowType.Utf8.INSTANCE), null), + new Field( + "db_schema_name", + FieldType.notNullable(ArrowType.Utf8.INSTANCE), + Collections.emptyList()), new Field( "db_schema_statistics", FieldType.notNullable(ArrowType.List.INSTANCE), @@ -195,7 +238,10 @@ private StandardSchemas() { public static final Schema GET_STATISTICS_SCHEMA = new Schema( Arrays.asList( - new Field("catalog_name", FieldType.notNullable(ArrowType.Utf8.INSTANCE), null), + new Field( + "catalog_name", + FieldType.notNullable(ArrowType.Utf8.INSTANCE), + Collections.emptyList()), new Field( "catalog_db_schemas", FieldType.notNullable(ArrowType.List.INSTANCE), diff --git a/java/core/src/main/java/org/apache/arrow/adbc/core/TypedKey.java b/java/core/src/main/java/org/apache/arrow/adbc/core/TypedKey.java index 21523bb429..1f1dda2f4b 100644 --- a/java/core/src/main/java/org/apache/arrow/adbc/core/TypedKey.java +++ b/java/core/src/main/java/org/apache/arrow/adbc/core/TypedKey.java @@ -19,6 +19,8 @@ import java.util.Map; import java.util.Objects; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; /** * A typesafe option key. @@ -45,8 +47,8 @@ public String getKey() { * * @throws ClassCastException if the value is of the wrong type. */ - public T get(Map options) { - Object value = options.get(key); + public @Nullable T get(Map options) { + @Nullable Object value = options.get(key); if (value == null) { return null; } @@ -59,12 +61,12 @@ public T get(Map options) { * @param options The options. * @param value The option value. */ - public void set(Map options, T value) { + public void set(Map options, @NonNull T value) { options.put(key, value); } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/java/driver-manager/pom.xml b/java/driver-manager/pom.xml index 79ad55b992..ce9464a087 100644 --- a/java/driver-manager/pom.xml +++ b/java/driver-manager/pom.xml @@ -28,6 +28,12 @@ adbc-core + + + org.checkerframework + checker-qual + + org.assertj diff --git a/java/driver-manager/src/main/java/org/apache/arrow/adbc/drivermanager/AdbcDriverManager.java b/java/driver-manager/src/main/java/org/apache/arrow/adbc/drivermanager/AdbcDriverManager.java index c2e20efd02..5068fb9a1f 100644 --- a/java/driver-manager/src/main/java/org/apache/arrow/adbc/drivermanager/AdbcDriverManager.java +++ b/java/driver-manager/src/main/java/org/apache/arrow/adbc/drivermanager/AdbcDriverManager.java @@ -27,6 +27,7 @@ import org.apache.arrow.adbc.core.AdbcException; import org.apache.arrow.adbc.core.AdbcStatusCode; import org.apache.arrow.memory.BufferAllocator; +import org.checkerframework.checker.nullness.qual.Nullable; /** Instantiate connections to ABDC databases generically based on driver name. */ public final class AdbcDriverManager { @@ -38,10 +39,13 @@ private AdbcDriverManager() { driverFactoryFunctions = new ConcurrentHashMap<>(); final ServiceLoader serviceLoader = ServiceLoader.load(AdbcDriverFactory.class); - serviceLoader.forEach( - driverFactory -> - driverFactoryFunctions.putIfAbsent( - driverFactory.getClass().getCanonicalName(), driverFactory::getDriver)); + for (AdbcDriverFactory driverFactory : serviceLoader) { + final @Nullable String className = driverFactory.getClass().getCanonicalName(); + if (className == null) { + throw new RuntimeException("Class has no canonical name"); + } + driverFactoryFunctions.putIfAbsent(className, driverFactory::getDriver); + } } /** @@ -77,7 +81,7 @@ public AdbcDatabase connect( * fully-qualified class name of an AdbcDriverFactory class. * @return A function to construct an AdbcDriver from a BufferAllocator, or null if not found. */ - Function lookupDriver(String driverFactoryName) { + @Nullable Function lookupDriver(String driverFactoryName) { return driverFactoryFunctions.get(driverFactoryName); } diff --git a/java/driver/flight-sql-validation/src/test/java/org/apache/arrow/adbc/driver/flightsql/FlightSqlQuirks.java b/java/driver/flight-sql-validation/src/test/java/org/apache/arrow/adbc/driver/flightsql/FlightSqlQuirks.java index d3f79889ec..fe01a18cd6 100644 --- a/java/driver/flight-sql-validation/src/test/java/org/apache/arrow/adbc/driver/flightsql/FlightSqlQuirks.java +++ b/java/driver/flight-sql-validation/src/test/java/org/apache/arrow/adbc/driver/flightsql/FlightSqlQuirks.java @@ -36,8 +36,8 @@ public class FlightSqlQuirks extends SqlValidationQuirks { static String getFlightLocation() { final String location = System.getenv(FLIGHT_SQL_LOCATION_ENV_VAR); - Assumptions.assumeFalse( - location == null || location.isEmpty(), + Assumptions.assumeTrue( + location != null && !location.isEmpty(), "Flight SQL server not found, set " + FLIGHT_SQL_LOCATION_ENV_VAR); return location; } diff --git a/java/driver/flight-sql/pom.xml b/java/driver/flight-sql/pom.xml index 26dd8a7262..040a9a3ba7 100644 --- a/java/driver/flight-sql/pom.xml +++ b/java/driver/flight-sql/pom.xml @@ -67,6 +67,12 @@ adbc-sql + + + org.checkerframework + checker-qual + + org.assertj diff --git a/java/driver/flight-sql/src/main/java/org/apache/arrow/adbc/driver/flightsql/FlightInfoReader.java b/java/driver/flight-sql/src/main/java/org/apache/arrow/adbc/driver/flightsql/FlightInfoReader.java index 9b0cda91dc..5b73c80dd0 100644 --- a/java/driver/flight-sql/src/main/java/org/apache/arrow/adbc/driver/flightsql/FlightInfoReader.java +++ b/java/driver/flight-sql/src/main/java/org/apache/arrow/adbc/driver/flightsql/FlightInfoReader.java @@ -36,6 +36,7 @@ import org.apache.arrow.vector.ipc.ArrowReader; import org.apache.arrow.vector.ipc.message.ArrowRecordBatch; import org.apache.arrow.vector.types.pojo.Schema; +import org.checkerframework.checker.nullness.qual.Nullable; /** An ArrowReader that wraps a FlightInfo. */ public class FlightInfoReader extends ArrowReader { @@ -47,6 +48,8 @@ public class FlightInfoReader extends ArrowReader { private FlightStream currentStream; private long bytesRead; + @SuppressWarnings( + "method.invocation") // Checker Framework does not like the ensureInitialized call FlightInfoReader( BufferAllocator allocator, FlightSqlClient client, @@ -120,8 +123,12 @@ private FlightStream tryLoadNextStream(FlightEndpoint endpoint) throws IOExcepti Collections.shuffle(locations); IOException failure = null; for (final Location location : locations) { + final @Nullable FlightClient client = clientCache.get(location); + if (client == null) { + throw new IllegalStateException("Could not connect to " + location); + } try { - return Objects.requireNonNull(clientCache.get(location)).getStream(endpoint.getTicket()); + return client.getStream(endpoint.getTicket()); } catch (RuntimeException e) { // Also handles CompletionException (from clientCache#get), FlightRuntimeException if (failure == null) { @@ -133,6 +140,9 @@ private FlightStream tryLoadNextStream(FlightEndpoint endpoint) throws IOExcepti } } } + if (failure == null) { + throw new IllegalStateException("FlightEndpoint had no locations"); + } throw Objects.requireNonNull(failure); } } diff --git a/java/driver/flight-sql/src/main/java/org/apache/arrow/adbc/driver/flightsql/FlightSqlConnection.java b/java/driver/flight-sql/src/main/java/org/apache/arrow/adbc/driver/flightsql/FlightSqlConnection.java index f583f2b866..b9dd29be0e 100644 --- a/java/driver/flight-sql/src/main/java/org/apache/arrow/adbc/driver/flightsql/FlightSqlConnection.java +++ b/java/driver/flight-sql/src/main/java/org/apache/arrow/adbc/driver/flightsql/FlightSqlConnection.java @@ -39,6 +39,7 @@ import org.apache.arrow.util.AutoCloseables; import org.apache.arrow.vector.VectorSchemaRoot; import org.apache.arrow.vector.ipc.ArrowReader; +import org.checkerframework.checker.nullness.qual.Nullable; public class FlightSqlConnection implements AdbcConnection { private final BufferAllocator allocator; @@ -54,7 +55,7 @@ public class FlightSqlConnection implements AdbcConnection { Caffeine.newBuilder() .expireAfterAccess(5, TimeUnit.MINUTES) .removalListener( - (Location key, FlightClient value, RemovalCause cause) -> { + (@Nullable Location key, @Nullable FlightClient value, RemovalCause cause) -> { if (value == null) return; try { value.close(); @@ -108,7 +109,7 @@ public AdbcStatement bulkIngest(String targetTableName, BulkIngestMode mode) } @Override - public ArrowReader getInfo(int[] infoCodes) throws AdbcException { + public ArrowReader getInfo(int @Nullable [] infoCodes) throws AdbcException { try (InfoMetadataBuilder builder = new InfoMetadataBuilder(allocator, client, infoCodes)) { try (final VectorSchemaRoot root = builder.build()) { return RootArrowReader.fromRoot(allocator, root); diff --git a/java/driver/flight-sql/src/main/java/org/apache/arrow/adbc/driver/flightsql/FlightSqlDriverUtil.java b/java/driver/flight-sql/src/main/java/org/apache/arrow/adbc/driver/flightsql/FlightSqlDriverUtil.java index 45b42df2ee..0e9291ae34 100644 --- a/java/driver/flight-sql/src/main/java/org/apache/arrow/adbc/driver/flightsql/FlightSqlDriverUtil.java +++ b/java/driver/flight-sql/src/main/java/org/apache/arrow/adbc/driver/flightsql/FlightSqlDriverUtil.java @@ -24,13 +24,18 @@ import org.apache.arrow.adbc.core.ErrorDetail; import org.apache.arrow.flight.FlightRuntimeException; import org.apache.arrow.flight.FlightStatusCode; +import org.checkerframework.checker.nullness.qual.Nullable; final class FlightSqlDriverUtil { private FlightSqlDriverUtil() { throw new AssertionError("Do not instantiate this class"); } - static String prefixExceptionMessage(final String s) { + static String prefixExceptionMessage(final @Nullable String s) { + // Allow null since Throwable#getMessage may be null + if (s == null) { + return "[Flight SQL] (No or unknown error)"; + } return "[Flight SQL] " + s; } diff --git a/java/driver/flight-sql/src/main/java/org/apache/arrow/adbc/driver/flightsql/FlightSqlStatement.java b/java/driver/flight-sql/src/main/java/org/apache/arrow/adbc/driver/flightsql/FlightSqlStatement.java index e64508b4bf..30ef03c09a 100644 --- a/java/driver/flight-sql/src/main/java/org/apache/arrow/adbc/driver/flightsql/FlightSqlStatement.java +++ b/java/driver/flight-sql/src/main/java/org/apache/arrow/adbc/driver/flightsql/FlightSqlStatement.java @@ -41,6 +41,7 @@ import org.apache.arrow.vector.VectorSchemaRoot; import org.apache.arrow.vector.types.pojo.Field; import org.apache.arrow.vector.types.pojo.Schema; +import org.checkerframework.checker.nullness.qual.Nullable; public class FlightSqlStatement implements AdbcStatement { private final BufferAllocator allocator; @@ -49,11 +50,11 @@ public class FlightSqlStatement implements AdbcStatement { private final SqlQuirks quirks; // State for SQL queries - private String sqlQuery; - private FlightSqlClient.PreparedStatement preparedStatement; + private @Nullable String sqlQuery; + private FlightSqlClient.@Nullable PreparedStatement preparedStatement; // State for bulk ingest - private BulkState bulkOperation; - private VectorSchemaRoot bindRoot; + private @Nullable BulkState bulkOperation; + private @Nullable VectorSchemaRoot bindRoot; FlightSqlStatement( BufferAllocator allocator, @@ -65,6 +66,9 @@ public class FlightSqlStatement implements AdbcStatement { this.clientCache = clientCache; this.quirks = quirks; this.sqlQuery = null; + this.preparedStatement = null; + this.bulkOperation = null; + this.bindRoot = null; } static FlightSqlStatement ingestRoot( @@ -77,9 +81,7 @@ static FlightSqlStatement ingestRoot( Objects.requireNonNull(targetTableName); final FlightSqlStatement statement = new FlightSqlStatement(allocator, client, clientCache, quirks); - statement.bulkOperation = new BulkState(); - statement.bulkOperation.mode = mode; - statement.bulkOperation.targetTable = targetTableName; + statement.bulkOperation = new BulkState(mode, targetTableName); return statement; } @@ -97,7 +99,8 @@ public void bind(VectorSchemaRoot root) { bindRoot = root; } - private void createBulkTable() throws AdbcException { + private void createBulkTable(BulkState bulkOperation, VectorSchemaRoot bindRoot) + throws AdbcException { final StringBuilder create = new StringBuilder("CREATE TABLE "); create.append(bulkOperation.targetTable); create.append(" ("); @@ -129,20 +132,21 @@ private void createBulkTable() throws AdbcException { } } - private UpdateResult executeBulk() throws AdbcException { + private UpdateResult executeBulk(BulkState bulkOperation) throws AdbcException { if (bindRoot == null) { throw AdbcException.invalidState("[Flight SQL] Must call bind() before bulk insert"); } + final VectorSchemaRoot bindParams = bindRoot; if (bulkOperation.mode == BulkIngestMode.CREATE) { - createBulkTable(); + createBulkTable(bulkOperation, bindParams); } // XXX: potential injection final StringBuilder insert = new StringBuilder("INSERT INTO "); insert.append(bulkOperation.targetTable); insert.append(" VALUES ("); - for (int col = 0; col < bindRoot.getFieldVectors().size(); col++) { + for (int col = 0; col < bindParams.getFieldVectors().size(); col++) { if (col > 0) { insert.append(", "); } @@ -164,7 +168,7 @@ private UpdateResult executeBulk() throws AdbcException { } try { try { - statement.setParameters(new NonOwningRoot(bindRoot)); + statement.setParameters(new NonOwningRoot(bindParams)); statement.executeUpdate(); } finally { statement.close(); @@ -178,7 +182,7 @@ private UpdateResult executeBulk() throws AdbcException { } throw FlightSqlDriverUtil.fromFlightException(e); } - return new UpdateResult(bindRoot.getRowCount()); + return new UpdateResult(bindParams.getRowCount()); } @FunctionalInterface @@ -192,13 +196,14 @@ private R execute( throws AdbcException { try { if (preparedStatement != null) { + FlightSqlClient.PreparedStatement prepared = preparedStatement; // TODO: This binds only the LAST row // See https://lists.apache.org/thread/47zfk3xooojckvfjq2h6ldlqkjrqnsjt // "[DISC] Flight SQL: clarifying prepared statements with parameters and result sets" if (bindRoot != null) { - preparedStatement.setParameters(new NonOwningRoot(bindRoot)); + prepared.setParameters(new NonOwningRoot(bindRoot)); } - return doPrepared.execute(preparedStatement); + return doPrepared.execute(prepared); } else { return doRegular.execute(client); } @@ -213,8 +218,8 @@ private FlightInfo executeFlightInfo() throws AdbcException { } else if (sqlQuery == null) { throw AdbcException.invalidState("[Flight SQL] Must setSqlQuery() before execute"); } - return execute( - FlightSqlClient.PreparedStatement::execute, (client) -> client.execute(sqlQuery)); + final String query = sqlQuery; + return execute(FlightSqlClient.PreparedStatement::execute, (client) -> client.execute(query)); } @Override @@ -254,18 +259,20 @@ public Schema executeSchema() throws AdbcException { } else if (sqlQuery == null) { throw AdbcException.invalidState("[Flight SQL] Must setSqlQuery() before execute"); } + final String query = sqlQuery; return execute( FlightSqlClient.PreparedStatement::getResultSetSchema, - (client) -> client.getExecuteSchema(sqlQuery).getSchema()); + (client) -> client.getExecuteSchema(query).getSchema()); } @Override public UpdateResult executeUpdate() throws AdbcException { if (bulkOperation != null) { - return executeBulk(); + return executeBulk(bulkOperation); } else if (sqlQuery == null) { throw AdbcException.invalidState("[Flight SQL] Must setSqlQuery() before executeUpdate"); } + final String query = sqlQuery; long updatedRows = execute( (preparedStatement) -> { @@ -276,7 +283,7 @@ public UpdateResult executeUpdate() throws AdbcException { throw FlightSqlDriverUtil.fromFlightException(e); } }, - (client) -> client.executeUpdate(sqlQuery)); + (client) -> client.executeUpdate(query)); return new UpdateResult(updatedRows); } @@ -304,12 +311,20 @@ public void prepare() throws AdbcException { @Override public void close() throws Exception { - AutoCloseables.close(preparedStatement); + // TODO(https://github.com/apache/arrow/issues/39814): this is annotated wrongly upstream + if (preparedStatement != null) { + AutoCloseables.close(preparedStatement); + } } private static final class BulkState { - public BulkIngestMode mode; + BulkIngestMode mode; String targetTable; + + public BulkState(BulkIngestMode mode, String targetTableName) { + this.mode = mode; + this.targetTable = targetTableName; + } } /** A VectorSchemaRoot which does not own its data. */ diff --git a/java/driver/flight-sql/src/main/java/org/apache/arrow/adbc/driver/flightsql/InfoMetadataBuilder.java b/java/driver/flight-sql/src/main/java/org/apache/arrow/adbc/driver/flightsql/InfoMetadataBuilder.java index 318405d6ce..20d1e0f2c4 100644 --- a/java/driver/flight-sql/src/main/java/org/apache/arrow/adbc/driver/flightsql/InfoMetadataBuilder.java +++ b/java/driver/flight-sql/src/main/java/org/apache/arrow/adbc/driver/flightsql/InfoMetadataBuilder.java @@ -39,6 +39,7 @@ import org.apache.arrow.vector.VarCharVector; import org.apache.arrow.vector.VectorSchemaRoot; import org.apache.arrow.vector.complex.DenseUnionVector; +import org.checkerframework.checker.nullness.qual.Nullable; /** Helper class to track state needed to build up the info structure. */ final class InfoMetadataBuilder implements AutoCloseable { @@ -80,7 +81,8 @@ interface AddInfo { }); } - InfoMetadataBuilder(BufferAllocator allocator, FlightSqlClient client, int[] infoCodes) { + InfoMetadataBuilder( + BufferAllocator allocator, FlightSqlClient client, int @Nullable [] infoCodes) { if (infoCodes == null) { this.requestedCodes = new ArrayList<>(SUPPORTED_CODES.keySet()); this.requestedCodes.add(AdbcInfoCode.DRIVER_NAME.getValue()); @@ -144,9 +146,7 @@ VectorSchemaRoot build() throws AdbcException { } root.setRowCount(dstIndex); - VectorSchemaRoot result = root; - root = null; - return result; + return root; } @Override diff --git a/java/driver/jdbc/pom.xml b/java/driver/jdbc/pom.xml index 6b9eda4afe..5a415c38f1 100644 --- a/java/driver/jdbc/pom.xml +++ b/java/driver/jdbc/pom.xml @@ -51,6 +51,12 @@ adbc-sql + + + org.checkerframework + checker-qual + + org.assertj diff --git a/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcDriver.java b/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcDriver.java index 14d4f0bee3..e197d225bc 100644 --- a/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcDriver.java +++ b/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcDriver.java @@ -30,8 +30,10 @@ public class JdbcDriver implements AdbcDriver { /** A parameter for creating an {@link AdbcDatabase} from a {@link DataSource}. */ public static final String PARAM_DATASOURCE = "adbc.jdbc.datasource"; + /** A parameter for specifying backend-specific configuration (type: {@link JdbcQuirks}). */ public static final String PARAM_JDBC_QUIRKS = "adbc.jdbc.quirks"; + /** * A parameter for specifying a URI to connect to. * diff --git a/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcDriverUtil.java b/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcDriverUtil.java index b2b65d7744..ed38bd1b2a 100644 --- a/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcDriverUtil.java +++ b/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcDriverUtil.java @@ -23,6 +23,7 @@ import java.util.Set; import org.apache.arrow.adbc.core.AdbcException; import org.apache.arrow.adbc.core.AdbcStatusCode; +import org.checkerframework.checker.nullness.qual.Nullable; final class JdbcDriverUtil { // Do our best to properly map database-specific errors to NOT_FOUND status. @@ -45,7 +46,7 @@ static String prefixExceptionMessage(final String s) { return "[JDBC] " + s; } - static AdbcStatusCode guessStatusCode(String sqlState) { + static AdbcStatusCode guessStatusCode(@Nullable String sqlState) { if (sqlState == null) { return AdbcStatusCode.UNKNOWN; } else if (SQLSTATE_TABLE_NOT_FOUND.contains(sqlState)) { diff --git a/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcStatement.java b/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcStatement.java index fd39e6d08b..b642be0d0b 100644 --- a/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcStatement.java +++ b/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcStatement.java @@ -261,7 +261,7 @@ public QueryResult executeQuery() throws AdbcException { } catch (SQLException e) { throw JdbcDriverUtil.fromSqlException(e); } - return new QueryResult(/*affectedRows=*/ -1, reader); + return new QueryResult(/* affectedRows */ -1, reader); } @Override diff --git a/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/StandardJdbcQuirks.java b/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/StandardJdbcQuirks.java index e87283cb4e..e345259ae4 100644 --- a/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/StandardJdbcQuirks.java +++ b/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/StandardJdbcQuirks.java @@ -16,17 +16,15 @@ */ package org.apache.arrow.adbc.driver.jdbc; -import java.sql.Types; -import org.apache.arrow.adapter.jdbc.JdbcToArrowUtils; -import org.apache.arrow.adbc.driver.jdbc.adapter.JdbcFieldInfoExtra; +import org.apache.arrow.adbc.driver.jdbc.adapter.JdbcToArrowTypeConverters; import org.apache.arrow.adbc.sql.SqlQuirks; -import org.apache.arrow.vector.types.TimeUnit; -import org.apache.arrow.vector.types.Types.MinorType; import org.apache.arrow.vector.types.pojo.ArrowType; public final class StandardJdbcQuirks { public static final JdbcQuirks MS_SQL_SERVER = - JdbcQuirks.builder("Microsoft SQL Server").typeConverter(StandardJdbcQuirks::mssql).build(); + JdbcQuirks.builder("Microsoft SQL Server") + .typeConverter(JdbcToArrowTypeConverters.MICROSOFT_SQL_SERVER) + .build(); public static final JdbcQuirks POSTGRESQL = JdbcQuirks.builder("PostgreSQL") .sqlQuirks( @@ -40,41 +38,7 @@ public final class StandardJdbcQuirks { arrowType); })) .build()) - .typeConverter(StandardJdbcQuirks::postgresql) + .typeConverter(JdbcToArrowTypeConverters.POSTGRESQL) .build(); private static final int MS_SQL_TYPE_DATETIMEOFFSET = -155; - - private static ArrowType mssql(JdbcFieldInfoExtra field) { - switch (field.getJdbcType()) { - case Types.TIME: - return MinorType.TIMENANO.getType(); - case Types.TIMESTAMP: - // DATETIME2 - // Precision is "100 nanoseconds" -> TimeUnit is NANOSECOND - return MinorType.TIMESTAMPNANO.getType(); - case MS_SQL_TYPE_DATETIMEOFFSET: - // DATETIMEOFFSET - // Precision is "100 nanoseconds" -> TimeUnit is NANOSECOND - return new ArrowType.Timestamp(TimeUnit.NANOSECOND, "UTC"); - default: - return JdbcToArrowUtils.getArrowTypeFromJdbcType(field.getFieldInfo(), /*calendar*/ null); - } - } - - private static ArrowType postgresql(JdbcFieldInfoExtra field) { - switch (field.getJdbcType()) { - case Types.TIME: - return MinorType.TIMEMICRO.getType(); - case Types.TIMESTAMP: - if ("timestamptz".equals(field.getTypeName())) { - return new ArrowType.Timestamp(TimeUnit.MICROSECOND, "UTC"); - } else if ("timestamp".equals(field.getTypeName())) { - return MinorType.TIMESTAMPMICRO.getType(); - } - // Unknown type - return null; - default: - return JdbcToArrowUtils.getArrowTypeFromJdbcType(field.getFieldInfo(), /*calendar*/ null); - } - } } diff --git a/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/UrlDataSource.java b/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/UrlDataSource.java index f74b97f448..51266ca006 100644 --- a/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/UrlDataSource.java +++ b/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/UrlDataSource.java @@ -25,15 +25,17 @@ import java.util.Objects; import java.util.logging.Logger; import javax.sql.DataSource; +import org.checkerframework.checker.nullness.qual.Nullable; /** Adapt a JDBC URL to the DataSource interface. */ class UrlDataSource implements DataSource { final String target; - PrintWriter logWriter; + @Nullable PrintWriter logWriter; int loginTimeout; UrlDataSource(String target) { this.target = Objects.requireNonNull(target); + this.logWriter = null; } @Override @@ -47,7 +49,8 @@ public Connection getConnection(String username, String password) throws SQLExce } @Override - public PrintWriter getLogWriter() throws SQLException { + @SuppressWarnings("override.return") + public @Nullable PrintWriter getLogWriter() throws SQLException { return logWriter; } diff --git a/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/adapter/JdbcToArrowTypeConverters.java b/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/adapter/JdbcToArrowTypeConverters.java index 928bfdf8ee..5640a3d459 100644 --- a/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/adapter/JdbcToArrowTypeConverters.java +++ b/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/adapter/JdbcToArrowTypeConverters.java @@ -58,7 +58,8 @@ private static ArrowType postgresql(JdbcFieldInfoExtra field) { unit = TimeUnit.NANOSECOND; } else { // Negative precision? - return null; + throw new UnsupportedOperationException( + "Cannot convert type to Arrow Timestamp (precision is negative)"); } if ("timestamptz".equals(field.getTypeName())) { return new ArrowType.Timestamp(unit, "UTC"); @@ -66,7 +67,7 @@ private static ArrowType postgresql(JdbcFieldInfoExtra field) { return new ArrowType.Timestamp(unit, /*timezone*/ null); } // Unknown type - return null; + throw new UnsupportedOperationException("Cannot convert type to Arrow Timestamp"); } default: return JdbcToArrowUtils.getArrowTypeFromJdbcType(field.getFieldInfo(), /*calendar*/ null); diff --git a/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractConnectionMetadataTest.java b/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractConnectionMetadataTest.java index 6a5fc9053d..f31588e68c 100644 --- a/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractConnectionMetadataTest.java +++ b/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractConnectionMetadataTest.java @@ -323,8 +323,7 @@ public void getTableSchema() throws Exception { final Schema schema = new Schema( Arrays.asList( - Field.nullable( - quirks.caseFoldColumnName("INTS"), new ArrowType.Int(32, /*signed=*/ true)), + Field.nullable(quirks.caseFoldColumnName("INTS"), new ArrowType.Int(32, true)), Field.nullable(quirks.caseFoldColumnName("STRS"), new ArrowType.Utf8()))); try (final VectorSchemaRoot root = VectorSchemaRoot.create(schema, allocator); final AdbcStatement stmt = connection.bulkIngest(tableName, BulkIngestMode.CREATE)) { diff --git a/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractTransactionTest.java b/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractTransactionTest.java index 29265ba7e3..e6aaa6e6c4 100644 --- a/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractTransactionTest.java +++ b/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractTransactionTest.java @@ -85,9 +85,7 @@ void toggleAutoCommit() throws Exception { @Test void rollback() throws Exception { final Schema schema = - new Schema( - Collections.singletonList( - Field.nullable("ints", new ArrowType.Int(32, /*signed=*/ true)))); + new Schema(Collections.singletonList(Field.nullable("ints", new ArrowType.Int(32, true)))); connection.setAutoCommit(false); try (VectorSchemaRoot root = VectorSchemaRoot.create(schema, allocator)) { @@ -116,9 +114,7 @@ void rollback() throws Exception { @Test void commit() throws Exception { final Schema schema = - new Schema( - Collections.singletonList( - Field.nullable("ints", new ArrowType.Int(32, /*signed=*/ true)))); + new Schema(Collections.singletonList(Field.nullable("ints", new ArrowType.Int(32, true)))); final String tableName = quirks.caseFoldTableName("temptable"); connection.setAutoCommit(false); @@ -149,9 +145,7 @@ void commit() throws Exception { @Test void enableAutoCommitAlsoCommits() throws Exception { final Schema schema = - new Schema( - Collections.singletonList( - Field.nullable("ints", new ArrowType.Int(32, /*signed=*/ true)))); + new Schema(Collections.singletonList(Field.nullable("ints", new ArrowType.Int(32, true)))); final String tableName = quirks.caseFoldTableName("temptable"); connection.setAutoCommit(false); diff --git a/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/SqlTestUtil.java b/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/SqlTestUtil.java index 814d0a81c4..c0536e5cf5 100644 --- a/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/SqlTestUtil.java +++ b/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/SqlTestUtil.java @@ -79,8 +79,7 @@ public Schema ingestTableWithConstraints( final Schema schema = new Schema( Arrays.asList( - Field.notNullable( - quirks.caseFoldColumnName("INTS"), new ArrowType.Int(32, /*signed=*/ true)), + Field.notNullable(quirks.caseFoldColumnName("INTS"), new ArrowType.Int(32, true)), Field.nullable(quirks.caseFoldColumnName("INTS2"), new ArrowType.Int(32, true)))); try (final VectorSchemaRoot root = VectorSchemaRoot.create(schema, allocator)) { final IntVector ints = (IntVector) root.getVector(0); @@ -133,8 +132,7 @@ public void ingestTablesWithReferentialConstraint( new Schema( Collections.singletonList( Field.notNullable( - quirks.caseFoldColumnName("PRODUCT_ID"), - new ArrowType.Int(32, /*signed=*/ true)))); + quirks.caseFoldColumnName("PRODUCT_ID"), new ArrowType.Int(32, true)))); final Schema dependentSchema = new Schema( diff --git a/java/pom.xml b/java/pom.xml index a632959f51..6ad153c1d1 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -153,6 +153,13 @@ ${adbc.version} + + + org.checkerframework + checker-qual + 3.42.0 + + org.assertj @@ -297,15 +304,26 @@ org.apache.maven.plugins maven-compiler-plugin - 3.10.1 + 3.11.0 1.8 1.8 - + UTF-8 + -XDcompilePolicy=simple -Xplugin:ErrorProne -Xep:NullAway:ERROR -XepOpt:NullAway:AnnotatedPackages=com.uber + + -Xmaxerrs + 10000 + -Xmaxwarns + 10000 + -AskipDefs=.*Test + -AatfDoNotCache + -AprintVerboseGenerics + -AprintAllQualifiers + -Astubs=.checker-framework/:stubs - + com.google.errorprone error_prone_core @@ -316,7 +334,15 @@ nullaway 0.10.10 + + org.checkerframework + checker + 3.42.0 + + + org.checkerframework.checker.nullness.NullnessChecker + diff --git a/java/sql/pom.xml b/java/sql/pom.xml index 9894210994..37904359b7 100644 --- a/java/sql/pom.xml +++ b/java/sql/pom.xml @@ -28,6 +28,12 @@ arrow-vector + + + org.checkerframework + checker-qual + + org.assertj diff --git a/java/sql/src/main/java/org/apache/arrow/adbc/sql/SqlQuirks.java b/java/sql/src/main/java/org/apache/arrow/adbc/sql/SqlQuirks.java index 007f699c0f..fc86435474 100644 --- a/java/sql/src/main/java/org/apache/arrow/adbc/sql/SqlQuirks.java +++ b/java/sql/src/main/java/org/apache/arrow/adbc/sql/SqlQuirks.java @@ -19,51 +19,53 @@ import java.util.function.Function; import org.apache.arrow.vector.types.pojo.ArrowType; +import org.checkerframework.checker.nullness.qual.Nullable; /** Parameters to pass to SQL-based drivers to account for driver/vendor-specific SQL quirks. */ public final class SqlQuirks { - public static final Function DEFAULT_ARROW_TYPE_TO_SQL_TYPE_NAME_MAPPING = - (arrowType) -> { - switch (arrowType.getTypeID()) { - case Null: - case Struct: - case List: - case LargeList: - case FixedSizeList: - case Union: - case Map: - return null; - case Int: - // TODO: - return "INT"; - case FloatingPoint: - return null; - case Utf8: - return "CLOB"; - case LargeUtf8: - case Binary: - case LargeBinary: - case FixedSizeBinary: - case Bool: - case Decimal: - case Date: - case Time: - case Timestamp: - case Interval: - case Duration: - case NONE: - default: - return null; - } - }; - Function arrowToSqlTypeNameMapping; + public static final Function + DEFAULT_ARROW_TYPE_TO_SQL_TYPE_NAME_MAPPING = + (arrowType) -> { + switch (arrowType.getTypeID()) { + case Null: + case Struct: + case List: + case LargeList: + case FixedSizeList: + case Union: + case Map: + return null; + case Int: + // TODO: + return "INT"; + case FloatingPoint: + return null; + case Utf8: + return "CLOB"; + case LargeUtf8: + case Binary: + case LargeBinary: + case FixedSizeBinary: + case Bool: + case Decimal: + case Date: + case Time: + case Timestamp: + case Interval: + case Duration: + case NONE: + default: + return null; + } + }; + Function arrowToSqlTypeNameMapping; public SqlQuirks() { this.arrowToSqlTypeNameMapping = DEFAULT_ARROW_TYPE_TO_SQL_TYPE_NAME_MAPPING; } /** The mapping from Arrow type to SQL type name, used to build queries. */ - public Function getArrowToSqlTypeNameMapping() { + public Function getArrowToSqlTypeNameMapping() { return arrowToSqlTypeNameMapping; } @@ -73,9 +75,10 @@ public static Builder builder() { } public static final class Builder { - Function arrowToSqlTypeNameMapping; + @Nullable Function arrowToSqlTypeNameMapping; - public Builder arrowToSqlTypeNameMapping(Function mapper) { + public Builder arrowToSqlTypeNameMapping( + @Nullable Function mapper) { this.arrowToSqlTypeNameMapping = mapper; return this; }