This is an automated email from the ASF dual-hosted git repository.
lidavidm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-adbc.git
The following commit(s) were added to refs/heads/main by this push:
new 2485d7c [Java] Implement prepared statements, refactor for
consistency (#40)
2485d7c is described below
commit 2485d7c3da217a7190f86128d769a7d0445755ab
Author: David Li <[email protected]>
AuthorDate: Fri Jul 15 14:01:19 2022 -0400
[Java] Implement prepared statements, refactor for consistency (#40)
* Implement prepared statements for Java
* Split out database-specific validation tests
---
.../org/apache/arrow/adbc/core/AdbcConnection.java | 43 +++----
.../org/apache/arrow/adbc/core/AdbcDriver.java | 2 +-
.../org/apache/arrow/adbc/core/AdbcException.java | 24 ++--
.../org/apache/arrow/adbc/core/AdbcStatement.java | 24 ++--
.../org/apache/arrow/adbc/core/AdbcStatusCode.java | 18 ++-
.../adbc/drivermanager/AdbcDriverManager.java | 5 +-
java/driver/jdbc-util/pom.xml | 2 +-
.../driver/{jdbc => jdbc-validation-derby}/pom.xml | 30 ++---
.../jdbc/derby/DerbyConnectionMetadataTest.java} | 19 ++-
.../driver/jdbc/derby/DerbyConnectionTest.java} | 19 ++-
.../arrow/adbc/driver/jdbc/derby/DerbyQuirks.java | 65 ++++++++++
.../driver/jdbc/derby/DerbyStatementTest.java} | 19 ++-
.../driver/jdbc/derby/DerbyTransactionTest.java} | 19 ++-
.../pom.xml | 26 ++--
.../JdbcPostgresqlConnectionMetadataTest.java} | 18 ++-
.../postgresql/JdbcPostgresqlConnectionTest.java} | 18 ++-
.../postgresql/JdbcPostgresqlStatementTest.java} | 18 ++-
.../postgresql/JdbcPostgresqlTransactionTest.java} | 18 ++-
.../driver/jdbc/postgresql/PostgresqlQuirks.java | 90 ++++++++++++++
java/driver/jdbc/pom.xml | 2 +-
.../arrow/adbc/driver/jdbc/FixedJdbcStatement.java | 4 +-
.../arrow/adbc/driver/jdbc/JdbcBindReader.java | 108 +++++++++++++++++
.../arrow/adbc/driver/jdbc/JdbcConnection.java | 12 +-
.../arrow/adbc/driver/jdbc/JdbcDatabase.java | 8 +-
.../apache/arrow/adbc/driver/jdbc/JdbcDriver.java | 17 ++-
.../arrow/adbc/driver/jdbc/JdbcDriverQuirks.java | 91 ++++++++++++++
.../arrow/adbc/driver/jdbc/JdbcDriverUtil.java | 28 ++++-
.../arrow/adbc/driver/jdbc/JdbcStatement.java | 132 ++++++++++-----------
java/driver/{testsuite => validation}/pom.xml | 4 +-
.../testsuite/AbstractConnectionMetadataTest.java | 76 +++++-------
.../driver/testsuite/AbstractConnectionTest.java | 8 +-
.../driver/testsuite/AbstractStatementTest.java | 128 ++++++++++++++------
.../driver/testsuite/AbstractTransactionTest.java | 37 ++++--
.../adbc/driver/testsuite/ArrowAssertions.java | 27 +++++
.../arrow/adbc/driver/testsuite/SqlTestUtil.java | 72 +++++++++++
.../driver/testsuite/SqlValidationQuirks.java} | 27 +++--
java/pom.xml | 6 +-
37 files changed, 896 insertions(+), 368 deletions(-)
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 5f11a88..00002a8 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
@@ -16,20 +16,15 @@
*/
package org.apache.arrow.adbc.core;
+import java.nio.ByteBuffer;
import org.apache.arrow.vector.VectorSchemaRoot;
import org.apache.arrow.vector.types.pojo.Schema;
/** A connection to a {@link AdbcDatabase}. */
public interface AdbcConnection extends AutoCloseable {
- /**
- * Commit the pending transaction.
- *
- * @throws AdbcException if a database error occurs
- * @throws IllegalStateException if autocommit is enabled
- * @throws UnsupportedOperationException if the database does not support
transactions
- */
+ /** Commit the pending transaction. */
default void commit() throws AdbcException {
- throw new UnsupportedOperationException("Connection does not support
transactions");
+ throw AdbcException.notImplemented("Connection does not support
transactions");
}
/** Create a new statement that can be executed. */
@@ -43,10 +38,22 @@ public interface AdbcConnection extends AutoCloseable {
*/
default AdbcStatement bulkIngest(String targetTableName, BulkIngestMode mode)
throws AdbcException {
- throw new UnsupportedOperationException(
+ throw AdbcException.notImplemented(
"Connection does not support bulkIngest(String, BulkIngestMode)");
}
+ /**
+ * Create a statement from a serialized PartitionDescriptor.
+ *
+ * @param descriptor The descriptor to load ({@link
PartitionDescriptor#getDescriptor()}.
+ * @return A statement that can be immediately executed.
+ * @see AdbcStatement#getPartitionDescriptors()
+ */
+ default AdbcStatement deserializePartitionDescriptor(ByteBuffer descriptor)
throws AdbcException {
+ throw AdbcException.notImplemented(
+ "Connection does not support
deserializePartitionDescriptor(ByteBuffer)");
+ }
+
/**
* Get a hierarchical view of all catalogs, database schemas, tables, and
columns.
*
@@ -160,7 +167,7 @@ public interface AdbcConnection extends AutoCloseable {
String[] tableTypes,
String columnNamePattern)
throws AdbcException {
- throw new UnsupportedOperationException(
+ throw AdbcException.notImplemented(
"Connection does not support getTableSchema(String, String, String)");
}
@@ -189,7 +196,7 @@ public interface AdbcConnection extends AutoCloseable {
*/
default Schema getTableSchema(String catalog, String dbSchema, String
tableName)
throws AdbcException {
- throw new UnsupportedOperationException(
+ throw AdbcException.notImplemented(
"Connection does not support getTableSchema(String, String, String)");
}
@@ -210,18 +217,16 @@ public interface AdbcConnection extends AutoCloseable {
* </table>
*/
default AdbcStatement getTableTypes() throws AdbcException {
- throw new UnsupportedOperationException("Connection does not support
getTableTypes()");
+ throw AdbcException.notImplemented("Connection does not support
getTableTypes()");
}
/**
* Rollback the pending transaction.
*
* @throws AdbcException if a database error occurs
- * @throws IllegalStateException if autocommit is enabled
- * @throws UnsupportedOperationException if the database does not support
transactions
*/
default void rollback() throws AdbcException {
- throw new UnsupportedOperationException("Connection does not support
transactions");
+ throw AdbcException.notImplemented("Connection does not support
transactions");
}
/**
@@ -233,12 +238,8 @@ public interface AdbcConnection extends AutoCloseable {
return true;
}
- /**
- * Toggle whether autocommit is enabled.
- *
- * @throws UnsupportedOperationException if the database does not support
toggling autocommit
- */
+ /** Toggle whether autocommit is enabled. */
default void setAutoCommit(boolean enableAutoCommit) throws AdbcException {
- throw new UnsupportedOperationException("Connection does not support
transactions");
+ throw AdbcException.notImplemented("Connection does not support
transactions");
}
}
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 fc3f5e4..8b71f95 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
@@ -26,5 +26,5 @@ public interface AdbcDriver {
*
* @param parameters Driver-specific parameters.
*/
- AdbcDatabase open(Map<String, String> parameters) throws AdbcException;
+ AdbcDatabase open(Map<String, Object> parameters) throws AdbcException;
}
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 af412af..66de077 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
@@ -28,15 +28,6 @@ package org.apache.arrow.adbc.core;
* <li>A SQLSTATE string
* <li>A vendor-specific status code
* </ul>
- *
- * Driver implementations should also use the following standard exception
classes to indicate
- * invalid API usage:
- *
- * <ul>
- * <li>{@link IllegalArgumentException} for invalid argument values
- * <li>{@link UnsupportedOperationException} for unimplemented operations
- * <li>{@link IllegalStateException} for other invalid use of the API (e.g.
preconditions not met)
- * </ul>
*/
public class AdbcException extends Exception {
private final AdbcStatusCode status;
@@ -52,6 +43,21 @@ public class AdbcException extends Exception {
this.vendorCode = vendorCode;
}
+ /** Create a new exception with code {@link
AdbcStatusCode#INVALID_ARGUMENT}. */
+ public static AdbcException invalidArgument(String message) {
+ return new AdbcException(message, /*cause*/ null,
AdbcStatusCode.INVALID_ARGUMENT, null, 0);
+ }
+
+ /** Create a new exception with code {@link AdbcStatusCode#INVALID_STATE}. */
+ public static AdbcException invalidState(String message) {
+ return new AdbcException(message, /*cause*/ null,
AdbcStatusCode.INVALID_STATE, null, 0);
+ }
+
+ /** Create a new exception with code {@link AdbcStatusCode#NOT_IMPLEMENTED}.
*/
+ public static AdbcException notImplemented(String message) {
+ return new AdbcException(message, /*cause*/ null,
AdbcStatusCode.NOT_IMPLEMENTED, null, 0);
+ }
+
public AdbcStatusCode getStatus() {
return status;
}
diff --git
a/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcStatement.java
b/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcStatement.java
index 7b62ef0..b621587 100644
--- a/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcStatement.java
+++ b/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcStatement.java
@@ -25,8 +25,8 @@ import org.apache.arrow.vector.ipc.ArrowReader;
public interface AdbcStatement extends AutoCloseable {
/** Set a generic query option. */
- default void setOption(String key, Object value) {
- throw new UnsupportedOperationException("Unsupported option " + key);
+ default void setOption(String key, Object value) throws AdbcException {
+ throw AdbcException.notImplemented("Unsupported option " + key);
}
/**
@@ -34,8 +34,8 @@ public interface AdbcStatement extends AutoCloseable {
*
* @param query The SQL query.
*/
- default void setSqlQuery(String query) {
- throw new UnsupportedOperationException("Statement does not support SQL
queries");
+ default void setSqlQuery(String query) throws AdbcException {
+ throw AdbcException.notImplemented("Statement does not support SQL
queries");
}
/**
@@ -43,13 +43,13 @@ public interface AdbcStatement extends AutoCloseable {
*
* @param plan The serialized Substrait plan.
*/
- default void setSubstraitPlan(ByteBuffer plan) {
- throw new UnsupportedOperationException("Statement does not support
Substrait plans");
+ default void setSubstraitPlan(ByteBuffer plan) throws AdbcException {
+ throw AdbcException.notImplemented("Statement does not support Substrait
plans");
}
/** Bind this statement to a VectorSchemaRoot to provide parameter
values/bulk data ingestion. */
- default void bind(VectorSchemaRoot root) {
- throw new UnsupportedOperationException("Statement does not support bind");
+ default void bind(VectorSchemaRoot root) throws AdbcException {
+ throw AdbcException.notImplemented("Statement does not support bind");
}
/**
@@ -70,7 +70,8 @@ public interface AdbcStatement extends AutoCloseable {
*
* <p>Must be called after {@link #execute()}.
*
- * @throws IllegalStateException if the statement has not been executed.
+ * @throws AdbcException with {@link AdbcStatusCode#INVALID_STATE} if the
statement has not been
+ * executed.
*/
ArrowReader getArrowReader() throws AdbcException;
@@ -81,10 +82,11 @@ public interface AdbcStatement extends AutoCloseable {
*
* <p>Must be called after {@link #execute()}.
*
- * @throws IllegalStateException if the statement has not been executed.
+ * @throws AdbcException with {@link AdbcStatusCode#INVALID_STATE} if the
statement has not been
+ * executed.
* @return The list of descriptors, or an empty list if unsupported.
*/
- default List<PartitionDescriptor> getPartitionDescriptors() {
+ default List<PartitionDescriptor> getPartitionDescriptors() throws
AdbcException {
return Collections.emptyList();
}
diff --git
a/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcStatusCode.java
b/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcStatusCode.java
index c97a384..7b8c634 100644
--- a/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcStatusCode.java
+++ b/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcStatusCode.java
@@ -19,13 +19,26 @@ package org.apache.arrow.adbc.core;
/**
* A status code indicating the general category of error that occurred.
*
- * <p>Also see the ADBC C API definition, which has similar status codes
(except here we use
- * standard Java exceptions to indicate API misuse).
+ * <p>Also see the ADBC C API definition, which has similar status codes.
*/
public enum AdbcStatusCode {
+ /**
+ * An unknown error occurred.
+ *
+ * <p>May indicate client-side or database-side error.
+ */
UNKNOWN,
+ /**
+ * The operation is not supported.
+ *
+ * <p>May indicate client-side or database-side error.
+ */
+ NOT_IMPLEMENTED,
+ /** */
NOT_FOUND,
ALREADY_EXISTS,
+ INVALID_ARGUMENT,
+ INVALID_STATE,
INVALID_DATA,
INTEGRITY,
INTERNAL,
@@ -34,4 +47,5 @@ public enum AdbcStatusCode {
TIMEOUT,
UNAUTHENTICATED,
UNAUTHORIZED,
+ ;
}
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 b197bf5..06cf5f9 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
@@ -42,7 +42,7 @@ public final class AdbcDriverManager {
* @return The AdbcDatabase instance.
* @throws AdbcException if the driver was not found or if connection fails.
*/
- public AdbcDatabase connect(String driverName, Map<String, String>
parameters)
+ public AdbcDatabase connect(String driverName, Map<String, Object>
parameters)
throws AdbcException {
final AdbcDriver driver = lookupDriver(driverName);
if (driver == null) {
@@ -64,7 +64,8 @@ public final class AdbcDriverManager {
public void registerDriver(String driverName, AdbcDriver driver) {
if (drivers.putIfAbsent(driverName, driver) != null) {
- throw new IllegalStateException("Driver already registered for '" +
driverName + "'");
+ throw new IllegalStateException(
+ "[DriverManager] Driver already registered for '" + driverName +
"'");
}
}
diff --git a/java/driver/jdbc-util/pom.xml b/java/driver/jdbc-util/pom.xml
index 44b5405..ea38d87 100644
--- a/java/driver/jdbc-util/pom.xml
+++ b/java/driver/jdbc-util/pom.xml
@@ -51,7 +51,7 @@
</dependency>
<dependency>
<groupId>org.apache.arrow.adbc</groupId>
- <artifactId>adbc-driver-testsuite</artifactId>
+ <artifactId>adbc-driver-validation</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
diff --git a/java/driver/jdbc/pom.xml
b/java/driver/jdbc-validation-derby/pom.xml
similarity index 75%
copy from java/driver/jdbc/pom.xml
copy to java/driver/jdbc-validation-derby/pom.xml
index 105ad4f..453c5d6 100644
--- a/java/driver/jdbc/pom.xml
+++ b/java/driver/jdbc-validation-derby/pom.xml
@@ -18,37 +18,21 @@
<relativePath>../../pom.xml</relativePath>
</parent>
- <artifactId>adbc-driver-jdbc</artifactId>
+ <artifactId>adbc-driver-jdbc-validation-derby</artifactId>
<packaging>jar</packaging>
- <name>Arrow ADBC Driver JDBC</name>
- <description>An ADBC driver wrapping the JDBC API.</description>
+ <name>Arrow ADBC Driver JDBC Validation with Derby</name>
+ <description>Tests validating the JDBC driver against Apache
Derby.</description>
<dependencies>
- <!-- Arrow -->
- <dependency>
- <groupId>org.apache.arrow</groupId>
- <artifactId>arrow-jdbc</artifactId>
- </dependency>
- <dependency>
- <groupId>org.apache.arrow</groupId>
- <artifactId>arrow-memory-core</artifactId>
- </dependency>
- <dependency>
- <groupId>org.apache.arrow</groupId>
- <artifactId>arrow-vector</artifactId>
- </dependency>
-
<dependency>
<groupId>org.apache.arrow.adbc</groupId>
<artifactId>adbc-core</artifactId>
+ <scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.arrow.adbc</groupId>
- <artifactId>adbc-driver-jdbc-util</artifactId>
- </dependency>
- <dependency>
- <groupId>org.apache.arrow.adbc</groupId>
- <artifactId>adbc-driver-manager</artifactId>
+ <artifactId>adbc-driver-jdbc</artifactId>
+ <scope>test</scope>
</dependency>
<!-- Derby -->
@@ -79,7 +63,7 @@
</dependency>
<dependency>
<groupId>org.apache.arrow.adbc</groupId>
- <artifactId>adbc-driver-testsuite</artifactId>
+ <artifactId>adbc-driver-validation</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
diff --git
a/java/driver/jdbc/src/test/java/org/apache/arrow/adbc/driver/jdbc/JdbcConnectionMetadataTest.java
b/java/driver/jdbc-validation-derby/src/test/java/org/apache/arrow/adbc/driver/jdbc/derby/DerbyConnectionMetadataTest.java
similarity index 63%
rename from
java/driver/jdbc/src/test/java/org/apache/arrow/adbc/driver/jdbc/JdbcConnectionMetadataTest.java
rename to
java/driver/jdbc-validation-derby/src/test/java/org/apache/arrow/adbc/driver/jdbc/derby/DerbyConnectionMetadataTest.java
index 9d8dca4..09eafcb 100644
---
a/java/driver/jdbc/src/test/java/org/apache/arrow/adbc/driver/jdbc/JdbcConnectionMetadataTest.java
+++
b/java/driver/jdbc-validation-derby/src/test/java/org/apache/arrow/adbc/driver/jdbc/derby/DerbyConnectionMetadataTest.java
@@ -14,23 +14,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.arrow.adbc.driver.jdbc;
+package org.apache.arrow.adbc.driver.jdbc.derby;
import java.nio.file.Path;
-import java.util.HashMap;
-import java.util.Map;
-import org.apache.arrow.adbc.core.AdbcDatabase;
-import org.apache.arrow.adbc.core.AdbcException;
import org.apache.arrow.adbc.driver.testsuite.AbstractConnectionMetadataTest;
+import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.io.TempDir;
-public class JdbcConnectionMetadataTest extends AbstractConnectionMetadataTest
{
- @TempDir Path tempDir;
+public class DerbyConnectionMetadataTest extends
AbstractConnectionMetadataTest {
+ @TempDir static Path tempDir;
- @Override
- protected AdbcDatabase init() throws AdbcException {
- final Map<String, String> parameters = new HashMap<>();
- parameters.put("path", tempDir.toString() + "/db;create=true");
- return JdbcDriver.INSTANCE.open(parameters);
+ @BeforeAll
+ static void beforeAll() {
+ quirks = new DerbyQuirks(tempDir);
}
}
diff --git
a/java/driver/jdbc/src/test/java/org/apache/arrow/adbc/driver/jdbc/JdbcConnectionTest.java
b/java/driver/jdbc-validation-derby/src/test/java/org/apache/arrow/adbc/driver/jdbc/derby/DerbyConnectionTest.java
similarity index 64%
rename from
java/driver/jdbc/src/test/java/org/apache/arrow/adbc/driver/jdbc/JdbcConnectionTest.java
rename to
java/driver/jdbc-validation-derby/src/test/java/org/apache/arrow/adbc/driver/jdbc/derby/DerbyConnectionTest.java
index 4fcf69e..f7065a2 100644
---
a/java/driver/jdbc/src/test/java/org/apache/arrow/adbc/driver/jdbc/JdbcConnectionTest.java
+++
b/java/driver/jdbc-validation-derby/src/test/java/org/apache/arrow/adbc/driver/jdbc/derby/DerbyConnectionTest.java
@@ -14,23 +14,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.arrow.adbc.driver.jdbc;
+package org.apache.arrow.adbc.driver.jdbc.derby;
import java.nio.file.Path;
-import java.util.HashMap;
-import java.util.Map;
-import org.apache.arrow.adbc.core.AdbcDatabase;
-import org.apache.arrow.adbc.core.AdbcException;
import org.apache.arrow.adbc.driver.testsuite.AbstractConnectionTest;
+import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.io.TempDir;
-public class JdbcConnectionTest extends AbstractConnectionTest {
- @TempDir Path tempDir;
+public class DerbyConnectionTest extends AbstractConnectionTest {
+ @TempDir static Path tempDir;
- @Override
- protected AdbcDatabase init() throws AdbcException {
- final Map<String, String> parameters = new HashMap<>();
- parameters.put("path", tempDir.toString() + "/db;create=true");
- return JdbcDriver.INSTANCE.open(parameters);
+ @BeforeAll
+ static void beforeAll() {
+ quirks = new DerbyQuirks(tempDir);
}
}
diff --git
a/java/driver/jdbc-validation-derby/src/test/java/org/apache/arrow/adbc/driver/jdbc/derby/DerbyQuirks.java
b/java/driver/jdbc-validation-derby/src/test/java/org/apache/arrow/adbc/driver/jdbc/derby/DerbyQuirks.java
new file mode 100644
index 0000000..877f512
--- /dev/null
+++
b/java/driver/jdbc-validation-derby/src/test/java/org/apache/arrow/adbc/driver/jdbc/derby/DerbyQuirks.java
@@ -0,0 +1,65 @@
+/*
+ * 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.adbc.driver.jdbc.derby;
+
+import java.nio.file.Path;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.arrow.adbc.core.AdbcDatabase;
+import org.apache.arrow.adbc.core.AdbcException;
+import org.apache.arrow.adbc.driver.jdbc.JdbcDriver;
+import org.apache.arrow.adbc.driver.testsuite.SqlValidationQuirks;
+
+public class DerbyQuirks extends SqlValidationQuirks {
+ private final String jdbcUrl;
+
+ public DerbyQuirks(Path databaseRoot) {
+ this.jdbcUrl = "jdbc:derby:" + databaseRoot.toString() + "/db;create=true";
+ }
+
+ @Override
+ public AdbcDatabase initDatabase() throws AdbcException {
+ final Map<String, Object> parameters = new HashMap<>();
+ parameters.put("adbc.jdbc.url", jdbcUrl);
+ return JdbcDriver.INSTANCE.open(parameters);
+ }
+
+ @Override
+ public void cleanupTable(String name) throws Exception {
+ try (final Connection connection1 = DriverManager.getConnection(jdbcUrl)) {
+ try (Statement statement = connection1.createStatement()) {
+ statement.execute("DROP TABLE " + name);
+ } catch (SQLException ignored) {
+ }
+ }
+ }
+
+ @Override
+ public String caseFoldTableName(String name) {
+ return name.toUpperCase();
+ }
+
+ @Override
+ public String caseFoldColumnName(String name) {
+ return name.toUpperCase();
+ }
+}
diff --git
a/java/driver/jdbc/src/test/java/org/apache/arrow/adbc/driver/jdbc/JdbcStatementTest.java
b/java/driver/jdbc-validation-derby/src/test/java/org/apache/arrow/adbc/driver/jdbc/derby/DerbyStatementTest.java
similarity index 64%
copy from
java/driver/jdbc/src/test/java/org/apache/arrow/adbc/driver/jdbc/JdbcStatementTest.java
copy to
java/driver/jdbc-validation-derby/src/test/java/org/apache/arrow/adbc/driver/jdbc/derby/DerbyStatementTest.java
index 72a28f3..0f71387 100644
---
a/java/driver/jdbc/src/test/java/org/apache/arrow/adbc/driver/jdbc/JdbcStatementTest.java
+++
b/java/driver/jdbc-validation-derby/src/test/java/org/apache/arrow/adbc/driver/jdbc/derby/DerbyStatementTest.java
@@ -15,23 +15,18 @@
* limitations under the License.
*/
-package org.apache.arrow.adbc.driver.jdbc;
+package org.apache.arrow.adbc.driver.jdbc.derby;
import java.nio.file.Path;
-import java.util.HashMap;
-import java.util.Map;
-import org.apache.arrow.adbc.core.AdbcDatabase;
-import org.apache.arrow.adbc.core.AdbcException;
import org.apache.arrow.adbc.driver.testsuite.AbstractStatementTest;
+import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.io.TempDir;
-class JdbcStatementTest extends AbstractStatementTest {
- @TempDir Path tempDir;
+class DerbyStatementTest extends AbstractStatementTest {
+ @TempDir static Path tempDir;
- @Override
- protected AdbcDatabase init() throws AdbcException {
- final Map<String, String> parameters = new HashMap<>();
- parameters.put("path", tempDir.toString() + "/db;create=true");
- return JdbcDriver.INSTANCE.open(parameters);
+ @BeforeAll
+ static void beforeAll() {
+ quirks = new DerbyQuirks(tempDir);
}
}
diff --git
a/java/driver/jdbc/src/test/java/org/apache/arrow/adbc/driver/jdbc/JdbcTransactionTest.java
b/java/driver/jdbc-validation-derby/src/test/java/org/apache/arrow/adbc/driver/jdbc/derby/DerbyTransactionTest.java
similarity index 64%
rename from
java/driver/jdbc/src/test/java/org/apache/arrow/adbc/driver/jdbc/JdbcTransactionTest.java
rename to
java/driver/jdbc-validation-derby/src/test/java/org/apache/arrow/adbc/driver/jdbc/derby/DerbyTransactionTest.java
index e5bcaeb..39446a9 100644
---
a/java/driver/jdbc/src/test/java/org/apache/arrow/adbc/driver/jdbc/JdbcTransactionTest.java
+++
b/java/driver/jdbc-validation-derby/src/test/java/org/apache/arrow/adbc/driver/jdbc/derby/DerbyTransactionTest.java
@@ -14,23 +14,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.arrow.adbc.driver.jdbc;
+package org.apache.arrow.adbc.driver.jdbc.derby;
import java.nio.file.Path;
-import java.util.HashMap;
-import java.util.Map;
-import org.apache.arrow.adbc.core.AdbcDatabase;
-import org.apache.arrow.adbc.core.AdbcException;
import org.apache.arrow.adbc.driver.testsuite.AbstractTransactionTest;
+import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.io.TempDir;
-public class JdbcTransactionTest extends AbstractTransactionTest {
- @TempDir Path tempDir;
+public class DerbyTransactionTest extends AbstractTransactionTest {
+ @TempDir static Path tempDir;
- @Override
- protected AdbcDatabase init() throws AdbcException {
- final Map<String, String> parameters = new HashMap<>();
- parameters.put("path", tempDir.toString() + "/db;create=true");
- return JdbcDriver.INSTANCE.open(parameters);
+ @BeforeAll
+ static void beforeAll() {
+ quirks = new DerbyQuirks(tempDir);
}
}
diff --git a/java/driver/jdbc-util/pom.xml
b/java/driver/jdbc-validation-postgresql/pom.xml
similarity index 74%
copy from java/driver/jdbc-util/pom.xml
copy to java/driver/jdbc-validation-postgresql/pom.xml
index 44b5405..9c5c209 100644
--- a/java/driver/jdbc-util/pom.xml
+++ b/java/driver/jdbc-validation-postgresql/pom.xml
@@ -18,24 +18,28 @@
<relativePath>../../pom.xml</relativePath>
</parent>
- <artifactId>adbc-driver-jdbc-util</artifactId>
+ <artifactId>adbc-driver-jdbc-validation-postgresql</artifactId>
<packaging>jar</packaging>
- <name>Arrow ADBC Driver JDBC Util</name>
- <description>Utilities for working with Arrow and JDBC.</description>
+ <name>Arrow ADBC Driver JDBC Validation with PostgreSQL</name>
+ <description>Tests validating the JDBC driver against
Postgresql.</description>
<dependencies>
- <!-- Arrow -->
<dependency>
- <groupId>org.apache.arrow</groupId>
- <artifactId>arrow-jdbc</artifactId>
+ <groupId>org.apache.arrow.adbc</groupId>
+ <artifactId>adbc-core</artifactId>
+ <scope>test</scope>
</dependency>
<dependency>
- <groupId>org.apache.arrow</groupId>
- <artifactId>arrow-memory-core</artifactId>
+ <groupId>org.apache.arrow.adbc</groupId>
+ <artifactId>adbc-driver-jdbc</artifactId>
+ <scope>test</scope>
</dependency>
+
<dependency>
- <groupId>org.apache.arrow</groupId>
- <artifactId>arrow-vector</artifactId>
+ <groupId>org.postgresql</groupId>
+ <artifactId>postgresql</artifactId>
+ <version>42.4.0</version>
+ <scope>test</scope>
</dependency>
<!-- Testing -->
@@ -51,7 +55,7 @@
</dependency>
<dependency>
<groupId>org.apache.arrow.adbc</groupId>
- <artifactId>adbc-driver-testsuite</artifactId>
+ <artifactId>adbc-driver-validation</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
diff --git a/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcDriver.java
b/java/driver/jdbc-validation-postgresql/src/test/java/org/apache/arrow/adbc/driver/jdbc/postgresql/JdbcPostgresqlConnectionMetadataTest.java
similarity index 69%
copy from java/core/src/main/java/org/apache/arrow/adbc/core/AdbcDriver.java
copy to
java/driver/jdbc-validation-postgresql/src/test/java/org/apache/arrow/adbc/driver/jdbc/postgresql/JdbcPostgresqlConnectionMetadataTest.java
index fc3f5e4..a34ced5 100644
--- a/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcDriver.java
+++
b/java/driver/jdbc-validation-postgresql/src/test/java/org/apache/arrow/adbc/driver/jdbc/postgresql/JdbcPostgresqlConnectionMetadataTest.java
@@ -15,16 +15,14 @@
* limitations under the License.
*/
-package org.apache.arrow.adbc.core;
+package org.apache.arrow.adbc.driver.jdbc.postgresql;
-import java.util.Map;
+import org.apache.arrow.adbc.driver.testsuite.AbstractConnectionMetadataTest;
+import org.junit.jupiter.api.BeforeAll;
-/** A handle to an ADBC database driver. */
-public interface AdbcDriver {
- /**
- * Open a database via this driver.
- *
- * @param parameters Driver-specific parameters.
- */
- AdbcDatabase open(Map<String, String> parameters) throws AdbcException;
+public class JdbcPostgresqlConnectionMetadataTest extends
AbstractConnectionMetadataTest {
+ @BeforeAll
+ public static void beforeAll() {
+ quirks = new PostgresqlQuirks();
+ }
}
diff --git a/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcDriver.java
b/java/driver/jdbc-validation-postgresql/src/test/java/org/apache/arrow/adbc/driver/jdbc/postgresql/JdbcPostgresqlConnectionTest.java
similarity index 71%
copy from java/core/src/main/java/org/apache/arrow/adbc/core/AdbcDriver.java
copy to
java/driver/jdbc-validation-postgresql/src/test/java/org/apache/arrow/adbc/driver/jdbc/postgresql/JdbcPostgresqlConnectionTest.java
index fc3f5e4..4ca7fe3 100644
--- a/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcDriver.java
+++
b/java/driver/jdbc-validation-postgresql/src/test/java/org/apache/arrow/adbc/driver/jdbc/postgresql/JdbcPostgresqlConnectionTest.java
@@ -15,16 +15,14 @@
* limitations under the License.
*/
-package org.apache.arrow.adbc.core;
+package org.apache.arrow.adbc.driver.jdbc.postgresql;
-import java.util.Map;
+import org.apache.arrow.adbc.driver.testsuite.AbstractConnectionTest;
+import org.junit.jupiter.api.BeforeAll;
-/** A handle to an ADBC database driver. */
-public interface AdbcDriver {
- /**
- * Open a database via this driver.
- *
- * @param parameters Driver-specific parameters.
- */
- AdbcDatabase open(Map<String, String> parameters) throws AdbcException;
+public class JdbcPostgresqlConnectionTest extends AbstractConnectionTest {
+ @BeforeAll
+ public static void beforeAll() {
+ quirks = new PostgresqlQuirks();
+ }
}
diff --git a/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcDriver.java
b/java/driver/jdbc-validation-postgresql/src/test/java/org/apache/arrow/adbc/driver/jdbc/postgresql/JdbcPostgresqlStatementTest.java
similarity index 71%
copy from java/core/src/main/java/org/apache/arrow/adbc/core/AdbcDriver.java
copy to
java/driver/jdbc-validation-postgresql/src/test/java/org/apache/arrow/adbc/driver/jdbc/postgresql/JdbcPostgresqlStatementTest.java
index fc3f5e4..598475d 100644
--- a/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcDriver.java
+++
b/java/driver/jdbc-validation-postgresql/src/test/java/org/apache/arrow/adbc/driver/jdbc/postgresql/JdbcPostgresqlStatementTest.java
@@ -15,16 +15,14 @@
* limitations under the License.
*/
-package org.apache.arrow.adbc.core;
+package org.apache.arrow.adbc.driver.jdbc.postgresql;
-import java.util.Map;
+import org.apache.arrow.adbc.driver.testsuite.AbstractStatementTest;
+import org.junit.jupiter.api.BeforeAll;
-/** A handle to an ADBC database driver. */
-public interface AdbcDriver {
- /**
- * Open a database via this driver.
- *
- * @param parameters Driver-specific parameters.
- */
- AdbcDatabase open(Map<String, String> parameters) throws AdbcException;
+class JdbcPostgresqlStatementTest extends AbstractStatementTest {
+ @BeforeAll
+ public static void beforeAll() {
+ quirks = new PostgresqlQuirks();
+ }
}
diff --git a/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcDriver.java
b/java/driver/jdbc-validation-postgresql/src/test/java/org/apache/arrow/adbc/driver/jdbc/postgresql/JdbcPostgresqlTransactionTest.java
similarity index 70%
copy from java/core/src/main/java/org/apache/arrow/adbc/core/AdbcDriver.java
copy to
java/driver/jdbc-validation-postgresql/src/test/java/org/apache/arrow/adbc/driver/jdbc/postgresql/JdbcPostgresqlTransactionTest.java
index fc3f5e4..7c509fe 100644
--- a/java/core/src/main/java/org/apache/arrow/adbc/core/AdbcDriver.java
+++
b/java/driver/jdbc-validation-postgresql/src/test/java/org/apache/arrow/adbc/driver/jdbc/postgresql/JdbcPostgresqlTransactionTest.java
@@ -15,16 +15,14 @@
* limitations under the License.
*/
-package org.apache.arrow.adbc.core;
+package org.apache.arrow.adbc.driver.jdbc.postgresql;
-import java.util.Map;
+import org.apache.arrow.adbc.driver.testsuite.AbstractTransactionTest;
+import org.junit.jupiter.api.BeforeAll;
-/** A handle to an ADBC database driver. */
-public interface AdbcDriver {
- /**
- * Open a database via this driver.
- *
- * @param parameters Driver-specific parameters.
- */
- AdbcDatabase open(Map<String, String> parameters) throws AdbcException;
+public class JdbcPostgresqlTransactionTest extends AbstractTransactionTest {
+ @BeforeAll
+ public static void beforeAll() {
+ quirks = new PostgresqlQuirks();
+ }
}
diff --git
a/java/driver/jdbc-validation-postgresql/src/test/java/org/apache/arrow/adbc/driver/jdbc/postgresql/PostgresqlQuirks.java
b/java/driver/jdbc-validation-postgresql/src/test/java/org/apache/arrow/adbc/driver/jdbc/postgresql/PostgresqlQuirks.java
new file mode 100644
index 0000000..a0a1885
--- /dev/null
+++
b/java/driver/jdbc-validation-postgresql/src/test/java/org/apache/arrow/adbc/driver/jdbc/postgresql/PostgresqlQuirks.java
@@ -0,0 +1,90 @@
+/*
+ * 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.adbc.driver.jdbc.postgresql;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.arrow.adbc.core.AdbcDatabase;
+import org.apache.arrow.adbc.core.AdbcException;
+import org.apache.arrow.adbc.driver.jdbc.JdbcDriver;
+import org.apache.arrow.adbc.driver.jdbc.JdbcDriverQuirks;
+import org.apache.arrow.adbc.driver.testsuite.SqlValidationQuirks;
+import org.apache.arrow.vector.types.pojo.ArrowType;
+import org.junit.jupiter.api.Assumptions;
+
+public class PostgresqlQuirks extends SqlValidationQuirks {
+ static final String POSTGRESQL_URL_ENV_VAR = "ADBC_JDBC_POSTGRESQL_URL";
+ static final String POSTGRESQL_USER_ENV_VAR = "ADBC_JDBC_POSTGRESQL_USER";
+ static final String POSTGRESQL_PASSWORD_ENV_VAR =
"ADBC_JDBC_POSTGRESQL_PASSWORD";
+
+ static String makeJdbcUrl() {
+ final String postgresUrl = System.getenv(POSTGRESQL_URL_ENV_VAR);
+ final String user = System.getenv(POSTGRESQL_USER_ENV_VAR);
+ final String password = System.getenv(POSTGRESQL_PASSWORD_ENV_VAR);
+ Assumptions.assumeFalse(
+ postgresUrl == null, "Postgres not found, set " +
POSTGRESQL_URL_ENV_VAR);
+ Assumptions.assumeFalse(
+ postgresUrl.isEmpty(), "Postgres not found, set " +
POSTGRESQL_URL_ENV_VAR);
+ return String.format("jdbc:postgresql://%s?user=%s&password=%s",
postgresUrl, user, password);
+ }
+
+ @Override
+ public AdbcDatabase initDatabase() throws AdbcException {
+ String url = makeJdbcUrl();
+
+ final Map<String, Object> parameters = new HashMap<>();
+ parameters.put("adbc.jdbc.url", url);
+ parameters.put(
+ "adbc.jdbc.quirks",
+ JdbcDriverQuirks.builder()
+ .arrowToSqlTypeNameMapping(
+ (arrowType -> {
+ if (arrowType.getTypeID() == ArrowType.ArrowTypeID.Utf8) {
+ return "TEXT";
+ }
+ return
JdbcDriverQuirks.DEFAULT_ARROW_TYPE_TO_SQL_TYPE_NAME_MAPPING.apply(
+ arrowType);
+ }))
+ .build());
+ return JdbcDriver.INSTANCE.open(parameters);
+ }
+
+ @Override
+ public void cleanupTable(String name) throws Exception {
+ try (final Connection connection1 =
DriverManager.getConnection(makeJdbcUrl())) {
+ try (Statement statement = connection1.createStatement()) {
+ statement.execute("DROP TABLE " + name);
+ } catch (SQLException ignored) {
+ }
+ }
+ }
+
+ @Override
+ public String caseFoldTableName(String name) {
+ return name.toLowerCase();
+ }
+
+ @Override
+ public String caseFoldColumnName(String name) {
+ return name.toLowerCase();
+ }
+}
diff --git a/java/driver/jdbc/pom.xml b/java/driver/jdbc/pom.xml
index 105ad4f..0ea8d43 100644
--- a/java/driver/jdbc/pom.xml
+++ b/java/driver/jdbc/pom.xml
@@ -79,7 +79,7 @@
</dependency>
<dependency>
<groupId>org.apache.arrow.adbc</groupId>
- <artifactId>adbc-driver-testsuite</artifactId>
+ <artifactId>adbc-driver-validation</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
diff --git
a/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/FixedJdbcStatement.java
b/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/FixedJdbcStatement.java
index feae265..4f34d22 100644
---
a/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/FixedJdbcStatement.java
+++
b/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/FixedJdbcStatement.java
@@ -51,7 +51,7 @@ class FixedJdbcStatement implements AdbcStatement {
@Override
public void execute() throws AdbcException {
- throw new UnsupportedOperationException("Cannot execute() this
statement.");
+ throw AdbcException.invalidState("[JDBC] Cannot execute() this statement");
}
@Override
@@ -70,7 +70,7 @@ class FixedJdbcStatement implements AdbcStatement {
@Override
public void prepare() throws AdbcException {
- throw new UnsupportedOperationException("Cannot prepare() this
statement.");
+ throw AdbcException.invalidState("[JDBC] Cannot execute() this statement");
}
@Override
diff --git
a/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcBindReader.java
b/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcBindReader.java
new file mode 100644
index 0000000..da32f5d
--- /dev/null
+++
b/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcBindReader.java
@@ -0,0 +1,108 @@
+/*
+ * 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.adbc.driver.jdbc;
+
+import java.io.IOException;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import org.apache.arrow.adapter.jdbc.ArrowVectorIterator;
+import org.apache.arrow.adapter.jdbc.JdbcToArrow;
+import org.apache.arrow.adapter.jdbc.JdbcToArrowUtils;
+import org.apache.arrow.adbc.driver.jdbc.util.JdbcParameterBinder;
+import org.apache.arrow.memory.BufferAllocator;
+import org.apache.arrow.vector.VectorSchemaRoot;
+import org.apache.arrow.vector.VectorUnloader;
+import org.apache.arrow.vector.ipc.ArrowReader;
+import org.apache.arrow.vector.ipc.message.ArrowRecordBatch;
+import org.apache.arrow.vector.types.pojo.Schema;
+
+/** An Arrow reader that binds parameters. */
+final class JdbcBindReader extends ArrowReader {
+ private final PreparedStatement statement;
+ private final JdbcParameterBinder binder;
+ private ResultSet currentResultSet;
+ private ArrowVectorIterator currentSource;
+
+ JdbcBindReader(
+ BufferAllocator allocator, PreparedStatement statement, VectorSchemaRoot
bindParameters) {
+ super(allocator);
+ this.statement = statement;
+ this.binder = JdbcParameterBinder.builder(statement,
bindParameters).bindAll().build();
+ }
+
+ @Override
+ public boolean loadNextBatch() throws IOException {
+ if (currentSource == null || !currentSource.hasNext()) {
+ if (!advance()) {
+ return false;
+ }
+ }
+
+ final VectorSchemaRoot root = currentSource.next();
+ try (final ArrowRecordBatch batch = new
VectorUnloader(root).getRecordBatch()) {
+ loadRecordBatch(batch);
+ }
+ return true;
+ }
+
+ @Override
+ public long bytesRead() {
+ return 0;
+ }
+
+ @Override
+ protected void closeReadSource() throws IOException {
+ try {
+ // Do not close PreparedStatement so we can reuse it
+ currentResultSet.close();
+ } catch (SQLException e) {
+ throw new IOException(e);
+ }
+ }
+
+ @Override
+ protected Schema readSchema() throws IOException {
+ try {
+ if (!advance()) {
+ throw new IOException("Parameter set is empty!");
+ }
+ return JdbcToArrowUtils.jdbcToArrowSchema(
+ currentResultSet.getMetaData(), JdbcToArrowUtils.getUtcCalendar());
+ } catch (SQLException e) {
+ throw new IOException(e);
+ }
+ }
+
+ private boolean advance() throws IOException {
+ try {
+ if (binder.next()) {
+ if (currentResultSet != null) {
+ currentSource.close();
+ currentResultSet.close();
+ }
+ currentResultSet = statement.executeQuery();
+ currentSource = JdbcToArrow.sqlToArrowVectorIterator(currentResultSet,
allocator);
+ return true;
+ }
+ } catch (SQLException e) {
+ throw new IOException(e);
+ }
+ return false;
+ }
+}
diff --git
a/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcConnection.java
b/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcConnection.java
index 0bc8fa3..8824b12 100644
---
a/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcConnection.java
+++
b/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcConnection.java
@@ -39,10 +39,12 @@ import org.apache.arrow.vector.types.pojo.Schema;
public class JdbcConnection implements AdbcConnection {
private final BufferAllocator allocator;
private final Connection connection;
+ private final JdbcDriverQuirks quirks;
- JdbcConnection(BufferAllocator allocator, Connection connection) {
+ JdbcConnection(BufferAllocator allocator, Connection connection,
JdbcDriverQuirks quirks) {
this.allocator = allocator;
this.connection = connection;
+ this.quirks = quirks;
}
@Override
@@ -57,13 +59,13 @@ public class JdbcConnection implements AdbcConnection {
@Override
public AdbcStatement createStatement() throws AdbcException {
- return new JdbcStatement(allocator, connection);
+ return new JdbcStatement(allocator, connection, quirks);
}
@Override
public AdbcStatement bulkIngest(String targetTableName, BulkIngestMode mode)
throws AdbcException {
- return JdbcStatement.ingestRoot(allocator, connection, targetTableName,
mode);
+ return JdbcStatement.ingestRoot(allocator, connection, quirks,
targetTableName, mode);
}
@Override
@@ -171,9 +173,9 @@ public class JdbcConnection implements AdbcConnection {
connection.close();
}
- private void checkAutoCommit() throws SQLException {
+ private void checkAutoCommit() throws AdbcException, SQLException {
if (connection.getAutoCommit()) {
- throw new IllegalStateException("Cannot perform operation in autocommit
mode");
+ throw AdbcException.invalidState("[JDBC] Cannot perform operation in
autocommit mode");
}
}
diff --git
a/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcDatabase.java
b/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcDatabase.java
index eded48e..4cef0e4 100644
---
a/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcDatabase.java
+++
b/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcDatabase.java
@@ -30,12 +30,15 @@ import org.apache.arrow.memory.BufferAllocator;
public final class JdbcDatabase implements AdbcDatabase {
private final BufferAllocator allocator;
private final String target;
+ private final JdbcDriverQuirks quirks;
private final Connection connection;
private final AtomicInteger counter;
- JdbcDatabase(BufferAllocator allocator, final String target) throws
AdbcException {
+ JdbcDatabase(BufferAllocator allocator, final String target,
JdbcDriverQuirks quirks)
+ throws AdbcException {
this.allocator = allocator;
this.target = target;
+ this.quirks = quirks;
try {
this.connection = DriverManager.getConnection(target);
} catch (SQLException e) {
@@ -55,7 +58,8 @@ public final class JdbcDatabase implements AdbcDatabase {
final int count = counter.getAndIncrement();
return new JdbcConnection(
allocator.newChildAllocator("adbc-jdbc-connection-" + count, 0,
allocator.getLimit()),
- connection);
+ connection,
+ quirks);
}
@Override
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 b3621ac..341c849 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
@@ -23,6 +23,7 @@ import org.apache.arrow.adbc.core.AdbcException;
import org.apache.arrow.adbc.drivermanager.AdbcDriverManager;
import org.apache.arrow.memory.BufferAllocator;
import org.apache.arrow.memory.RootAllocator;
+import org.apache.arrow.util.Preconditions;
public enum JdbcDriver implements AdbcDriver {
INSTANCE;
@@ -35,7 +36,19 @@ public enum JdbcDriver implements AdbcDriver {
}
@Override
- public AdbcDatabase open(Map<String, String> parameters) throws
AdbcException {
- return new JdbcDatabase(allocator, "jdbc:derby:" + parameters.get("path"));
+ public AdbcDatabase open(Map<String, Object> parameters) throws
AdbcException {
+ Object target = parameters.get("adbc.jdbc.url");
+ if (!(target instanceof String)) {
+ throw AdbcException.invalidArgument("[JDBC] Must provide String
adbc.jdbc.url parameter");
+ }
+ Object quirks = parameters.get("adbc.jdbc.quirks");
+ if (quirks != null) {
+ Preconditions.checkArgument(
+ quirks instanceof JdbcDriverQuirks,
+ "[JDBC] adbc.jdbc.quirks must be a JdbcDriverQuirks instance");
+ } else {
+ quirks = new JdbcDriverQuirks();
+ }
+ return new JdbcDatabase(allocator, (String) target, (JdbcDriverQuirks)
quirks);
}
}
diff --git
a/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcDriverQuirks.java
b/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcDriverQuirks.java
new file mode 100644
index 0000000..99c87e6
--- /dev/null
+++
b/java/driver/jdbc/src/main/java/org/apache/arrow/adbc/driver/jdbc/JdbcDriverQuirks.java
@@ -0,0 +1,91 @@
+/*
+ * 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.adbc.driver.jdbc;
+
+import java.util.function.Function;
+import org.apache.arrow.vector.types.pojo.ArrowType;
+
+/** Parameters to pass to the ADBC JDBC driver to account for
driver/vendor-specific quirks. */
+public final class JdbcDriverQuirks {
+ public static final Function<ArrowType, String>
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<ArrowType, String> arrowToSqlTypeNameMapping;
+
+ public JdbcDriverQuirks() {
+ 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<ArrowType, String> getArrowToSqlTypeNameMapping() {
+ return arrowToSqlTypeNameMapping;
+ }
+
+ /** Create a new builder. */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static final class Builder {
+ Function<ArrowType, String> arrowToSqlTypeNameMapping;
+
+ public Builder arrowToSqlTypeNameMapping(Function<ArrowType, String>
mapper) {
+ this.arrowToSqlTypeNameMapping = mapper;
+ return this;
+ }
+
+ public JdbcDriverQuirks build() {
+ final JdbcDriverQuirks quirks = new JdbcDriverQuirks();
+ if (arrowToSqlTypeNameMapping != null) {
+ quirks.arrowToSqlTypeNameMapping = arrowToSqlTypeNameMapping;
+ }
+ return quirks;
+ }
+ }
+}
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 4926877..7ba30f0 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
@@ -17,10 +17,25 @@
package org.apache.arrow.adbc.driver.jdbc;
import java.sql.SQLException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
import org.apache.arrow.adbc.core.AdbcException;
import org.apache.arrow.adbc.core.AdbcStatusCode;
final class JdbcDriverUtil {
+ // Do our best to properly map database-specific errors to NOT_FOUND status.
+ private static final Set<String> SQLSTATE_TABLE_NOT_FOUND =
+ new HashSet<>(
+ Arrays.asList(
+ // Apache Derby
https://db.apache.org/derby/docs/10.4/ref/rrefexcept71493.html
+ "42X05",
+ // MySQL
+ //
https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-error-sqlstates.html
+ "42S02",
+ // Postgres
https://www.postgresql.org/docs/current/errcodes-appendix.html
+ "42P01"));
+
private JdbcDriverUtil() {
throw new AssertionError("Do not instantiate this class");
}
@@ -29,17 +44,26 @@ final class JdbcDriverUtil {
return "[JDBC] " + s;
}
+ static AdbcStatusCode guessStatusCode(String sqlState) {
+ if (sqlState == null) {
+ return AdbcStatusCode.UNKNOWN;
+ } else if (SQLSTATE_TABLE_NOT_FOUND.contains(sqlState)) {
+ return AdbcStatusCode.NOT_FOUND;
+ }
+ return AdbcStatusCode.UNKNOWN;
+ }
+
static AdbcException fromSqlException(SQLException e) {
return new AdbcException(
prefixExceptionMessage(e.getMessage()),
e.getCause(),
- AdbcStatusCode.UNKNOWN,
+ guessStatusCode(e.getSQLState()),
e.getSQLState(),
e.getErrorCode());
}
static AdbcException fromSqlException(String format, SQLException e,
Object... values) {
- return fromSqlException(AdbcStatusCode.UNKNOWN, format, e, values);
+ return fromSqlException(guessStatusCode(e.getSQLState()), format, e,
values);
}
static AdbcException fromSqlException(
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 99b46ac..df523d4 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
@@ -17,13 +17,12 @@
package org.apache.arrow.adbc.driver.jdbc;
+import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
-import java.util.Arrays;
-import java.util.List;
import java.util.Objects;
import org.apache.arrow.adbc.core.AdbcException;
import org.apache.arrow.adbc.core.AdbcStatement;
@@ -37,40 +36,34 @@ import org.apache.arrow.vector.ipc.ArrowReader;
import org.apache.arrow.vector.types.pojo.Field;
public class JdbcStatement implements AdbcStatement {
- // Do our best to properly map database-specific errors to NOT_FOUND status.
- private static final List<String> SQLSTATE_TABLE_NOT_FOUND =
- Arrays.asList(
- // Apache Derby
https://db.apache.org/derby/docs/10.4/ref/rrefexcept71493.html
- "42X05",
- // MySQL
- //
https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-error-sqlstates.html
- "42S02",
- // Postgres
https://www.postgresql.org/docs/current/errcodes-appendix.html
- "42P01");
private final BufferAllocator allocator;
private final Connection connection;
+ private final JdbcDriverQuirks quirks;
// State for SQL queries
private Statement statement;
private String sqlQuery;
+ private ArrowReader reader;
private ResultSet resultSet;
// State for bulk ingest
private BulkState bulkOperation;
private VectorSchemaRoot bindRoot;
- JdbcStatement(BufferAllocator allocator, Connection connection) {
+ JdbcStatement(BufferAllocator allocator, Connection connection,
JdbcDriverQuirks quirks) {
this.allocator = allocator;
this.connection = connection;
+ this.quirks = quirks;
this.sqlQuery = null;
}
static JdbcStatement ingestRoot(
BufferAllocator allocator,
Connection connection,
+ JdbcDriverQuirks quirks,
String targetTableName,
BulkIngestMode mode) {
Objects.requireNonNull(targetTableName);
- final JdbcStatement statement = new JdbcStatement(allocator, connection);
+ final JdbcStatement statement = new JdbcStatement(allocator, connection,
quirks);
statement.bulkOperation = new BulkState();
statement.bulkOperation.mode = mode;
statement.bulkOperation.targetTable = targetTableName;
@@ -78,9 +71,10 @@ public class JdbcStatement implements AdbcStatement {
}
@Override
- public void setSqlQuery(String query) {
+ public void setSqlQuery(String query) throws AdbcException {
if (bulkOperation != null) {
- throw new IllegalStateException("Statement is configured for a bulk
ingest/append operation");
+ throw AdbcException.invalidState(
+ "[JDBC] Statement is configured for a bulk ingest/append operation");
}
sqlQuery = query;
}
@@ -97,7 +91,7 @@ public class JdbcStatement implements AdbcStatement {
} else if (sqlQuery != null) {
executeSqlQuery();
} else {
- throw new IllegalStateException("Must setSqlQuery first");
+ throw AdbcException.invalidState("[JDBC] Must setSqlQuery() first");
}
}
@@ -111,38 +105,13 @@ public class JdbcStatement implements AdbcStatement {
}
final Field field = bindRoot.getVector(col).getField();
create.append(field.getName());
- switch (field.getType().getTypeID()) {
- case Null:
- case Struct:
- case List:
- case LargeList:
- case FixedSizeList:
- case Union:
- case Map:
- throw new UnsupportedOperationException("Type " + field);
- case Int:
- // TODO:
- create.append(" INT");
- break;
- case FloatingPoint:
- throw new UnsupportedOperationException("Type " + field);
- case Utf8:
- create.append(" CLOB");
- break;
- case LargeUtf8:
- case Binary:
- case LargeBinary:
- case FixedSizeBinary:
- case Bool:
- case Decimal:
- case Date:
- case Time:
- case Timestamp:
- case Interval:
- case Duration:
- case NONE:
- throw new UnsupportedOperationException("Type " + field);
+ create.append(' ');
+ String typeName =
quirks.getArrowToSqlTypeNameMapping().apply(field.getType());
+ if (typeName == null) {
+ throw AdbcException.notImplemented(
+ "[JDBC] Cannot generate CREATE TABLE statement for field " +
field);
}
+ create.append(typeName);
}
create.append(")");
@@ -150,13 +119,16 @@ public class JdbcStatement implements AdbcStatement {
statement.execute(create.toString());
} catch (SQLException e) {
throw JdbcDriverUtil.fromSqlException(
- AdbcStatusCode.ALREADY_EXISTS, "Could not create table %s", e,
bulkOperation.targetTable);
+ AdbcStatusCode.ALREADY_EXISTS,
+ "Could not create table %s: ",
+ e,
+ bulkOperation.targetTable);
}
}
private void executeBulk() throws AdbcException {
if (bindRoot == null) {
- throw new IllegalStateException("Must bind() before bulk insert");
+ throw AdbcException.invalidState("[JDBC] Must call bind() before bulk
insert");
}
if (bulkOperation.mode == BulkIngestMode.CREATE) {
@@ -180,17 +152,8 @@ public class JdbcStatement implements AdbcStatement {
try {
statement = connection.prepareStatement(insert.toString());
} catch (SQLException e) {
- // It's hard to differentiate between 'table not found' and parameter
type/count mismatch here
- // because SQLState is inconsistent (see SQLSTATE_TABLE_NOT_FOUND
above). We could query for
- // table existence but that's another roundtrip and leads to a TOC/TOU
- // error. Instead, we hard-code some common codes here.
-
- final AdbcStatusCode code =
- SQLSTATE_TABLE_NOT_FOUND.contains(e.getSQLState())
- ? AdbcStatusCode.NOT_FOUND
- : AdbcStatusCode.ALREADY_EXISTS;
throw JdbcDriverUtil.fromSqlException(
- code, "Could not bulk insert into table %s: ", e,
bulkOperation.targetTable);
+ "Could not bulk insert into table %s: ", e,
bulkOperation.targetTable);
}
try {
try {
@@ -211,12 +174,33 @@ public class JdbcStatement implements AdbcStatement {
private void executeSqlQuery() throws AdbcException {
try {
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (IOException e) {
+ throw new AdbcException(
+ "Failed to close unread result set", e, AdbcStatusCode.IO, null,
/*vendorCode*/ 0);
+ }
+ }
if (resultSet != null) {
resultSet.close();
}
- statement =
- connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
- resultSet = statement.executeQuery(sqlQuery);
+ if (statement instanceof PreparedStatement) {
+ PreparedStatement preparedStatement = (PreparedStatement) statement;
+ if (bindRoot != null) {
+ reader = new JdbcBindReader(allocator, preparedStatement, bindRoot);
+ } else {
+ resultSet = preparedStatement.executeQuery();
+ }
+ } else {
+ if (statement != null) {
+ statement.close();
+ }
+ statement =
+ connection.createStatement(
+ ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
+ resultSet = statement.executeQuery(sqlQuery);
+ }
} catch (SQLException e) {
throw JdbcDriverUtil.fromSqlException(e);
}
@@ -224,8 +208,13 @@ public class JdbcStatement implements AdbcStatement {
@Override
public ArrowReader getArrowReader() throws AdbcException {
+ if (reader != null) {
+ ArrowReader result = reader;
+ reader = null;
+ return result;
+ }
if (resultSet == null) {
- throw new IllegalStateException("Must call execute() before
getArrowIterator()");
+ throw AdbcException.invalidState("[JDBC] Must call execute() before
getArrowReader()");
}
final JdbcArrowReader reader =
new JdbcArrowReader(allocator, resultSet, /*overrideSchema*/ null);
@@ -234,13 +223,22 @@ public class JdbcStatement implements AdbcStatement {
}
@Override
- public void prepare() {
- throw new UnsupportedOperationException("prepare");
+ public void prepare() throws AdbcException {
+ try {
+ if (resultSet != null) {
+ resultSet.close();
+ }
+ statement =
+ connection.prepareStatement(
+ sqlQuery, ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
+ } catch (SQLException e) {
+ throw JdbcDriverUtil.fromSqlException(e);
+ }
}
@Override
public void close() throws Exception {
- AutoCloseables.close(resultSet, statement);
+ AutoCloseables.close(reader, resultSet, statement);
}
private static final class BulkState {
diff --git a/java/driver/testsuite/pom.xml b/java/driver/validation/pom.xml
similarity index 95%
rename from java/driver/testsuite/pom.xml
rename to java/driver/validation/pom.xml
index cf489bc..6128fa7 100644
--- a/java/driver/testsuite/pom.xml
+++ b/java/driver/validation/pom.xml
@@ -17,9 +17,9 @@
<version>9.0.0-SNAPSHOT</version>
</parent>
- <artifactId>adbc-driver-testsuite</artifactId>
+ <artifactId>adbc-driver-validation</artifactId>
<packaging>jar</packaging>
- <name>Arrow ADBC Driver Test Suite</name>
+ <name>Arrow ADBC Driver Validation Suite</name>
<description>A reusable ADBC driver compliance/test suite.</description>
<dependencies>
diff --git
a/java/driver/testsuite/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
similarity index 79%
rename from
java/driver/testsuite/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractConnectionMetadataTest.java
rename to
java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractConnectionMetadataTest.java
index 0ded7d9..6d9b3fc 100644
---
a/java/driver/testsuite/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
@@ -19,23 +19,22 @@ package org.apache.arrow.adbc.driver.testsuite;
import static org.assertj.core.api.Assertions.assertThat;
-import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
+import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.apache.arrow.adbc.core.AdbcConnection;
import org.apache.arrow.adbc.core.AdbcDatabase;
-import org.apache.arrow.adbc.core.AdbcException;
import org.apache.arrow.adbc.core.AdbcStatement;
import org.apache.arrow.adbc.core.BulkIngestMode;
import org.apache.arrow.adbc.core.StandardSchemas;
import org.apache.arrow.memory.BufferAllocator;
import org.apache.arrow.memory.RootAllocator;
import org.apache.arrow.util.AutoCloseables;
+import org.apache.arrow.util.Preconditions;
import org.apache.arrow.vector.FieldVector;
-import org.apache.arrow.vector.IntVector;
import org.apache.arrow.vector.VarCharVector;
import org.apache.arrow.vector.VectorSchemaRoot;
import org.apache.arrow.vector.complex.ListVector;
@@ -49,28 +48,36 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+/** Common tests of metadata methods of AdbcConnection. */
public abstract class AbstractConnectionMetadataTest {
+ /** Must be initialized by the subclass. */
+ protected static SqlValidationQuirks quirks;
+
protected AdbcDatabase database;
protected AdbcConnection connection;
protected BufferAllocator allocator;
-
- protected abstract AdbcDatabase init() throws AdbcException;
+ protected SqlTestUtil util;
+ protected String tableName;
@BeforeEach
public void beforeEach() throws Exception {
- database = init();
+ Preconditions.checkNotNull(quirks, "Must initialize quirks in subclass
with @BeforeAll");
+ database = quirks.initDatabase();
connection = database.connect();
allocator = new RootAllocator();
+ util = new SqlTestUtil(quirks);
+ tableName = quirks.caseFoldTableName("foo");
}
@AfterEach
public void afterEach() throws Exception {
+ quirks.cleanupTable(tableName);
AutoCloseables.close(connection, database, allocator);
}
@Test
void getObjectsColumns() throws Exception {
- loadTable();
+ final Schema schema = util.ingestTableIntsStrs(allocator, connection,
tableName);
boolean tableFound = false;
try (final AdbcStatement stmt =
connection.getObjects(AdbcConnection.GetObjectsDepth.ALL, null, null,
null, null, null)) {
@@ -91,13 +98,16 @@ public abstract class AbstractConnectionMetadataTest {
continue;
}
final Text tableName = tableNames.getObject(i);
- if (tableName != null && tableName.toString().equals("FOO")) {
+ if (tableName != null &&
tableName.toString().equalsIgnoreCase(this.tableName)) {
tableFound = true;
@SuppressWarnings("unchecked")
final List<Map<String, ?>> columns = (List<Map<String, ?>>)
tableColumns.getObject(i);
assertThat(columns)
.extracting("column_name")
- .containsExactlyInAnyOrder(new Text("INTS"), new Text("STRS"));
+ .containsExactlyInAnyOrderElementsOf(
+ schema.getFields().stream()
+ .map(field -> new Text(field.getName()))
+ .collect(Collectors.toList()));
assertThat(columns).extracting("ordinal_position").containsExactlyInAnyOrder(1,
2);
}
}
@@ -108,7 +118,7 @@ public abstract class AbstractConnectionMetadataTest {
@Test
void getObjectsCatalogs() throws Exception {
- loadTable();
+ util.ingestTableIntsStrs(allocator, connection, tableName);
try (final AdbcStatement stmt =
connection.getObjects(
AdbcConnection.GetObjectsDepth.CATALOGS, null, null, null, null,
null)) {
@@ -126,7 +136,7 @@ public abstract class AbstractConnectionMetadataTest {
@Test
void getObjectsDbSchemas() throws Exception {
- loadTable();
+ util.ingestTableIntsStrs(allocator, connection, tableName);
try (final AdbcStatement stmt =
connection.getObjects(
AdbcConnection.GetObjectsDepth.DB_SCHEMAS, null, null, null, null,
null)) {
@@ -141,7 +151,7 @@ public abstract class AbstractConnectionMetadataTest {
@Test
void getObjectsTables() throws Exception {
- loadTable();
+ util.ingestTableIntsStrs(allocator, connection, tableName);
try (final AdbcStatement stmt =
connection.getObjects(
AdbcConnection.GetObjectsDepth.TABLES, null, null, null, null,
null)) {
@@ -156,7 +166,7 @@ public abstract class AbstractConnectionMetadataTest {
final StructVector tables = (StructVector)
dbSchemaTables.getDataVector();
final VarCharVector tableNames = (VarCharVector)
tables.getVectorById(0);
assertThat(IntStream.range(0,
tableNames.getValueCount()).mapToObj(tableNames::getObject))
- .contains(new Text("FOO"));
+ .containsAnyOf(new Text(quirks.caseFoldTableName(tableName)));
}
}
}
@@ -166,15 +176,16 @@ public abstract class AbstractConnectionMetadataTest {
final Schema schema =
new Schema(
Arrays.asList(
- Field.nullable("INTS", new ArrowType.Int(32, /*signed=*/
true)),
- Field.nullable("STRS", new ArrowType.Utf8())));
+ Field.nullable(
+ quirks.caseFoldColumnName("INTS"), new ArrowType.Int(32,
/*signed=*/ true)),
+ Field.nullable(quirks.caseFoldColumnName("STRS"), new
ArrowType.Utf8())));
try (final VectorSchemaRoot root = VectorSchemaRoot.create(schema,
allocator)) {
- try (final AdbcStatement stmt = connection.bulkIngest("FOO",
BulkIngestMode.CREATE)) {
+ try (final AdbcStatement stmt = connection.bulkIngest(tableName,
BulkIngestMode.CREATE)) {
stmt.bind(root);
stmt.execute();
}
}
- assertThat(connection.getTableSchema(/*catalog*/ null, /*dbSchema*/ null,
"FOO"))
+ assertThat(connection.getTableSchema(/*catalog*/ null, /*dbSchema*/ null,
tableName))
.isEqualTo(schema);
}
@@ -196,35 +207,4 @@ public abstract class AbstractConnectionMetadataTest {
}
}
}
-
- void loadTable() throws Exception {
- final Schema schema =
- new Schema(
- Arrays.asList(
- Field.nullable("INTS", new ArrowType.Int(32, /*signed=*/
true)),
- Field.nullable("STRS", new ArrowType.Utf8())));
- try (final VectorSchemaRoot root = VectorSchemaRoot.create(schema,
allocator)) {
- final IntVector ints = (IntVector) root.getVector(0);
- final VarCharVector strs = (VarCharVector) root.getVector(1);
-
- ints.allocateNew(4);
- ints.setSafe(0, 0);
- ints.setSafe(1, 1);
- ints.setSafe(2, 2);
- ints.setNull(3);
- strs.allocateNew(4);
- strs.setNull(0);
- strs.setSafe(1, "foo".getBytes(StandardCharsets.UTF_8));
- strs.setSafe(2, "".getBytes(StandardCharsets.UTF_8));
- strs.setSafe(3, "asdf".getBytes(StandardCharsets.UTF_8));
- root.setRowCount(4);
-
- // TODO: XXX: need a "quirks" system to handle idiosyncracies. For
example: Derby forces table
- // names to uppercase, but does not do case folding in all places.
- try (final AdbcStatement stmt = connection.bulkIngest("FOO",
BulkIngestMode.CREATE)) {
- stmt.bind(root);
- stmt.execute();
- }
- }
- }
}
diff --git
a/java/driver/testsuite/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractConnectionTest.java
b/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractConnectionTest.java
similarity index 92%
rename from
java/driver/testsuite/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractConnectionTest.java
rename to
java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractConnectionTest.java
index fb22e8a..4b6944e 100644
---
a/java/driver/testsuite/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractConnectionTest.java
+++
b/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractConnectionTest.java
@@ -19,21 +19,21 @@ package org.apache.arrow.adbc.driver.testsuite;
import org.apache.arrow.adbc.core.AdbcConnection;
import org.apache.arrow.adbc.core.AdbcDatabase;
-import org.apache.arrow.adbc.core.AdbcException;
import org.apache.arrow.util.AutoCloseables;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public abstract class AbstractConnectionTest {
+ /** Must be initialized by the subclass. */
+ protected static SqlValidationQuirks quirks;
+
protected AdbcDatabase database;
protected AdbcConnection connection;
- protected abstract AdbcDatabase init() throws AdbcException;
-
@BeforeEach
public void beforeEach() throws Exception {
- database = init();
+ database = quirks.initDatabase();
connection = database.connect();
}
diff --git
a/java/driver/testsuite/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractStatementTest.java
b/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractStatementTest.java
similarity index 55%
rename from
java/driver/testsuite/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractStatementTest.java
rename to
java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractStatementTest.java
index a47a339..947cd3d 100644
---
a/java/driver/testsuite/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractStatementTest.java
+++
b/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractStatementTest.java
@@ -33,6 +33,7 @@ import org.apache.arrow.adbc.core.BulkIngestMode;
import org.apache.arrow.memory.BufferAllocator;
import org.apache.arrow.memory.RootAllocator;
import org.apache.arrow.util.AutoCloseables;
+import org.apache.arrow.util.Preconditions;
import org.apache.arrow.vector.IntVector;
import org.apache.arrow.vector.VarCharVector;
import org.apache.arrow.vector.VectorSchemaRoot;
@@ -40,36 +41,46 @@ import org.apache.arrow.vector.ipc.ArrowReader;
import org.apache.arrow.vector.types.pojo.ArrowType;
import org.apache.arrow.vector.types.pojo.Field;
import org.apache.arrow.vector.types.pojo.Schema;
+import org.apache.arrow.vector.util.Text;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public abstract class AbstractStatementTest {
+ /** Must be initialized by the subclass. */
+ protected static SqlValidationQuirks quirks;
+
protected AdbcDatabase database;
protected AdbcConnection connection;
protected BufferAllocator allocator;
-
- protected abstract AdbcDatabase init() throws AdbcException;
+ protected SqlTestUtil util;
+ protected String tableName;
+ protected Schema schema;
@BeforeEach
public void beforeEach() throws Exception {
- database = init();
+ Preconditions.checkNotNull(quirks, "Must initialize quirks in subclass
with @BeforeAll");
+ database = quirks.initDatabase();
connection = database.connect();
allocator = new RootAllocator();
+ util = new SqlTestUtil(quirks);
+ tableName = quirks.caseFoldTableName("bulktable");
+ schema =
+ new Schema(
+ Arrays.asList(
+ Field.nullable(
+ quirks.caseFoldColumnName("ints"), new ArrowType.Int(32,
/*signed=*/ true)),
+ Field.nullable(quirks.caseFoldColumnName("strs"), new
ArrowType.Utf8())));
}
@AfterEach
public void afterEach() throws Exception {
+ quirks.cleanupTable(tableName);
AutoCloseables.close(connection, database, allocator);
}
@Test
public void bulkInsertAppend() throws Exception {
- final Schema schema =
- new Schema(
- Arrays.asList(
- Field.nullable("INTS", new ArrowType.Int(32, /*signed=*/
true)),
- Field.nullable("STRS", new ArrowType.Utf8())));
try (final VectorSchemaRoot root = VectorSchemaRoot.create(schema,
allocator)) {
final IntVector ints = (IntVector) root.getVector(0);
final VarCharVector strs = (VarCharVector) root.getVector(1);
@@ -86,14 +97,12 @@ public abstract class AbstractStatementTest {
strs.setSafe(3, "asdf".getBytes(StandardCharsets.UTF_8));
root.setRowCount(4);
- // TODO: XXX: need a "quirks" system to handle idiosyncracies. For
example: Derby forces table
- // names to uppercase, but does not do case folding in all places.
- try (final AdbcStatement stmt = connection.bulkIngest("FOO",
BulkIngestMode.CREATE)) {
+ try (final AdbcStatement stmt = connection.bulkIngest(tableName,
BulkIngestMode.CREATE)) {
stmt.bind(root);
stmt.execute();
}
try (final AdbcStatement stmt = connection.createStatement()) {
- stmt.setSqlQuery("SELECT * FROM foo");
+ stmt.setSqlQuery("SELECT * FROM " + tableName);
try (ArrowReader arrowReader = stmt.executeQuery()) {
assertThat(arrowReader.loadNextBatch()).isTrue();
assertRoot(arrowReader.getVectorSchemaRoot()).isEqualTo(root);
@@ -101,12 +110,12 @@ public abstract class AbstractStatementTest {
}
// Append
- try (final AdbcStatement stmt = connection.bulkIngest("FOO",
BulkIngestMode.APPEND)) {
+ try (final AdbcStatement stmt = connection.bulkIngest(tableName,
BulkIngestMode.APPEND)) {
stmt.bind(root);
stmt.execute();
}
try (final AdbcStatement stmt = connection.createStatement()) {
- stmt.setSqlQuery("SELECT * FROM FOO");
+ stmt.setSqlQuery("SELECT * FROM " + tableName);
try (ArrowReader arrowReader = stmt.executeQuery()) {
assertThat(arrowReader.loadNextBatch()).isTrue();
root.setRowCount(8);
@@ -126,65 +135,108 @@ public abstract class AbstractStatementTest {
@Test
public void bulkIngestAppendConflict() throws Exception {
- final Schema schema =
- new Schema(
- Arrays.asList(
- Field.nullable("INTS", new ArrowType.Int(32, /*signed=*/
true)),
- Field.nullable("STRS", new ArrowType.Utf8())));
final Schema schema2 =
new Schema(
Collections.singletonList(
- Field.nullable("INTS", new ArrowType.Int(32, /*signed=*/
true))));
+ Field.nullable(quirks.caseFoldColumnName("ints"), new
ArrowType.Utf8())));
try (final VectorSchemaRoot root = VectorSchemaRoot.create(schema,
allocator)) {
- try (final AdbcStatement stmt = connection.bulkIngest("FOO",
BulkIngestMode.CREATE)) {
+ try (final AdbcStatement stmt = connection.bulkIngest(tableName,
BulkIngestMode.CREATE)) {
stmt.bind(root);
stmt.execute();
}
}
try (final VectorSchemaRoot root = VectorSchemaRoot.create(schema2,
allocator)) {
- try (final AdbcStatement stmt = connection.bulkIngest("FOO",
BulkIngestMode.APPEND)) {
+ root.setRowCount(1);
+ try (final AdbcStatement stmt = connection.bulkIngest(tableName,
BulkIngestMode.APPEND)) {
stmt.bind(root);
- final AdbcException e = assertThrows(AdbcException.class,
stmt::execute);
- assertThat(e.getStatus()).isEqualTo(AdbcStatusCode.ALREADY_EXISTS);
+ assertThrows(AdbcException.class, stmt::execute);
}
}
}
@Test
public void bulkIngestAppendNotFound() throws Exception {
- final Schema schema =
- new Schema(
- Arrays.asList(
- Field.nullable("INTS", new ArrowType.Int(32, /*signed=*/
true)),
- Field.nullable("STRS", new ArrowType.Utf8())));
try (final VectorSchemaRoot root = VectorSchemaRoot.create(schema,
allocator)) {
- try (final AdbcStatement stmt = connection.bulkIngest("FOO",
BulkIngestMode.APPEND)) {
+ root.setRowCount(1);
+ try (final AdbcStatement stmt = connection.bulkIngest(tableName,
BulkIngestMode.APPEND)) {
stmt.bind(root);
final AdbcException e = assertThrows(AdbcException.class,
stmt::execute);
- assertThat(e.getStatus()).isEqualTo(AdbcStatusCode.NOT_FOUND);
+ assertThat(e.getStatus()).describedAs("%s",
e).isEqualTo(AdbcStatusCode.NOT_FOUND);
}
}
}
@Test
public void bulkIngestCreateConflict() throws Exception {
- final Schema schema =
- new Schema(
- Arrays.asList(
- Field.nullable("INTS", new ArrowType.Int(32, /*signed=*/
true)),
- Field.nullable("STRS", new ArrowType.Utf8())));
try (final VectorSchemaRoot root = VectorSchemaRoot.create(schema,
allocator)) {
- try (final AdbcStatement stmt = connection.bulkIngest("FOO",
BulkIngestMode.CREATE)) {
+ try (final AdbcStatement stmt = connection.bulkIngest(tableName,
BulkIngestMode.CREATE)) {
stmt.bind(root);
stmt.execute();
}
}
try (final VectorSchemaRoot root = VectorSchemaRoot.create(schema,
allocator)) {
- try (final AdbcStatement stmt = connection.bulkIngest("FOO",
BulkIngestMode.CREATE)) {
+ try (final AdbcStatement stmt = connection.bulkIngest(tableName,
BulkIngestMode.CREATE)) {
stmt.bind(root);
final AdbcException e = assertThrows(AdbcException.class,
stmt::execute);
assertThat(e.getStatus()).isEqualTo(AdbcStatusCode.ALREADY_EXISTS);
}
}
}
+
+ @Test
+ public void prepareQuery() throws Exception {
+ final Schema expectedSchema = util.ingestTableIntsStrs(allocator,
connection, tableName);
+ try (final AdbcStatement stmt = connection.createStatement()) {
+ stmt.setSqlQuery("SELECT * FROM " + tableName);
+ stmt.prepare();
+ try (final ArrowReader reader = stmt.executeQuery()) {
+
assertThat(reader.getVectorSchemaRoot().getSchema()).isEqualTo(expectedSchema);
+ assertThat(reader.loadNextBatch()).isTrue();
+ assertThat(reader.getVectorSchemaRoot().getRowCount()).isEqualTo(4);
+ assertThat(reader.loadNextBatch()).isFalse();
+ }
+ }
+ }
+
+ @Test
+ public void prepareQueryWithParameters() throws Exception {
+ final Schema expectedSchema = util.ingestTableIntsStrs(allocator,
connection, tableName);
+ final Schema paramsSchema =
+ new
Schema(Collections.singletonList(expectedSchema.getFields().get(0)));
+ try (final AdbcStatement stmt = connection.createStatement();
+ final VectorSchemaRoot params = VectorSchemaRoot.create(paramsSchema,
allocator)) {
+ stmt.setSqlQuery(String.format("SELECT * FROM %s WHERE INTS = ?",
tableName));
+ stmt.prepare();
+ stmt.bind(params);
+ IntVector param0 = (IntVector) params.getVector(0);
+ param0.setSafe(0, 1);
+ param0.setSafe(1, 2);
+ params.setRowCount(2);
+ try (final ArrowReader reader = stmt.executeQuery()) {
+ VectorSchemaRoot root = reader.getVectorSchemaRoot();
+ assertThat(root.getSchema()).isEqualTo(expectedSchema);
+ assertThat(reader.loadNextBatch()).isTrue();
+ assertThat(root.getRowCount()).isEqualTo(1);
+ assertThat(root.getVector(1).getObject(0)).isEqualTo(new Text("foo"));
+
+ assertThat(reader.loadNextBatch()).isTrue();
+ assertThat(root.getRowCount()).isEqualTo(1);
+ assertThat(root.getVector(1).getObject(0)).isEqualTo(new Text(""));
+
+ assertThat(reader.loadNextBatch()).isFalse();
+ }
+
+ param0.setSafe(0, 0);
+ params.setRowCount(1);
+ try (final ArrowReader reader = stmt.executeQuery()) {
+ VectorSchemaRoot root = reader.getVectorSchemaRoot();
+ assertThat(root.getSchema()).isEqualTo(expectedSchema);
+ assertThat(reader.loadNextBatch()).isTrue();
+ assertThat(root.getRowCount()).isEqualTo(1);
+ assertThat(root.getVector(1).getObject(0)).isNull();
+
+ assertThat(reader.loadNextBatch()).isFalse();
+ }
+ }
+ }
}
diff --git
a/java/driver/testsuite/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
similarity index 79%
rename from
java/driver/testsuite/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractTransactionTest.java
rename to
java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/AbstractTransactionTest.java
index cc2908e..208d656 100644
---
a/java/driver/testsuite/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
@@ -17,6 +17,7 @@
package org.apache.arrow.adbc.driver.testsuite;
+import static
org.apache.arrow.adbc.driver.testsuite.ArrowAssertions.assertAdbcException;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -25,10 +26,12 @@ import org.apache.arrow.adbc.core.AdbcConnection;
import org.apache.arrow.adbc.core.AdbcDatabase;
import org.apache.arrow.adbc.core.AdbcException;
import org.apache.arrow.adbc.core.AdbcStatement;
+import org.apache.arrow.adbc.core.AdbcStatusCode;
import org.apache.arrow.adbc.core.BulkIngestMode;
import org.apache.arrow.memory.BufferAllocator;
import org.apache.arrow.memory.RootAllocator;
import org.apache.arrow.util.AutoCloseables;
+import org.apache.arrow.util.Preconditions;
import org.apache.arrow.vector.IntVector;
import org.apache.arrow.vector.VectorSchemaRoot;
import org.apache.arrow.vector.types.pojo.ArrowType;
@@ -39,15 +42,17 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public abstract class AbstractTransactionTest {
+ /** Must be initialized by the subclass. */
+ protected static SqlValidationQuirks quirks;
+
protected BufferAllocator allocator;
protected AdbcDatabase database;
protected AdbcConnection connection;
- protected abstract AdbcDatabase init() throws AdbcException;
-
@BeforeEach
public void beforeEach() throws Exception {
- database = init();
+ Preconditions.checkNotNull(quirks, "Must initialize quirks in subclass
with @BeforeAll");
+ database = quirks.initDatabase();
connection = database.connect();
allocator = new RootAllocator();
}
@@ -59,8 +64,10 @@ public abstract class AbstractTransactionTest {
@Test
void autoCommitByDefault() throws Exception {
- assertThrows(IllegalStateException.class, () -> connection.commit());
- assertThrows(IllegalStateException.class, () -> connection.rollback());
+ assertAdbcException(assertThrows(AdbcException.class, () ->
connection.commit()))
+ .isStatus(AdbcStatusCode.INVALID_STATE);
+ assertAdbcException(assertThrows(AdbcException.class, () ->
connection.rollback()))
+ .isStatus(AdbcStatusCode.INVALID_STATE);
assertThat(connection.getAutoCommit()).isTrue();
}
@@ -102,6 +109,8 @@ public abstract class AbstractTransactionTest {
assertThrows(AdbcException.class, stmt::execute);
}
}
+
+ quirks.cleanupTable("foo");
}
@Test
@@ -110,6 +119,7 @@ public abstract class AbstractTransactionTest {
new Schema(
Collections.singletonList(
Field.nullable("ints", new ArrowType.Int(32, /*signed=*/
true))));
+ final String tableName = quirks.caseFoldTableName("temptable");
connection.setAutoCommit(false);
try (VectorSchemaRoot root = VectorSchemaRoot.create(schema, allocator)) {
@@ -117,21 +127,23 @@ public abstract class AbstractTransactionTest {
ints.setSafe(0, 1);
ints.setSafe(1, 2);
root.setRowCount(2);
- try (final AdbcStatement stmt = connection.bulkIngest("foo",
BulkIngestMode.CREATE)) {
+ try (final AdbcStatement stmt = connection.bulkIngest(tableName,
BulkIngestMode.CREATE)) {
stmt.bind(root);
stmt.execute();
}
try (final AdbcStatement stmt = connection.createStatement()) {
- stmt.setSqlQuery("SELECT * FROM foo");
+ stmt.setSqlQuery("SELECT * FROM " + tableName);
stmt.execute();
}
connection.commit();
try (final AdbcStatement stmt = connection.createStatement()) {
- stmt.setSqlQuery("SELECT * FROM foo");
+ stmt.setSqlQuery("SELECT * FROM " + tableName);
stmt.execute();
}
connection.commit();
}
+
+ quirks.cleanupTable(tableName);
}
@Test
@@ -140,6 +152,7 @@ public abstract class AbstractTransactionTest {
new Schema(
Collections.singletonList(
Field.nullable("ints", new ArrowType.Int(32, /*signed=*/
true))));
+ final String tableName = quirks.caseFoldTableName("temptable");
connection.setAutoCommit(false);
try (VectorSchemaRoot root = VectorSchemaRoot.create(schema, allocator)) {
@@ -147,19 +160,21 @@ public abstract class AbstractTransactionTest {
ints.setSafe(0, 1);
ints.setSafe(1, 2);
root.setRowCount(2);
- try (final AdbcStatement stmt = connection.bulkIngest("foo",
BulkIngestMode.CREATE)) {
+ try (final AdbcStatement stmt = connection.bulkIngest(tableName,
BulkIngestMode.CREATE)) {
stmt.bind(root);
stmt.execute();
}
try (final AdbcStatement stmt = connection.createStatement()) {
- stmt.setSqlQuery("SELECT * FROM foo");
+ stmt.setSqlQuery("SELECT * FROM " + tableName);
stmt.execute();
}
connection.setAutoCommit(true);
try (final AdbcStatement stmt = connection.createStatement()) {
- stmt.setSqlQuery("SELECT * FROM foo");
+ stmt.setSqlQuery("SELECT * FROM " + tableName);
stmt.execute();
}
}
+
+ quirks.cleanupTable(tableName);
}
}
diff --git
a/java/driver/testsuite/src/main/java/org/apache/arrow/adbc/driver/testsuite/ArrowAssertions.java
b/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/ArrowAssertions.java
similarity index 76%
rename from
java/driver/testsuite/src/main/java/org/apache/arrow/adbc/driver/testsuite/ArrowAssertions.java
rename to
java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/ArrowAssertions.java
index 2b3f280..e357824 100644
---
a/java/driver/testsuite/src/main/java/org/apache/arrow/adbc/driver/testsuite/ArrowAssertions.java
+++
b/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/ArrowAssertions.java
@@ -17,6 +17,8 @@
package org.apache.arrow.adbc.driver.testsuite;
+import org.apache.arrow.adbc.core.AdbcException;
+import org.apache.arrow.adbc.core.AdbcStatusCode;
import org.apache.arrow.vector.FieldVector;
import org.apache.arrow.vector.VectorSchemaRoot;
import org.apache.arrow.vector.compare.VectorEqualsVisitor;
@@ -24,11 +26,36 @@ import org.assertj.core.api.AbstractAssert;
/** AssertJ assertions for Arrow. */
public final class ArrowAssertions {
+ /** Assert on a {@link AdbcException}. */
+ public static AdbcExceptionAssert assertAdbcException(AdbcException actual) {
+ return new AdbcExceptionAssert(actual);
+ }
+
/** Assert on a {@link VectorSchemaRoot}. */
public static VectorSchemaRootAssert assertRoot(VectorSchemaRoot actual) {
return new VectorSchemaRootAssert(actual);
}
+ public static final class AdbcExceptionAssert
+ extends AbstractAssert<AdbcExceptionAssert, AdbcException> {
+ AdbcExceptionAssert(AdbcException e) {
+ super(e, AdbcExceptionAssert.class);
+ }
+
+ public AdbcExceptionAssert isStatus(AdbcStatusCode status) {
+ if (actual.getStatus() != status) {
+ throw failureWithActualExpected(
+ actual.getStatus(),
+ status,
+ "Expected status %s but got %s:\n%s",
+ status,
+ actual.getStatus(),
+ actual);
+ }
+ return this;
+ }
+ }
+
public static final class VectorSchemaRootAssert
extends AbstractAssert<VectorSchemaRootAssert, VectorSchemaRoot> {
VectorSchemaRootAssert(VectorSchemaRoot vectorSchemaRoot) {
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
new file mode 100644
index 0000000..9248720
--- /dev/null
+++
b/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/SqlTestUtil.java
@@ -0,0 +1,72 @@
+/*
+ * 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.adbc.driver.testsuite;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import org.apache.arrow.adbc.core.AdbcConnection;
+import org.apache.arrow.adbc.core.AdbcStatement;
+import org.apache.arrow.adbc.core.BulkIngestMode;
+import org.apache.arrow.memory.BufferAllocator;
+import org.apache.arrow.vector.IntVector;
+import org.apache.arrow.vector.VarCharVector;
+import org.apache.arrow.vector.VectorSchemaRoot;
+import org.apache.arrow.vector.types.pojo.ArrowType;
+import org.apache.arrow.vector.types.pojo.Field;
+import org.apache.arrow.vector.types.pojo.Schema;
+
+public final class SqlTestUtil {
+ private final SqlValidationQuirks quirks;
+
+ public SqlTestUtil(SqlValidationQuirks quirks) {
+ this.quirks = quirks;
+ }
+
+ /** Load a simple table with two columns. */
+ public Schema ingestTableIntsStrs(
+ BufferAllocator allocator, AdbcConnection connection, String tableName)
throws Exception {
+ tableName = quirks.caseFoldTableName(tableName);
+ final Schema schema =
+ new Schema(
+ Arrays.asList(
+ Field.nullable(
+ quirks.caseFoldColumnName("INTS"), new ArrowType.Int(32,
/*signed=*/ true)),
+ Field.nullable(quirks.caseFoldColumnName("STRS"), new
ArrowType.Utf8())));
+ try (final VectorSchemaRoot root = VectorSchemaRoot.create(schema,
allocator)) {
+ final IntVector ints = (IntVector) root.getVector(0);
+ final VarCharVector strs = (VarCharVector) root.getVector(1);
+
+ ints.allocateNew(4);
+ ints.setSafe(0, 0);
+ ints.setSafe(1, 1);
+ ints.setSafe(2, 2);
+ ints.setNull(3);
+ strs.allocateNew(4);
+ strs.setNull(0);
+ strs.setSafe(1, "foo".getBytes(StandardCharsets.UTF_8));
+ strs.setSafe(2, "".getBytes(StandardCharsets.UTF_8));
+ strs.setSafe(3, "asdf".getBytes(StandardCharsets.UTF_8));
+ root.setRowCount(4);
+ try (final AdbcStatement stmt = connection.bulkIngest(tableName,
BulkIngestMode.CREATE)) {
+ stmt.bind(root);
+ stmt.execute();
+ }
+ }
+ return schema;
+ }
+}
diff --git
a/java/driver/jdbc/src/test/java/org/apache/arrow/adbc/driver/jdbc/JdbcStatementTest.java
b/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/SqlValidationQuirks.java
similarity index 62%
rename from
java/driver/jdbc/src/test/java/org/apache/arrow/adbc/driver/jdbc/JdbcStatementTest.java
rename to
java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/SqlValidationQuirks.java
index 72a28f3..9ad6883 100644
---
a/java/driver/jdbc/src/test/java/org/apache/arrow/adbc/driver/jdbc/JdbcStatementTest.java
+++
b/java/driver/validation/src/main/java/org/apache/arrow/adbc/driver/testsuite/SqlValidationQuirks.java
@@ -15,23 +15,24 @@
* limitations under the License.
*/
-package org.apache.arrow.adbc.driver.jdbc;
+package org.apache.arrow.adbc.driver.testsuite;
-import java.nio.file.Path;
-import java.util.HashMap;
-import java.util.Map;
import org.apache.arrow.adbc.core.AdbcDatabase;
import org.apache.arrow.adbc.core.AdbcException;
-import org.apache.arrow.adbc.driver.testsuite.AbstractStatementTest;
-import org.junit.jupiter.api.io.TempDir;
-class JdbcStatementTest extends AbstractStatementTest {
- @TempDir Path tempDir;
+/** Account for driver/vendor-specific quirks in implementing validation
tests. */
+public abstract class SqlValidationQuirks {
+ public abstract AdbcDatabase initDatabase() throws AdbcException;
- @Override
- protected AdbcDatabase init() throws AdbcException {
- final Map<String, String> parameters = new HashMap<>();
- parameters.put("path", tempDir.toString() + "/db;create=true");
- return JdbcDriver.INSTANCE.open(parameters);
+ public void cleanupTable(String name) throws Exception {}
+
+ /** Normalize a table name. */
+ public String caseFoldTableName(String name) {
+ return name;
+ }
+
+ /** Normalize a column name. */
+ public String caseFoldColumnName(String name) {
+ return name;
}
}
diff --git a/java/pom.xml b/java/pom.xml
index b74158a..11987e1 100644
--- a/java/pom.xml
+++ b/java/pom.xml
@@ -79,7 +79,9 @@
<module>core</module>
<module>driver/jdbc</module>
<module>driver/jdbc-util</module>
- <module>driver/testsuite</module>
+ <module>driver/jdbc-validation-derby</module>
+ <module>driver/jdbc-validation-postgresql</module>
+ <module>driver/validation</module>
<module>driver-manager</module>
</modules>
@@ -119,7 +121,7 @@
</dependency>
<dependency>
<groupId>org.apache.arrow.adbc</groupId>
- <artifactId>adbc-driver-testsuite</artifactId>
+ <artifactId>adbc-driver-validation</artifactId>
<version>${adbc.version}</version>
</dependency>
<dependency>